1. Problems 11–20
Same format as Module 1: expand a problem to see the approach and both commented solutions.
P11
Check if a Number Is Prime
29 is prime; 30 isn't (it's divisible by 2, 3, 5, 6, 10, 15).
Check if a Number Is Prime
29 is prime; 30 isn't (it's divisible by 2, 3, 5, 6, 10, 15).
Approach: a number is prime if nothing between 2 and its square root divides it evenly — you never need to check past the square root, because any factor pair has one member below it and one above.
JavaScript
function isPrime(n) {
if (n < 2) return false; // 0, 1 and negatives are not prime
for (let i = 2; i * i <= n; i++) { // only need to check up to sqrt(n)
if (n % i === 0) return false; // found a divisor -> not prime
}
return true;
}
console.log(isPrime(29)); // true
console.log(isPrime(30)); // false
Python
def is_prime(n: int) -> bool:
if n < 2: # 0, 1 and negatives are not prime
return False
i = 2
while i * i <= n: # only need to check up to sqrt(n)
if n % i == 0: # found a divisor -> not prime
return False
i += 1
return True
print(is_prime(29)) # True
print(is_prime(30)) # False
P12
Check if a Number Is a Palindrome
12321 reads the same forwards and backwards; 12345 doesn't.
Check if a Number Is a Palindrome
12321 reads the same forwards and backwards; 12345 doesn't.
Approach: convert the number to a string and reuse the same "walk it backwards" idea from Module 1's string reversal — then compare original to reversed.
JavaScript
function isNumericPalindrome(n) {
const str = String(n);
let reversed = "";
for (let i = str.length - 1; i >= 0; i--) {
reversed += str[i];
}
return str === reversed;
}
console.log(isNumericPalindrome(12321)); // true
console.log(isNumericPalindrome(12345)); // false
Python
def is_numeric_palindrome(n: int) -> bool:
s = str(n)
reversed_s = ""
for i in range(len(s) - 1, -1, -1):
reversed_s += s[i]
return s == reversed_s
print(is_numeric_palindrome(12321)) # True
print(is_numeric_palindrome(12345)) # False
P13
Factorial (Iterative)
5! = 5 × 4 × 3 × 2 × 1 = 120.
Factorial (Iterative)
5! = 5 × 4 × 3 × 2 × 1 = 120.
Approach: keep a running product starting at 1, and multiply it by every whole number from 2 up to n.
JavaScript
function factorial(n) {
if (n < 0) throw new Error("Factorial is undefined for negative numbers");
let result = 1;
for (let i = 2; i <= n; i++) {
result *= i; // multiply the running product by each number up to n
}
return result;
}
console.log(factorial(5)); // 120
console.log(factorial(0)); // 1 (by definition)
Python
def factorial(n: int) -> int:
if n < 0:
raise ValueError("Factorial is undefined for negative numbers")
result = 1
for i in range(2, n + 1):
result *= i # multiply the running product by each number up to n
return result
print(factorial(5)) # 120
print(factorial(0)) # 1 (by definition)
P14
Nth Fibonacci Number (Iterative)
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 — the 10th term is 55.
Nth Fibonacci Number (Iterative)
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 — the 10th term is 55.
Approach: track only the previous two terms and slide them forward each iteration — no need to store the whole sequence.
JavaScript
function fibonacci(n) {
if (n <= 1) return n; // base cases: fib(0) = 0, fib(1) = 1
let prev = 0;
let curr = 1;
for (let i = 2; i <= n; i++) {
const next = prev + curr; // each number is the sum of the previous two
prev = curr;
curr = next;
}
return curr;
}
console.log(fibonacci(10)); // 55
Python
def fibonacci(n: int) -> int:
if n <= 1: # base cases: fib(0) = 0, fib(1) = 1
return n
prev, curr = 0, 1
for _ in range(2, n + 1):
prev, curr = curr, prev + curr # each number is the sum of the previous two
return curr
print(fibonacci(10)) # 55
P15
Armstrong Number Check
153 = 1³ + 5³ + 3³ — a number equal to its own digits raised to the digit count.
Armstrong Number Check
153 = 1³ + 5³ + 3³ — a number equal to its own digits raised to the digit count.
Approach: split the number into its digits, raise each one to the power of how many digits there are in total, sum the results, and compare that sum to the original number.
JavaScript
function isArmstrong(n) {
const digits = String(n).split("");
const power = digits.length;
let sum = 0;
for (const d of digits) {
sum += Math.pow(Number(d), power); // raise each digit to the digit count
}
return sum === n;
}
console.log(isArmstrong(153)); // true (1^3 + 5^3 + 3^3 = 153)
console.log(isArmstrong(123)); // false
Python
def is_armstrong(n: int) -> bool:
digits = str(n)
power = len(digits)
total = 0
for d in digits:
total += int(d) ** power # raise each digit to the digit count
return total == n
print(is_armstrong(153)) # True (1**3 + 5**3 + 3**3 = 153)
print(is_armstrong(123)) # False
P16
GCD of Two Numbers (Euclidean Algorithm)
gcd(48, 18) = 6.
GCD of Two Numbers (Euclidean Algorithm)
gcd(48, 18) = 6.
Approach: the Euclidean algorithm — repeatedly replace the pair (a, b) with (b, a mod b) until b reaches 0. Whatever a is at that point is the greatest common divisor.
JavaScript
function gcd(a, b) {
while (b !== 0) {
[a, b] = [b, a % b]; // replace (a, b) with (b, a mod b) until b hits 0
}
return Math.abs(a);
}
console.log(gcd(48, 18)); // 6
Python
def gcd(a: int, b: int) -> int:
while b != 0:
a, b = b, a % b # replace (a, b) with (b, a mod b) until b hits 0
return abs(a)
print(gcd(48, 18)) # 6
# Idiomatic one-liner for real code: math.gcd(48, 18)
P17
LCM of Two Numbers
lcm(4, 6) = 12.
LCM of Two Numbers
lcm(4, 6) = 12.
Approach: reuse GCD from the previous problem — the least common multiple is always |a × b| / gcd(a, b), so there's no need to write a second search from scratch.
JavaScript
function gcd(a, b) {
while (b !== 0) [a, b] = [b, a % b];
return Math.abs(a);
}
function lcm(a, b) {
return Math.abs(a * b) / gcd(a, b); // LCM = |a*b| / GCD(a,b)
}
console.log(lcm(4, 6)); // 12
Python
def gcd(a: int, b: int) -> int:
while b != 0:
a, b = b, a % b
return abs(a)
def lcm(a: int, b: int) -> int:
return abs(a * b) // gcd(a, b) # LCM = |a*b| / GCD(a,b)
print(lcm(4, 6)) # 12
P18
Reverse the Digits of an Integer
1234 becomes 4321; -56 becomes -65.
Reverse the Digits of an Integer
1234 becomes 4321; -56 becomes -65.
Approach: repeatedly pull off the last digit with % 10, append it to a growing result, then drop that digit with integer division by 10 — all without ever converting to a string.
JavaScript
function reverseNumber(n) {
const sign = n < 0 ? -1 : 1;
n = Math.abs(n);
let reversed = 0;
while (n > 0) {
reversed = reversed * 10 + (n % 10); // peel off the last digit, append it
n = Math.floor(n / 10);
}
return reversed * sign;
}
console.log(reverseNumber(1234)); // 4321
console.log(reverseNumber(-56)); // -65
Python
def reverse_number(n: int) -> int:
sign = -1 if n < 0 else 1
n = abs(n)
reversed_n = 0
while n > 0:
reversed_n = reversed_n * 10 + (n % 10) # peel off the last digit, append it
n //= 10
return reversed_n * sign
print(reverse_number(1234)) # 4321
print(reverse_number(-56)) # -65
P19
Perfect Number Check
28 = 1 + 2 + 4 + 7 + 14 — a number equal to the sum of its own proper divisors.
Perfect Number Check
28 = 1 + 2 + 4 + 7 + 14 — a number equal to the sum of its own proper divisors.
Approach: reuse the same square-root trick as the prime check — but instead of stopping at the first divisor, add up every divisor found (and its paired divisor n/i) along the way.
JavaScript
function isPerfectNumber(n) {
if (n < 2) return false;
let sum = 1; // 1 always divides n (for n > 1)
for (let i = 2; i * i <= n; i++) {
if (n % i === 0) {
sum += i;
if (i !== n / i) sum += n / i; // add the paired divisor too
}
}
return sum === n;
}
console.log(isPerfectNumber(28)); // true (1 + 2 + 4 + 7 + 14 = 28)
console.log(isPerfectNumber(12)); // false
Python
def is_perfect_number(n: int) -> bool:
if n < 2:
return False
total = 1 # 1 always divides n (for n > 1)
i = 2
while i * i <= n:
if n % i == 0:
total += i
if i != n // i:
total += n // i # add the paired divisor too
i += 1
return total == n
print(is_perfect_number(28)) # True (1 + 2 + 4 + 7 + 14 = 28)
print(is_perfect_number(12)) # False
P20
Sum of Digits of a Number
The digits of 12345 add up to 15.
Sum of Digits of a Number
The digits of 12345 add up to 15.
Approach: the same digit-peeling loop from problem 18, minus the reassembly step — just accumulate n % 10 into a running total each pass.
JavaScript
function sumOfDigits(n) {
n = Math.abs(n);
let sum = 0;
while (n > 0) {
sum += n % 10; // add the last digit
n = Math.floor(n / 10); // drop the last digit
}
return sum;
}
console.log(sumOfDigits(12345)); // 15
Python
def sum_of_digits(n: int) -> int:
n = abs(n)
total = 0
while n > 0:
total += n % 10 # add the last digit
n //= 10 # drop the last digit
return total
print(sum_of_digits(12345)) # 15
2. Key Takeaways
- Any "check every divisor" problem — primality, perfect numbers — only needs to loop up to the square root of n, because factors always pair up around it.
- Digit-by-digit problems (reversal, digit sum, Armstrong numbers) all lean on the same two operations:
n % 10to read the last digit, and integer division by 10 to drop it. - Iterative Fibonacci and factorial only need to remember the last one or two values, not the whole sequence — a pattern worth contrasting with the recursive versions in Module 5.