Numbers & Math: The Handful of Tricks Behind Every Math Problem
Math problems have a reputation for being intimidating — as if each one needs some clever theorem you were supposed to remember from school. The truth is far friendlier. Just like array problems, the whole space is covered by a small set of reusable techniques. Once you can name them, "is this a prime?", "convert to binary," and "sum the first n even numbers" stop feeling like separate battles and start feeling like the same few tools applied again and again.
In this post we will take the small math problems we have already solved, pull out the technique each one teaches, and map every practice problem onto those techniques. We will split it into two natural families: number theory & arithmetic, and bases & bits.
Part 1: Number Theory & Arithmetic
Technique 1 — Closed-Form Instead of a Loop
The problem that teaches it: Sum of the first n even numbers.
The idea in one line: before writing a loop to add things up, ask whether a formula gives you the answer in one step.
The loop version is the obvious one — walk from 1 to n and add 2, 4, 6, …:
function sumOfEvenLoop(n) {
let sum = 0;
for (let i = 1; i <= n; i++) {
sum += 2 * i; // the i-th even number is 2i
}
return sum;
}That is O(n) — fine, but not the point. The point is to notice that adding 2 + 4 + 6 + … + 2n is just 2 × (1 + 2 + … + n), and the famous sum 1 + 2 + … + n equals n(n+1)/2 (the trick young Gauss reportedly discovered: pair the first and last, 1 + n, 2 + (n-1), each pair sums to n+1, and there are n/2 pairs). Multiply by 2 and it collapses to:
function sumOfEvenFormula(n) {
return n * (n + 1); // closed form — O(1), no loop at all
}
console.log(sumOfEvenFormula(3)); // 12 (2 + 4 + 6)The lesson is not this one formula — it is the habit: when you are summing a regular, patterned sequence, a closed-form often exists and turns O(n) into O(1). It also shows the interviewer you see the structure, not just the brute force.
Similar problems to practice:
- Sum of the first n odd numbers — a gorgeous one:
1 + 3 + 5 + … = n²exactly. Try to see why (each new odd number adds the next "L-shaped" layer to a square). - Sum of the first n squares —
n(n+1)(2n+1)/6. Same spirit: recognise the pattern, reach for the formula.
Technique 2 — Divisor Check Up to √n
The problem that teaches it: Is a number prime?
The idea in one line: to test for a divisor, you only need to check up to the square root of the number — not all the way up to the number itself.
Here is the intuition, and it is beautiful. Divisors always come in pairs. If 36 = 4 × 9, then finding 4 automatically tells you about 9. In every such pair, one member is ≤ √n and the other is ≥ √n. So if a number has any divisor, the smaller partner of the pair is guaranteed to appear at or below √n. Check that far, and if you found nothing, there is nothing to find.
function isPrime(n) {
if (n < 2) return false; // 0 and 1 are not prime
for (let i = 2; i * i <= n; i++) { // i * i <= n means i <= √n
if (n % i === 0) return false; // found a divisor → not prime
}
return true; // no divisor found → prime
}
console.log(isPrime(29)); // true
console.log(isPrime(35)); // false (5 × 7)Notice i * i <= n instead of i <= Math.sqrt(n) — it avoids floating-point fuzziness and is a touch faster. This √n idea takes a naive O(n) check down to O(√n), which is a massive win for large numbers.
Similar problems to practice:
- Count primes below n (Sieve of Eratosthenes) — when you need all primes up to
n, do not test each one individually. Instead, start from 2 and cross out all its multiples, then the next uncrossed number, and so on. It is the divisor idea turned inside out, and it is far faster (about O(n log log n)) for bulk prime-finding.
Technique 3 — Integer Roots (Careful With Floating Point)
The problem that teaches it: Is a number a perfect square?
The idea in one line: to check if n is a perfect square, take its integer square root r and verify that r × r really equals n.
The subtlety here is floating point. Math.sqrt(n) gives a double, and for very large n it can be off by a tiny amount (say 9999999999999999.9998), so blindly rounding can give the wrong answer. The safe pattern is to round to the nearest integer and then verify by squaring back — integer multiplication is exact:
function isPerfectSquare(n) {
if (n < 0) return false;
let r = Math.round(Math.sqrt(n));
return r * r === n; // verify with exact integer math
}
console.log(isPerfectSquare(144)); // true (12 × 12)
console.log(isPerfectSquare(145)); // falseIn Python this is exactly what math.isqrt(n) is for — it gives the exact integer square root with no floating point at all, so isqrt(n) ** 2 == n is bulletproof. If you ever cannot trust the language's sqrt, you can compute an integer root yourself with binary search (guess a root, square it, adjust up or down) — a great little exercise that reuses the binary-search pattern.
Similar problems to practice:
- Is a number a power of two / power of three — "power of two" has a famous one-line bit trick:
n > 0 && (n & (n - 1)) === 0(a power of two has exactly one bit set, and subtracting 1 flips it and everything below). "Power of three" has no bit trick, so you divide by 3 repeatedly and check you end at 1. - Greatest common divisor (Euclid's algorithm) —
gcd(a, b) = gcd(b, a % b)untilbis 0. One of the oldest and most elegant algorithms; it also underlies fraction simplification. - Factorial and Fibonacci (iterative) — both are "accumulate in a loop" problems: keep a running product for factorial, or track the last two values for Fibonacci. The lesson is to do them iteratively to avoid the overhead (and stack limits) of naive recursion.
Part 2: Bases & Bits
Numbers do not have to live in base 10. This family is all about converting a number between representations, and it rests on two mirror-image techniques.
Technique 4 — Repeated Division (Number → Base)
The problem that teaches it: Convert an integer to binary.
The idea in one line: to write a number in another base, repeatedly divide by that base; the remainders, read in reverse, are the digits.
Think about what a digit means. The remainder of n ÷ 2 is the last binary bit (0 if even, 1 if odd). Divide away that bit and repeat, and you peel off the bits from least-significant to most-significant. Since we discover them in reverse, we prepend each new bit to the front:
function intToBinary(n) {
if (n === 0) return "0";
let negative = n < 0;
n = Math.abs(n); // handle the sign separately
let bits = "";
while (n > 0) {
bits = (n % 2) + bits; // remainder is the next bit; prepend it
n = Math.floor(n / 2); // divide away that bit
}
return negative ? "-" + bits : bits;
}
console.log(intToBinary(13)); // "1101" (8 + 4 + 1)
console.log(intToBinary(-6)); // "-110"Two details worth calling out: we handle the sign up front (take the absolute value, remember the minus), and we treat zero as a special case (the loop would otherwise produce an empty string). This "repeated division, collect remainders" recipe is completely general — it works for any base, not just 2.
Similar problems to practice:
- Decimal ↔ hex / octal / any base — the exact same loop, just divide by 8, or 16, or
b. For hex you map remainders 10–15 to the lettersa–f. Once you see base-2 is not special, all base conversions become one function with a parameter.
Technique 5 — Horner's Method (Base → Number)
The problem that teaches it: Convert binary to decimal.
The idea in one line: to read digits back into a number, sweep left to right doing result = result × base + digit — no powers, no exponents.
The naive way multiplies each bit by a power of two (bit × 2^position) and sums them. Horner's method is cleaner and avoids computing powers at all. The trick: every time you shift to include one more digit, everything you have accumulated so far is worth base times more, so you multiply the running total by the base and add the new digit:
function binaryToDecimal(bits) {
let result = 0;
for (let ch of bits) {
result = result * 2 + (ch === '1' ? 1 : 0);
}
return result;
}
console.log(binaryToDecimal("1101")); // 13Walk "1101" through it: start 0 → 0×2+1 = 1 → 1×2+1 = 3 → 3×2+0 = 6 → 6×2+1 = 13. Each step "makes room" for the next bit by multiplying by 2, then drops it in. This is exactly how you would evaluate a polynomial efficiently, which is why it carries Horner's name — and again, swap the 2 for any base and it reads that base.
Similar problems to practice:
- Count set bits (number of 1s) — the elegant trick is
n & (n - 1), which erases the lowest set bit; count how many times you can do that beforenhits 0. (This is the same one-bit insight behind the power-of-two check.) - Add two binary strings — school-style addition, right to left, tracking a carry. It reuses the "process digit by digit with a carry" idea that also solves add-two-numbers and multiply-strings.
- Roman numerals ↔ integer — a positional/lookup problem: scan the symbols, and when a smaller value sits before a larger one (like
IV), subtract instead of add. Different surface, same "read symbols left to right and accumulate" muscle as Horner's method.
The Recognition Tables
Everything above, compressed for quick scanning:
| If the problem is about… | Reach for… |
|---|---|
| summing a regular sequence | closed-form formula (Technique 1) |
| testing primality / divisors | check up to √n (Technique 2) |
| roots / perfect squares | integer sqrt + verify (Technique 3) |
| number → some base | repeated division (Technique 4) |
| digits → a number | Horner's ×base + digit (Technique 5) |
And each practice problem mapped to the technique it stretches:
| Practice problem | Technique it extends |
|---|---|
| Sum of first n odd / squares | Technique 1 (closed form) |
| Count primes below n (Sieve) | Technique 2 (divisors, in bulk) |
| Power of two / three | Technique 3 (bit trick / repeated division) |
| GCD (Euclid) | Technique 3 family (repeated reduction) |
| Factorial, Fibonacci (iterative) | loop accumulation |
| Decimal ↔ hex / octal / any base | Technique 4 (repeated division) |
| Count set bits | bit trick n & (n-1) |
| Add two binary strings | digit-by-digit with carry |
| Roman numerals ↔ integer | Horner-style left-to-right scan |
Final Thoughts
Math problems feel scary only until you have names for the moves. Summing a pattern? Look for a formula. Testing divisibility? Stop at √n. Converting bases? Divide down, or Horner up. That is genuinely most of it.
So the next time a "math" question appears, resist the urge to hunt for a half-remembered theorem. Instead ask:
"Is this a pattern to sum, a divisibility check, a root question, or a base conversion?"
Nine times out of ten it is one of those four, and you already own the tool. That shift — from fearing math problems to categorising them — is the whole game.
Happy coding, and all the best for your interviews!