Divide and conquer on a geometric sum
· algorithms
I wanted (1-r^{n+1})/(1-r). Mod does not keep that division. Cut the series in the middle: two copies of a shorter sum, O(log n).
I was summing a geometric series today:
then taking that modulo .
Closed form
My first reaction was: isn’t there a formula? Just use it. If ,
That formula divides by . Congruence does not preserve division, so this is not a method for .
Linear scan
The naive thing: start from , keep multiplying by , add into the running total. There are terms, so .
int geometric_sum_naive(int ratio, int max_exponent) { int total = 0; int current_term = 1; for (int exponent = 0; exponent <= max_exponent; ++exponent) { total += current_term; current_term *= ratio; } return total;}Divide and conquer
There is a smarter way, and this is divide and conquer as I see it. Cut the series in the middle, into two equal pieces.
When is odd, is even. Set . Then has terms, so it splits into two blocks of length :
Since , this is
The problem shrinks to . Half the input.
When is even, set . Now has terms, so the same two blocks of length leave a last term :
Half again.
So the recurrence is
with base , of course.
That brings the time down to . Remember the pieces: binary exponentiation is also , so it does not change the overall time.
const int kModulus = 10000;
int pow_mod(int base, int exponent) { int result = 1; int base_power = base; while (exponent > 0) { bool is_odd = (exponent & 1) == 1; exponent >>= 1; if (is_odd) { result = (result * base_power) % kModulus; } base_power = (base_power * base_power) % kModulus; } return result;}
int geometric_sum(int ratio, int max_exponent) { if (max_exponent == 1) { return 1 + ratio; }
if (max_exponent % 2 == 1) { return (1 + pow_mod(ratio, (max_exponent + 1) / 2)) * geometric_sum(ratio, (max_exponent - 1) / 2); }
return ((1 + pow_mod(ratio, max_exponent / 2)) * geometric_sum(ratio, (max_exponent / 2) - 1)) + pow_mod(ratio, max_exponent);}