Part 1 — Week 1 Homework Review
Before we move on, let's walk through clean solutions to the three problems from last week. Pay attention to how similar the structure is across the three problems — that's a clue for what we'll learn today.
Problem 1 — GCD
Two ways to solve this. The first works but is slow when numbers are large; the second is the classic Euclidean algorithm.
Approach 1 — Brute force (loop from 1 to min(a, b))
#include <stdio.h>int main() { int a, b; scanf("%d %d", &a, &b); int result = 1; int min = (a < b) ? a : b; for (int i = 1; i <= min; i++) { if (a % i == 0 && b % i == 0) { result = i; } } printf("%d\n", result); return 0;}Approach 2 — Euclidean algorithm (much faster)
Idea: GCD(a, b) = GCD(b, a mod b). Keep replacing the pair until the second number becomes 0; the first one is the answer.
#include <stdio.h>int main() { int a, b; scanf("%d %d", &a, &b); while (b != 0) { int r = a % b; a = b; b = r; } printf("%d\n", a); return 0;}Python version (Euclidean)
a, b = map(int, input().split())while b != 0: a, b = b, a % bprint(a)while loop 5 times. That's exactly the problem functions solve.Problem 2 — Prime Number Check
The key optimization: we only need to check divisors up to √n. Use i * i <= n in the loop condition. Also notice the use of break to exit the loop early once we've found a divisor.
#include <stdio.h>int main() { int n; scanf("%d", &n); int is_prime = 1; for (int i = 2; i * i <= n; i++) { if (n % i == 0) { is_prime = 0; break; } } printf("%s\n", is_prime ? "YES" : "NO"); return 0;}n = int(input())is_prime = Truei = 2while i * i <= n: if n % i == 0: is_prime = False break i += 1print("YES" if is_prime else "NO")break jumps out of the nearest enclosing loop. Use it whenever there's no point continuing — like "I found a divisor, so n is definitely not prime."Problem 3 — Perfect Number
Loop through all numbers from 1 to n−1, sum the divisors, then compare with n.
#include <stdio.h>int main() { int n; scanf("%d", &n); int sum = 0; for (int i = 1; i < n; i++) { if (n % i == 0) { sum += i; } } printf("%s\n", sum == n ? "YES" : "NO"); return 0;}n = int(input())total = 0for i in range(1, n): if n % i == 0: total += iprint("YES" if total == n else "NO")Part 2 — Functions
1. Why do we need functions?
Suppose you want to compute the GCD of three pairs of numbers and print all three results. Without functions:
#include <stdio.h>int main() { // GCD of (12, 18) int a1 = 12, b1 = 18; while (b1 != 0) { int r = a1 % b1; a1 = b1; b1 = r; } printf("%d\n", a1); // GCD of (100, 75) — same logic, copied! int a2 = 100, b2 = 75; while (b2 != 0) { int r = a2 % b2; a2 = b2; b2 = r; } printf("%d\n", a2); // GCD of (7, 13) — copied again! int a3 = 7, b3 = 13; while (b3 != 0) { int r = a3 % b3; a3 = b3; b3 = r; } printf("%d\n", a3); return 0;}With a function, the same program becomes:
#include <stdio.h>int gcd(int a, int b) { while (b != 0) { int r = a % b; a = b; b = r; } return a;}int main() { printf("%d\n", gcd(12, 18)); printf("%d\n", gcd(100, 75)); printf("%d\n", gcd(7, 13)); return 0;}2. The anatomy of a C function
Every C function has the same shape:
return_type function_name (parameter_list) { // function body return value;}A concrete example:
#include <stdio.h>int square(int x) { // return type=int, name=square, parameter=int x return x * x; // give the value back to the caller}int main() { int result = square(5); // result = 25 printf("%d\n", result); printf("%d\n", square(7)); // you can use the call directly return 0;}In Python, the equivalent is:
def square(x): return x * xresult = square(5)print(result)print(square(7))Notice: Python uses def instead of a return type, and there's no type for the parameter. Python is dynamically typed — the same function can take any kind of value.
3. Multiple parameters
#include <stdio.h>int max_of_two(int a, int b) { if (a > b) { return a; } else { return b; }}int main() { printf("%d\n", max_of_two(7, 12)); // 12 printf("%d\n", max_of_two(99, 3)); // 99 return 0;}def max_of_two(a, b): if a > b: return a else: return bprint(max_of_two(7, 12))print(max_of_two(99, 3))We named it max_of_two because Python already has a built-in max().
4. void functions — when nothing is returned
Sometimes a function just performs an action (like printing) and doesn't need to return anything. Use the return type void to say "this function returns nothing."
#include <stdio.h>void print_separator() { printf("---------------\n");}void greet(int age) { printf("Hello! You are %d years old.\n", age);}int main() { greet(20); print_separator(); greet(30); return 0;}In Python there's no void — just leave out the return:
def print_separator(): print("---------------")def greet(age): print(f"Hello! You are {age} years old.")greet(20)print_separator()greet(30)5. Function order in C (declarations)
C requires the compiler to know about a function before you call it. There are two ways to satisfy this:
Option A — Define the function before main
#include <stdio.h>int square(int x) { // define first return x * x;}int main() { // use after printf("%d\n", square(5)); return 0;}Option B — Forward declaration (function prototype)
#include <stdio.h>int square(int x); // declaration: "this function exists, here's its signature"int main() { printf("%d\n", square(5)); // OK — compiler knows about square return 0;}int square(int x) { // definition can come later return x * x;}Python doesn't have this requirement — as long as the function is defined before it's called at runtime, you're fine.
6. Local vs global variables (scope)
Variables declared inside a function are local — they only exist while the function is running, and they're invisible to other functions.
#include <stdio.h>int global_x = 100; // global: visible everywherevoid show() { int local_y = 42; // local: only exists inside show() printf("global_x=%d, local_y=%d\n", global_x, local_y);}int main() { show(); printf("global_x=%d\n", global_x); // printf("%d", local_y); // ERROR: local_y doesn't exist here return 0;}Best practice: avoid global variables when you can. Pass values into functions as parameters, return results. This keeps each function self-contained and easy to reason about.
Part 3 — Assignments
Requirements
- 3 problems in total. Estimated time: 1 – 2 hours.
- Each problem requires a C version (
.c) and a Python version (.py). - You must define and use the function specified in each problem. Don't put everything in main!
- Use the exact function name and signature shown for each problem.
- Compile and run the same way as last week:
gcc file.c -o file && ./filefor C,python3 file.pyfor Python.
The factorial of a non-negative integer n, written n!, is the product 1 × 2 × 3 × ... × n. By convention, 0! = 1.
Examples: 5! = 5 × 4 × 3 × 2 × 1 = 120; 3! = 3 × 2 × 1 = 6; 1! = 1.
Write a function int factorial(int n) that takes a non-negative integer and returns n!. Then in main, read n from stdin, call the function, and print the result.
Input format
One line containing a single non-negative integer n.Constraint: 0 ≤ n ≤ 12Output format
One line containing the value of n!.Sample 1
Input
5Output
120Sample 2
Input
0Output
1Sample 3
Input
10Output
3628800Sample 4
Input
1Output
1Required structure
#include <stdio.h>int factorial(int n) { // TODO: compute and return n!}int main() { int n; scanf("%d", &n); printf("%d\n", factorial(n)); return 0;}def factorial(n): # TODO: compute and return n! passn = int(input())print(factorial(n))for loop multiplying from 1 to n. Why is n capped at 12? Because 13! = 6,227,020,800 which is larger than the maximum int (about 2.1 billion). Python doesn't have this problem — its integers can be arbitrarily large — but we use the same constraint to keep the two versions equivalent.Write a function int reverse_int(int n) that takes a non-negative integer and returns the integer formed by reversing its digits.
- 1234 → 4321
- 100 → 1 (leading zeros are dropped, because integers have no leading zeros)
- 7 → 7
- 120030 → 30021
Input format
One line containing a single non-negative integer n.Constraint: 0 ≤ n ≤ 1,000,000Output format
One line containing the digits of n in reverse order, as an integer.Sample 1
Input
1234Output
4321Sample 2
Input
100Output
1Sample 3
Input
9Output
9Sample 4
Input
120030Output
30021Required structure
#include <stdio.h>int reverse_int(int n) { // TODO: return n with its digits reversed}int main() { int n; scanf("%d", &n); printf("%d\n", reverse_int(n)); return 0;}def reverse_int(n): # TODO: return n with its digits reversed passn = int(input())print(reverse_int(n))while loop: take the last digit of n with n % 10, then "shift" the result by doing result = result * 10 + last_digit, and drop the last digit of n with n = n / 10 (in Python, use n //= 10). Stop when n becomes 0. Edge case: n = 0 should output 0 — make sure your loop handles this correctly.Write a function int is_prime(int n) (you can reuse your logic from Week 1) that returns 1 if n is prime, 0 otherwise.
Then in main: read two integers L and R, and print every prime in the inclusive range [L, R] in increasing order, one per line. If there are no primes in the range, print NONE on a single line.
Input format
One line containing two positive integers L and R separated by a space.Constraint: 2 ≤ L ≤ R ≤ 10,000Output format
Each prime in [L, R] on its own line, in increasing order.If no primes exist in the range, print NONE on a single line.Sample 1
Input
2 10Output
2357Sample 2
Input
14 16Output
NONESample 3
Input
20 30Output
2329Sample 4
Input
7 7Output
7Required structure
#include <stdio.h>int is_prime(int n) { // TODO: return 1 if n is prime, 0 otherwise}int main() { int L, R; scanf("%d %d", &L, &R); // TODO: loop from L to R, print every prime on its own line. // If no primes, print "NONE". return 0;}def is_prime(n): # TODO: return True if n is prime, False otherwise passL, R = map(int, input().split())# TODO: loop from L to R, print every prime on its own line.# If no primes, print "NONE".int count = 0) that you increment every time you find a prime. After the loop, if count is still 0, print "NONE". Use the √n optimization from Week 1 inside is_prime — the function will be called many times, so making it fast matters.