Arithmetic essentials
The arithmetic that algorithm analysis leans on: integer division and remainder, floor and ceiling, absolute value, powers, roots, logarithms, exponent laws, factorials and scientific notation, with the Java forms that avoid rounding and overflow bugs.
Learning goals
- Use division, remainder, floor and ceiling correctly for positive and negative numbers
- Compute powers, roots and logarithms in integer arithmetic
- Know how fast factorials and powers outgrow int and long
Prerequisites
Integer division
For integers, a / b gives the quotient truncated toward zero and a % b the remainder, with a == (a / b) * b + a % b. Dividing by 10 removes the last digit and % 10 reads it, which is how you process digits.
Run the digit-sum animation: each step takes n % 10 and then n / 10.
1int n = 4072, s = 0;2while (n > 0) { s += n % 10; n /= 10; } // 2 + 7 + 0 + 4 = 133int q = 17 / 5, r = 17 % 5; // q = 3, r = 2: 17 = 3*5 + 2
Sum of digits
Interactive · edit the inputRecursion tree · 1 call
Call sumDigits(4821).
1static int sumDigits(int n) {2if (n == 0) return 0; // no digits left3return n % 10 + sumDigits(n / 10); // last digit + the rest4}
Live state
n
4821
Call stack (top first)
- sumDigits(4821)
Execution trace (dry run table)Show 20 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | n=4821 | Call sumDigits(4821). |
| 2 | L2 | n=4821 | Split 4821 into last digit 1 and the rest 482. |
| 3 | L3 | n=4821 digit=1 | Call sumDigits(482) for the remaining digits. |
| 4 | L1 | n=482 | Call sumDigits(482). |
| 5 | L2 | n=482 | Split 482 into last digit 2 and the rest 48. |
| 6 | L3 | n=482 digit=2 | Call sumDigits(48) for the remaining digits. |
| 7 | L1 | n=48 | Call sumDigits(48). |
| 8 | L2 | n=48 | Split 48 into last digit 8 and the rest 4. |
| 9 | L3 | n=48 digit=8 | Call sumDigits(4) for the remaining digits. |
| 10 | L1 | n=4 | Call sumDigits(4). |
| 11 | L2 | n=4 | Split 4 into last digit 4 and the rest 0. |
| 12 | L3 | n=4 digit=4 | Call sumDigits(0) for the remaining digits. |
| 13 | L1 | n=0 | Call sumDigits(0). |
| 14 | L2 | n=0 | Base case: no digits left. |
| 15 | L2 | n=0 return=0 | return 0. |
| 16 | L3 | n=4 return=4 | 4 + 0 = 4. |
| 17 | L3 | n=48 return=12 | 8 + 4 = 12. |
| 18 | L3 | n=482 return=14 | 2 + 12 = 14. |
| 19 | L3 | n=4821 return=15 | 1 + 14 = 15. |
| 20 | L3 | return=15 | The digits of 4821 sum to 15. Calls = number of digits + 1 = 5. |
Remainder
The remainder (modulo) tells how far a number is past the last multiple of the divisor. It is used for parity (n % 2), cyclic indexes ((i + 1) % n), hashing and number theory. In Java the sign follows the dividend, so use Math.floorMod when a non-negative result is required.
Euclid's algorithm uses the identity gcd(a, b) = gcd(b, a mod b). Step through it below.
1int even = 10 % 2; // 02int next = (4 + 1) % 5; // 0: wraps around a 5-element ring3int prev = Math.floorMod(0 - 1, 5); // 44int g = gcd(48, 18); // 65static int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
Greatest common divisor (Euclid)
Interactive · edit the inputRecursion tree · 1 call
Call gcd(252, 105).
1static int gcd(int a, int b) {2if (b == 0) return a; // base case3return gcd(b, a % b); // same answer, smaller numbers4}
Live state
a
252
b
105
Call stack (top first)
- gcd(252, 105)
Execution trace (dry run table)Show 16 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | a=252 b=105 | Call gcd(252, 105). |
| 2 | L2 | a=252 b=105 | b is not 0. 252 % 105 = 42, and gcd(a, b) = gcd(b, a % b). |
| 3 | L3 | a=252 b=105 a % b=42 | Call gcd(105, 42). |
| 4 | L1 | a=105 b=42 | Call gcd(105, 42). |
| 5 | L2 | a=105 b=42 | b is not 0. 105 % 42 = 21, and gcd(a, b) = gcd(b, a % b). |
| 6 | L3 | a=105 b=42 a % b=21 | Call gcd(42, 21). |
| 7 | L1 | a=42 b=21 | Call gcd(42, 21). |
| 8 | L2 | a=42 b=21 | b is not 0. 42 % 21 = 0, and gcd(a, b) = gcd(b, a % b). |
| 9 | L3 | a=42 b=21 a % b=0 | Call gcd(21, 0). |
| 10 | L1 | a=21 b=0 | Call gcd(21, 0). |
| 11 | L2 | a=21 b=0 | b is 0, so 21 divides everything left: the answer is 21. |
| 12 | L2 | a=21 b=0 return=21 | return 21. Nothing more to do on the way back: this is a tail call. |
| 13 | L3 | a=42 b=21 return=21 | return 21. Nothing more to do on the way back: this is a tail call. |
| 14 | L3 | a=105 b=42 return=21 | return 21. Nothing more to do on the way back: this is a tail call. |
| 15 | L3 | a=252 b=105 return=21 | return 21. Nothing more to do on the way back: this is a tail call. |
| 16 | L3 | return=21 | gcd(252, 105) = 21 after 4 calls. |
Floor and ceiling
⌊x⌋ is the greatest integer ≤ x and ⌈x⌉ is the smallest integer ≥ x. For positive integers, ⌈a / b⌉ = (a + b - 1) / b, which avoids floating point. For negative numbers Java division truncates, so use Math.floorDiv for floors.
Binary search takes ⌊log₂ n⌋ + 1 probes at most, and merge sort recursion depth is ⌈log₂ n⌉.
1int pages = (10 + 3 - 1) / 3; // ceil(10 / 3) = 42int f = Math.floorDiv(-7, 2); // -43int c = -Math.floorDiv(-(-7), 2); // ceil(-7 / 2) = -34double d = Math.ceil(10 / 3.0); // 4.0
Absolute value
|x| is the distance from zero. In code it measures differences regardless of direction: Math.abs(a - b). The triangle inequality |a + b| ≤ |a| + |b| appears in Manhattan distance and in proofs.
Beware: Math.abs(Integer.MIN_VALUE) is negative, and Math.abs(a - b) can overflow for large operands. Compare with Integer.compare when you only need order.
1int manhattan = Math.abs(1 - 4) + Math.abs(2 - (-2)); // 3 + 4 = 72long safeDiff = Math.abs((long) a - b); // widen before subtracting
Powers and roots
xⁿ is x multiplied n times; the n-th root undoes it. Integer powers grow quickly: 2³¹ already exceeds int. Compute powers with a loop or binary exponentiation (about log₂ n multiplications) rather than Math.pow, which returns an inexact double.
Watch fast exponentiation square the base and halve the exponent.
1static long pow(long base, int exp, long mod) {2long result = 1; base %= mod;3while (exp > 0) {4if ((exp & 1) == 1) result = result * base % mod;5base = base * base % mod;6exp >>= 1;7}8return result;9}10static long isqrt(long n) { // floor(sqrt(n)), safe up to Long.MAX_VALUE11if (n < 2) return n;12long r = (long) Math.sqrt((double) n);13while (r > n / r) r--; // r * r > n, written without multiplying14while (r + 1 <= n / (r + 1)) r++; // (r + 1)^2 <= n15return r;16}
Power function: halving the exponent
Interactive · edit the inputRecursion tree · 1 call
Call power(2, 13).
1static long power(long x, int n) {2if (n == 0) return 1; // x^0 = 13long half = power(x, n / 2); // solve a problem half the size4return (n % 2 == 0) ? half * half : half * half * x;5}
Live state
x
2
n
13
Call stack (top first)
- power(2, 13)
Execution trace (dry run table)Show 20 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | x=2 n=13 | Call power(2, 13). |
| 2 | L2 | n=13 | n = 13 is not 0. Halve it: 13 / 2 = 6. |
| 3 | L3 | x=2 n=13 | Compute power(2, 6) once and reuse it. |
| 4 | L1 | x=2 n=6 | Call power(2, 6). |
| 5 | L2 | n=6 | n = 6 is not 0. Halve it: 6 / 2 = 3. |
| 6 | L3 | x=2 n=6 | Compute power(2, 3) once and reuse it. |
| 7 | L1 | x=2 n=3 | Call power(2, 3). |
| 8 | L2 | n=3 | n = 3 is not 0. Halve it: 3 / 2 = 1. |
| 9 | L3 | x=2 n=3 | Compute power(2, 1) once and reuse it. |
| 10 | L1 | x=2 n=1 | Call power(2, 1). |
| 11 | L2 | n=1 | n = 1 is not 0. Halve it: 1 / 2 = 0. |
| 12 | L3 | x=2 n=1 | Compute power(2, 0) once and reuse it. |
| 13 | L1 | x=2 n=0 | Call power(2, 0). |
| 14 | L2 | n=0 | Base case: any number to the power 0 is 1. |
| 15 | L2 | n=0 return=1 | return 1. |
| 16 | L4 | n=1 half=1 return=2 | n is odd: half × half × x = 1 × 1 × 2 = 2. |
| 17 | L4 | n=3 half=2 return=8 | n is odd: half × half × x = 2 × 2 × 2 = 8. |
| 18 | L4 | n=6 half=8 return=64 | n is even: half × half = 8 × 8 = 64. |
| 19 | L4 | n=13 half=64 return=8192 | n is odd: half × half × x = 64 × 64 × 2 = 8192. |
| 20 | L4 | return=8192 | 2^13 = 8192 with 5 calls (about log₂ n) instead of 13 multiplications. |
Logarithms
log_b(n) is the exponent to which b must be raised to get n: log₂ 1024 = 10. Equivalently it counts how many times you can halve n before reaching 1. That is why binary search, balanced trees and heaps take O(log n).
Change the starting value in the animation and watch the number of halvings equal ⌊log₂ n⌋.
| n | log₂ n |
|---|---|
| 8 | 3 |
| 1,024 | 10 |
| 1,048,576 (2²⁰) | 20 |
| 1,000,000,000 | about 29.9 |
1int n = 1000, steps = 0;2while (n > 1) { n /= 2; steps++; } // steps = 9 = floor(log2(1000))3int floorLog2 = 31 - Integer.numberOfLeadingZeros(1000); // 94double ln = Math.log(1000) / Math.log(2); // 9.9657...
Logarithm = how many times can n be halved?
Interactive · edit the inputvalue of n after each step
log₂ 100 answers “to what power must 2 be raised to reach 100?”. Equivalently: how many halvings take 100 down to 1?
1static int halvings(int n) { // = floor(log2 n) for n >= 12int count = 0;3while (n > 1) {4n /= 2; // integer division5count++;6}7return count;8}910static int doublings(int n) { // = ceil(log2 n)11int count = 0;12for (long x = 1; x < n; x *= 2) count++;13return count;14}
Live state
n
100
count
0
Execution trace (dry run table)Show 14 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | n=100 count=0 | log₂ 100 answers “to what power must 2 be raised to reach 100?”. Equivalently: how many halvings take 100 down to 1? |
| 2 | L3 | n=100 count=0 | 100 > 1, so halve again. |
| 3 | L5 | n=50 count=1 | n = 50 after 1 halving. |
| 4 | L3 | n=50 count=1 | 50 > 1, so halve again. |
| 5 | L5 | n=25 count=2 | n = 25 after 2 halvings. |
| 6 | L3 | n=25 count=2 | 25 > 1, so halve again. |
| 7 | L5 | n=12 count=3 | n = 12 after 3 halvings. |
| 8 | L3 | n=12 count=3 | 12 > 1, so halve again. |
| 9 | L5 | n=6 count=4 | n = 6 after 4 halvings. |
| 10 | L3 | n=6 count=4 | 6 > 1, so halve again. |
| 11 | L5 | n=3 count=5 | n = 3 after 5 halvings. |
| 12 | L3 | n=3 count=5 | 3 > 1, so halve again. |
| 13 | L5 | n=1 count=6 | n = 1 after 6 halvings. |
| 14 | L7 | return=6 log2(n)=6.644 | return 6 = ⌊log₂ 100⌋ (log₂ 100 ≈ 6.644). Even for n = 1,000,000 this is only 19 steps, which is why O(log n) algorithms feel instant. |
Exponents
Laws: xᵃ · xᵇ = xᵃ⁺ᵇ, (xᵃ)ᵇ = xᵃᵇ, x⁻ᵃ = 1/xᵃ, x⁰ = 1. Exponentials grow faster than any polynomial, and logarithms grow slower than any positive power. Hence 2ⁿ ≫ n² ≫ n log n ≫ n ≫ log n for large n.
Useful anchors: 2¹⁰ = 1,024 ≈ 10³, so 2²⁰ ≈ 10⁶ and 2³⁰ ≈ 10⁹.
| Expression | Value |
|---|---|
| 2¹⁰ | 1,024 |
| 2²⁰ | 1,048,576 |
| 2³¹ - 1 | 2,147,483,647 (int max) |
| 2⁶³ - 1 | 9,223,372,036,854,775,807 (long max) |
| 10⁹ + 7 | 1,000,000,007 (common modulus) |
Factorials
n! = n · (n-1) · … · 1, with 0! = 1. It counts the orderings of n distinct items and grows faster than any exponential: 12! = 479,001,600 is the largest factorial in int, and 20! = 2,432,902,008,176,640,000 is the largest in long. 21! overflows long.
Algorithms that try all orderings are practical only up to about n = 10 or 11.
1long f = 1;2for (int i = 2; i <= 20; i++) f *= i; // 24329020081766400003// i = 21 would overflow: use BigInteger beyond 20!4BigInteger big = BigInteger.ONE;5for (int i = 2; i <= 30; i++) big = big.multiply(BigInteger.valueOf(i));
Scientific notation
Scientific notation writes a value as m × 10ᵉ. It makes constraints and growth easy to compare: n ≤ 2 × 10⁵ means 200,000. Java accepts 1e9 (a double) and 1_000_000_000 (an int).
(int) 1e9 is 1,000,000,000, but 1e18 does not fit in int and needs (long) 1e18 or the literal 1_000_000_000_000_000_000L.
| Notation | Value | Fits in |
|---|---|---|
| 1e5 | 100,000 | int |
| 1e9 | 1,000,000,000 | int |
| 2e9 | 2,000,000,000 | int (just) |
| 3e9 | 3,000,000,000 | long |
| 1e18 | 10¹⁸ | long |
| 1e19 | 10¹⁹ | BigInteger |
Practice ladder
Now try it yourself
Easy
- Compute ⌈17 / 5⌉ with integers only
- Count the halvings needed to reduce 100 to 1
Medium
- Write isqrt(long) without floating point errors
- Compute 3²⁰⁰ mod 1,000,000,007 with fast exponentiation
Advanced
- Find the smallest n for which n! exceeds 10¹⁸ and explain how to detect the overflow
Key takeaways
- Use (a + b - 1) / b for ceiling division and floorMod/floorDiv for negatives.
- log₂ n is the number of halvings; it explains every O(log n).
- 20! is the last factorial in long; 2³¹ - 1 and 2⁶³ - 1 are the integer limits.