I don't know how they discovered to raise to the fourth power, but the -15 is because FizzBuzz deals with multiples of 3 or multiples of 5 or multiples of both 3 and 5 (ie, multiples of 15)...then negating it ends up working with negative indices quite well. We can see that it works with Modular Exponentiation. The Memory-efficient method section there says:
c mod m = (a ⋅ b) mod m
c mod m = [(a mod m) ⋅ (b mod m)] mod m
In our case, the c is our n, so we have
c ** 4 % m
using the law of exponents, we know that (c ** e1) * (c ** e2) = c ** (e1 + e2)
, so c ** 4 = (c ** 2) * (c ** 2)
, so we now have an a
and a b
, which are both c ** 2
. Thus:
(c ** 4) % m = ((c ** 2) * (c ** 2)) % m
= (((c ** 2) % m) * ((c ** 2) % m)) % m
= (((c ** 2) % m) ** 2) % m
and following the same steps, again:
(c ** 2) % m = (c * c) % m
= ((c % m) * (c % m)) % m
= ((c % m) ** 2) % m
and finally:
(c ** 4) % m = ((((c % m) ** 2) % m) ** 2) % m
When m = -15
, the only values for c % m
are (-14..0)
and we can build a simple table to look at. Since we only ever operate on the result of a modulo, we only need to be able to prove these 15 numbers work:
c%m **2 %m **2 %m
-14 => 196 => -14 => 196 => -14
-13 => 169 => -11 => 121 => -14
-12 => 144 => -06 => 36 => -09
-11 => 121 => -14 => 196 => -14
-10 => 100 => -05 => 25 => -05
-09 => 81 => -09 => 81 => -09
-08 => 64 => -11 => 121 => -14
-07 => 49 => -11 => 121 => -14
-06 => 36 => -09 => 81 => -09
-05 => 25 => -05 => 25 => -05
-04 => 16 => -14 => 196 => -14
-03 => 9 => -06 => 36 => -09
-02 => 4 => -11 => 121 => -14
-01 => 1 => -14 => 196 => -14
00 => 0 => 00 => 0 => 00
Now, looking at our table, the values for all multiples of 3 are -09
, the values for all multiples of 5 are -05
, and things that are multiples of 3 and 5 are set to 00
; everything else is -14
(If we had used 15 instead of -15, we'd have 6, 10, 0, and 1, respectively, and would need a look up to turn that into string indices). Plugging those in for the start parameter of String#[]
with the string 'FizzBuzz '
gives us:
'FizzBuzz '[-9] # => 'F'
'FizzBuzz '[-5] # => 'B'
'FizzBuzz '[0] # => 'F'
'FizzBuzz '[-14]# => nil
and adding 13 to those numbers to get the length:
'FizzBuzz '[-9, 4] # => "Fizz"
'FizzBuzz '[-5, 8] # => "Buzz "
'FizzBuzz '[0, 13] # => "FizzBuzz "
'FizzBuzz '[-14, -1] # => nil