Lesson 20 of 50 intermediate

Problem Solving with Functions

Learn to break big problems into small pieces using functions — the key to solving harder CP problems!

Open interactive version (quiz + challenge)

Real-world analogy

Solving a big CP problem is like building a LEGO spaceship. You don't build the whole thing at once. First you build the wings, then the cockpit, then the engines — each piece separately. Then you connect them all together. Functions are your LEGO pieces — small, reusable parts that you combine to solve the big problem!

What is it?

Problem solving with functions means breaking complex problems into smaller, manageable pieces. Each piece becomes a function that does one specific task. By combining these small functions, you can solve problems that would be overwhelming to tackle all at once.

Real-world relevance

Real software is built exactly this way! A banking app has functions like calculateInterest(), validatePassword(), transferFunds() — each does one job. Game developers have functions like checkCollision(), updateScore(), renderFrame(). Breaking problems into functions is how all professional software is built.

Key points

Code example

#include <bits/stdc++.h>
using namespace std;

// Check if a number is prime
bool isPrime(int n) {
    if (n < 2) return false;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) return false;
    }
    return true;
}

// GCD using Euclidean algorithm
int gcd(int a, int b) {
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

// LCM using GCD
int lcm(int a, int b) {
    return a / gcd(a, b) * b;  // Divide first to avoid overflow!
}

// Swap using pass by reference
void mySwap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}

// Count primes in an array — combining functions!
int countPrimes(int arr[], int n) {
    int count = 0;
    for (int i = 0; i < n; i++) {
        if (isPrime(arr[i])) {
            count++;
        }
    }
    return count;
}

int main() {
    // Using isPrime
    for (int i = 1; i <= 20; i++) {
        if (isPrime(i)) {
            cout << i << " ";
        }
    }
    cout << endl;  // Output: 2 3 5 7 11 13 17 19
    
    // Using gcd and lcm
    cout << "GCD(12, 8) = " << gcd(12, 8) << endl;   // 4
    cout << "LCM(12, 8) = " << lcm(12, 8) << endl;   // 24
    
    // Using pass by reference
    int x = 10, y = 20;
    mySwap(x, y);
    cout << "After swap: x=" << x << " y=" << y << endl;  // x=20 y=10
    
    // Using countPrimes
    int arr[] = {4, 7, 10, 13, 15, 17, 20, 23};
    cout << "Primes in array: " << countPrimes(arr, 8) << endl;  // 4
    
    return 0;
}

Line-by-line walkthrough

  1. 1. isPrime checks if n < 2 first (0 and 1 are not prime). Then it checks divisors from 2 to sqrt(n) using i*i <= n.
  2. 2. If any divisor divides n evenly (n % i == 0), we immediately return false — it is not prime.
  3. 3. If no divisor works, we return true — the number is prime!
  4. 4. gcd uses the Euclidean algorithm: keep replacing (a, b) with (b, a%b) until b becomes 0. Then a is the GCD.
  5. 5. lcm divides first (a / gcd) then multiplies by b to avoid overflow.
  6. 6. mySwap uses & (pass by reference) so changing a and b inside the function changes x and y in main.
  7. 7. countPrimes combines the array loop pattern with our isPrime function — this is the power of functions!
  8. 8. In main, we use all our functions together to solve different problems with clean, readable code.

Spot the bug

#include <bits/stdc++.h>
using namespace std;

bool isPrime(int n) {
    for (int i = 2; i < n; i++) {
        if (n % i == 0) return false;
    }
    return true;
}

int main() {
    cout << isPrime(1) << endl;  // Should print 0 (false)
    cout << isPrime(2) << endl;  // Should print 1 (true)
    cout << isPrime(7) << endl;  // Should print 1 (true)
    cout << isPrime(1000000007) << endl;  // Should print 1, but takes forever!
    return 0;
}
Need a hint?
Two bugs! First: what does isPrime(1) return? Does the loop even run? Second: the loop goes up to n — for a billion, that is a billion iterations! How can you make it faster?
Show answer
Bug 1: isPrime(1) returns true because the loop 'for i = 2; i < 1' never runs, so it skips to 'return true.' But 1 is NOT prime! Fix: add 'if (n < 2) return false;' at the start. Bug 2: The loop checks all numbers up to n, which is painfully slow for large numbers. Fix: change 'i < n' to 'i * i <= n' — you only need to check up to the square root of n. This makes isPrime(1000000007) finish instantly instead of taking minutes!

Explain like I'm 5

Imagine you want to clean your whole room. That is a BIG job! But if you break it down — first pick up toys, then make the bed, then vacuum — each little job is easy. Functions are exactly that! Instead of writing one giant messy program, you make little helper functions like 'isPrime' and 'findMax,' and then you put them together to solve the big problem. Easy peasy!

Fun fact

The Euclidean algorithm for GCD is one of the oldest algorithms in the world — it was described by the Greek mathematician Euclid around 300 BC! That means this algorithm is over 2,300 years old and people still use it every single day in computer science. It is one of the most efficient algorithms ever created — hard to improve even with modern computers!

Hands-on challenge

Write these functions and test them in main(): (1) bool isPalindrome(string s) — checks if a string is a palindrome. (2) int power(int base, int exp) — calculates base^exp using a loop. (3) int sumOfDigits(int n) — returns the sum of digits (e.g., 123 -> 6). Then solve: read a number and print whether it is prime, and print the sum of its digits!

More resources

Open interactive version (quiz + challenge) ← Back to course: CP Zero to Hero