Problem Analysis
A palindromic number reads identically forwards and backwards, like 906609. I want the largest such palindrome that can be written as the product of two n-digit numbers. The naive way to think about it is to enumerate every pair of n-digit factors and test each product: with 900 three-digit numbers that is roughly 900 x 900 = 810,000 pairs for n = 3, which is cheap, but the work is quadratic in the count of n-digit values, scaling as O(d^2) where d = 9 x 10^(n-1). At n = 5 that is 90,000^2 = 8.1 billion pairs, far too many to walk blindly.
The observation that makes the search tractable is a number-theoretic fact about even-length palindromes: every palindrome with an even number of digits is divisible by 11. The cleanest way to see this is with modular arithmetic: because 10 is congruent to -1 modulo 11, the power 10^k alternates between +1 and -1 as k changes parity. In an even-length palindrome the two equal digits of each symmetric pair sit at positions whose exponents have opposite parity, so each pair contributes 10^i + 10^j congruent to 1 + (-1) = 0 modulo 11. The whole number is therefore divisible by 11. Since the product a x b equals such a palindrome and 11 is prime, at least one of a or b must itself be divisible by 11. That single fact lets me step the inner loop by 11 instead of by 1, skipping ten of every eleven candidates. Combined with iterating downward (so large products surface first) and a monotonicity-based early exit, the practical running time drops to microseconds even at n = 5. The palindrome test itself is O(d) in the digit count d, so the pruned pair enumeration dominates the cost.
Solution Approach
Pruned Descending Enumeration with 11-Divisibility Stepping (Python and C)
Both files implement the same algorithm, so I describe it once and note the language differences at the end.
Step 1 - Set up bounds. For scale n, the largest n-digit number is max_number = 10^n - 1 and the smallest is min_number = 10^(n-1). I also precompute max_multiple_11 = max_number - max_number % 11, the largest multiple of 11 not exceeding max_number. That value is the starting point for the inner loop whenever a is not itself a multiple of 11.
Step 2 - Outer loop descending. I iterate a from max_number downward. Descending order means the first palindromes I encounter tend to be large, which raises the best-known value quickly and makes the inner early-exit fire sooner.
Step 3 - The 11 split. If a is already a multiple of 11, the divisibility constraint is satisfied no matter what b is, so b steps by 1 starting from max_number. Otherwise b must supply the factor of 11, so b steps by 11 starting from max_multiple_11. This is the core optimisation: in the common case it discards ten of every eleven inner iterations.
Step 4 - Early termination. Inside the inner loop I compute ab = a * b and break immediately if ab <= largest_palindrome. Because a is fixed and b only decreases, ab is monotonically decreasing, so once it falls to or below the best palindrome already found, no later b can beat it.
Step 5 - Palindrome test. I convert the product to a string and check whether it equals its reverse. If so, I record the new best. The C version additionally breaks out of the inner loop on the first palindrome found, which is valid given the descending order: for a fixed a, the first palindrome encountered is the largest possible for that a.
Language differences. The Python version builds the reversed string with "".join(reversed(str_number)) and compares. The C version writes the number into a stack buffer with snprintf and walks two pointers inward, avoiding any heap allocation. The C inner loop also explicitly breaks after recording a palindrome; Python omits that extra break, but its ab <= largest_palindrome guard achieves the same prune on the very next iteration. Both compute identical results.