Rosettacode.org has this excellent one-line FizzBuzz solution in Ruby.
1.upto(100){|n|puts'FizzBuzz '[i=n**4%-15,i+13]||n}
The trouble is, I don’t understand it. The part that puzzles me is the ”n to the power of 4 modulo -15”. Does anyone have an explanation or a reference to an explanation? I want to use this way of selecting substrings in other problems. For more information on FizzBuzz, see [https://rosettacode.org/wiki/FizzBuzz]
I'll try to add a simpler explanation to @Simple Lime's excellent answer. If
n
is a multiple of 3, let's denote it as3k
, Now:81 % 15 == 6
and let's subtract 15 (since it's modulo -15) to get -9.Likewise when
n
is a multiple of 5, it's625(k^4)
and625 % 15 == 10
and after subtracting we get -5.Otherwise, n might be a multiple of 2, 7, 11, and 13. In all these cases n^4 % 15 will be 1 (see Simple Lime's table) and -15 will get us -14.
Quite tricky.
Modulus is a periodic function. You can get more periodic function with the same schema, changing the exponent (k) and divisor (h):
Or just see the x,y pairs for the case:
The periodicity is evident choosing a basic example of
y = x % 2
:k = 1
andh = 2
. You get a series of1, 0, 1, 0, 1, ...
To visualise the function used in this case, you can plot in ruby for example using gnuplot gem.
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:
In our case, the c is our n, so we have
using the law of exponents, we know that
(c ** e1) * (c ** e2) = c ** (e1 + e2)
, soc ** 4 = (c ** 2) * (c ** 2)
, so we now have ana
and ab
, which are bothc ** 2
. Thus:and following the same steps, again:
and finally:
When
m = -15
, the only values forc % 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: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 to00
; 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 ofString#[]
with the string'FizzBuzz '
gives us:and adding 13 to those numbers to get the length: