Well, there must be an obvious solution where the fizzbuzz sequence is seen as a spectrum of two frequencies (1/3 and 1/5), and a Fourier transform gives us a periodic signal with peaks of one amplitude at fizz spots, another amplitude at buzz spots, and their sum at fizzbuzz spots. I mean. that would be approximately the same solution as the article offers, just through a more straightforward mechanism.
That is precisely how I began writing this post. I thought I'd demonstrate how to apply the discrete Fourier transform (DFT) but to do so for each of the 15 coefficients turned out to be a lot of tedious work. That's when I began noticing shortcuts for calculating each coefficient c_k based on the divisibility properties of k. One shortcut led to another and this post is the end result. It turns out it was far less tedious (and more interesting as well) to use the shortcuts than to perform a full-blown DFT calculation for each coefficient.
Of course, we could calculate the DFT using a tool, and from there work out the coefficients for the cosine terms. For example, we could get the coefficients for the exponential form like this:
That's certainly one way to avoid the tedious work but I decided to use the shortcuts as the basis for my post because I found this approach more interesting. The straightforward DFT method is perfectly valid as well and it would make an interesting post by itself.
Ah so taking the Fourier transform of this function[0]? The summation of the fizz and buzz frequencies don't lead to perfect peaks for the fizz and buzz locations. I need to revisit Fourier cause I would have thought the transform would have just recovered the two fizz and buzz peaks not the fizzbuzz spot.
There was another great satirical take on FizzBuzz which had something to do with runes and incantation and magical spells...? I sort of remember that the same author maybe even wrote a follow up? to this extremely experienced developer solving FizzBuzz in the most arcane way possible.
What a neat trick. I'm thinking you can abuse polynomials similarly. If the goal is to print the first, say, 100 elements, a 99-degree polynomial would do just fine :^)
EDIT: the llm gods do recreational mathematics as well. claude actually thinks it was able to come up with and verify a solution...
That's the most expletive-laden LLM output I've ever seen. ChatGPT would have aborted half way through to protect its pure and unsullied silicon mind from the filthy impure thoughts.
Inspired by this post & TF comment I tried symbollic regression [0]
Basically it uses genetic algorithm to find a formula that matches known input and output vectors with minimal loss
I tried to force it to use pi constant but was unable
I don't have much expreience with this library but I'm sure with more tweaks you'll get the right result
from pysr import PySRRegressor
def f(n):
if n % 15 == 0:
return 3
elif n%5 == 0:
return 2
elif n%3 == 0:
return 1
return 0
n = 500
X = np.array(range(1,n)).reshape(-1,1)
Y = np.array([f(n) for n in range(1,n)]).reshape(-1,1)
model = PySRRegressor(
maxsize=25,
niterations=200, # < Increase me for better results
binary_operators=["+", "*"],
unary_operators=["cos", "sin", "exp"],
elementwise_loss="loss(prediction, target) = (prediction - target)^2",
with compleixty 22 loss: 0.000015800686
The first term is close to 2/3 * cos(2pi*n/3) which is featured in the actual formula in the article. the constant doesn't compare to 11/15 though
HN is a great place to learn non-trivial things about trivial things, and that’s why I like it. My comment won’t add much to the discussion, but I just wanted to say that I learned something new today about a trivial topic I thought I already understood. Thank you, HN, for the great discussion thread.
I once had a coworker who used the FFT to determine whether coordinates formed a regular 2D grid. It didn't really work because of the interior points.
Background Context:
I am a machine vision engineer working with the Halcon vision library and HDevelop to write Halcon code. Below is an example of a program I wrote using Halcon:
* Generate a tuple from 1 to 1000 and name it 'Sequence'
tuple_gen_sequence (1, 1000, 1, Sequence)
* Replace elements in 'Sequence' divisible by 3 with 'Fizz', storing the result in 'SequenceModThree'
In this program, I applied a vectorization approach, which is an efficient technique for processing large datasets. Instead of iterating through each element individually in a loop (a comparatively slower process), I applied operations directly to the entire data sequence in one step. This method takes advantage of Halcon's optimized, low-level implementations to significantly improve performance and streamline computations.
Very cool! There's definitely some similarity to Ramanujan Sums, though the approach here sort of packages the fizz-buzz divisibility properties into one function.
https://en.wikipedia.org/wiki/Ramanujan%27s_sum
While it's cute use of mathematics, it's extremely inefficient in the real world because it introduces floating point multiplications and cos() which are very expensive. The only thing it lacks is branching which reduces the chances of a pipeline stall due to branch prediction miss.
This can be translated to the discrete domain pretty easily, just like the NTT. Pick a sufficiently large prime with order 15k, say, p = 2^61-1. 37 generates the whole multiplicative group, and 37^((2^61-2)/3) and 37^((2^61-2)/5) are appropriate roots of unity. Putting it all together yields
This involves 6 exponentiations by n with constant bases. Because in fizzbuzz the inputs are sequential, one can further precompute c^(2^i) and c^(-2^i) and, having c^n, one can go to c^(n+1) in average 2 modular multiplications by multiplying the appropriate powers c^(+-2^i) corresponding to the flipped bits.
> There are several mentions of "closed-form expression" without precisely defining what that means, only "finite combinations of basic operations".
There is no universal definition of 'closed-form expression'. But there are some basic operations and functions that are broadly accepted, and they are spelled out directly after the 'finite combinations' phrase you quoted from the post. Quoting the remainder of that sentence here:
'[...] finite combinations of basic operations such as addition, subtraction, multiplication, division, integer exponents and roots with integer index as well as functions such as exponentials, logarithms and trigonometric functions.'
The article conceit is fantastic. That said, is the going-in algo wrong?
I see a case for 3 * 5 in here:
for n in range(1, 101):
if n % 15 == 0:
print('FizzBuzz')
elif n % 3 == 0:
print('Fizz')
elif n % 5 == 0:
print('Buzz')
else:
print(n)
Why?
If we add 'Bazz' for mod 7, are we going to hardcode:
for n in range(1, 105):
if n % 105 == 0: # 3 * 5 * 7
print('FizzBuzzBazz')
elif n % 15 == 0: # 3 * 5
print('FizzBuzz')
elif n % 21 == 0: # 3 * 7
print('FizzBazz')
elif n % 35 == 0: # 5 * 7
print('BuzzBazz')
elif n % 3 == 0:
print('Fizz')
elif n % 5 == 0:
print('Buzz')
elif n % 7 == 0:
print('Bazz')
else:
print(n)
Or should we have done something like:
for n in range(1, 105):
out = ''
if n % 3 == 0:
out += 'Fizz'
if n % 5 == 0:
out += 'Buzz'
if n % 7 == 0:
out += 'Bazz'
print(out or n)
I've been told sure, but that's a premature optimization, 3 factors wasn't in the spec. OK, but if we changed our minds on even one of the two factors, we're having to find and change 2 lines of code ... still seems off.
Sort of fun to muse whether almost all FizzBuzz implementations are a bit wrong.
> Sort of fun to muse whether almost all FizzBuzz implementations are a bit wrong.
They're only wrong if they provide output that isn't in the spec. Adding "bazz" isn't in the spec, and assuming that something indeterminate MIGHT come later is also not part.
nine_k|3 months ago
susam|3 months ago
Of course, we could calculate the DFT using a tool, and from there work out the coefficients for the cosine terms. For example, we could get the coefficients for the exponential form like this:
https://www.wolframalpha.com/input?i=Fourier%5B%7B3%2C+0%2C+...
And then convert them to the coefficients for the cosine form like this:
https://www.wolframalpha.com/input?i=%7B11%2F15%2C+2*0%2C+2*...
That's certainly one way to avoid the tedious work but I decided to use the shortcuts as the basis for my post because I found this approach more interesting. The straightforward DFT method is perfectly valid as well and it would make an interesting post by itself.
mr_wiglaf|3 months ago
[0]: https://www.desmos.com/calculator/wgr3zvhazp
unknown|3 months ago
[deleted]
atemerev|3 months ago
Also probably easy enough to encode as quantum superpositions.
thomasjudge|3 months ago
gregsadetsky|3 months ago
Does this ring a bell for anyone?
---
Found it!
https://aphyr.com/posts/340-reversing-the-technical-intervie...
https://aphyr.com/posts/341-hexing-the-technical-interview
https://aphyr.com/posts/342-typing-the-technical-interview
https://aphyr.com/posts/353-rewriting-the-technical-intervie... (the FizzBuzz one)
https://aphyr.com/posts/354-unifying-the-technical-interview
wow.
arealaccount|3 months ago
taolson|3 months ago
https://github.com/taolson/Admiran/blob/main/examples/fizzBu...
isoprophlex|3 months ago
EDIT: the llm gods do recreational mathematics as well. claude actually thinks it was able to come up with and verify a solution...
https://claude.ai/share/5664fb69-78cf-4723-94c9-7a381f947633
jiggawatts|3 months ago
drob518|3 months ago
chaboud|3 months ago
mikestaas|3 months ago
user070223|3 months ago
((cos((x0 + x0) * 1.0471969) * 0.66784626) + ((cos(sin(x0 * 0.628323) * -4.0887628) + 0.06374673) * 1.1508249)) + 1.1086457
with compleixty 22 loss: 0.000015800686 The first term is close to 2/3 * cos(2pi*n/3) which is featured in the actual formula in the article. the constant doesn't compare to 11/15 though
[0] https://github.com/MilesCranmer/PySR
Quarrel|3 months ago
> Can we make the program more complicated? The words 'Fizz', 'Buzz' and
> 'FizzBuzz' repeat in a periodic manner throughout the sequence. What else is
> periodic?
and then I'm thinking ..
> Trigonometric functions!
is a good start, but there are so many places to go!
pillars001|3 months ago
ok123456|3 months ago
vincenthwt|3 months ago
* Generate a tuple from 1 to 1000 and name it 'Sequence'
tuple_gen_sequence (1, 1000, 1, Sequence)
* Replace elements in 'Sequence' divisible by 3 with 'Fizz', storing the result in 'SequenceModThree'
tuple_mod (Sequence, 3, Mod)
tuple_find (Mod, 0, Indices)
tuple_replace (Sequence, Indices, 'Fizz', SequenceModThree)
* Replace elements in 'Sequence' divisible by 5 with 'Buzz', storing the result in 'SequenceModFive'
tuple_mod (Sequence, 5, Mod)
tuple_find (Mod, 0, Indices)
tuple_replace (SequenceModThree, Indices, 'Buzz', SequenceModFive)
* Replace elements in 'Sequence' divisible by 15 with 'FizzBuzz', storing the final result in 'SequenceFinal'
tuple_mod (Sequence, 15, Mod)
tuple_find (Mod, 0, Indices)
tuple_replace (SequenceModFive, Indices, 'FizzBuzz', SequenceFinal)
Alternatively, this process can be written more compactly using inline operators:
tuple_gen_sequence (1, 1000, 1, Sequence)
tempThree:= replace(Sequence, find(Sequence % 3, 0), Fizz')
tempFive:= replace(tempThree, find(Sequence % 5, 0), 'Buzz')
FinalSequence := replace(tempFive, find(Sequence % 15, 0), 'FizzBuzz')
In this program, I applied a vectorization approach, which is an efficient technique for processing large datasets. Instead of iterating through each element individually in a loop (a comparatively slower process), I applied operations directly to the entire data sequence in one step. This method takes advantage of Halcon's optimized, low-level implementations to significantly improve performance and streamline computations.
layer8|3 months ago
siegelzero|3 months ago
makerofthings|3 months ago
Someone|3 months ago
- increases i on every \n,
- prints i when that produces x, otherwise prints the character
(Disregarding rounding errors)
That would be fairly obfuscated, I think.
econ|3 months ago
arr = [];
y = 0;
setInterval(()=>{arr[y]=x},10)
setInterval(()=>{x=y++},1000)
setInterval(()=>{x="fizz"},3000)
setInterval(()=>{x="buzz"},5000)
setInterval(()=>{x="fizzbuzz"},15000)
seattle_spring|3 months ago
raffael_de|3 months ago
unknown|3 months ago
[deleted]
throwaway81523|3 months ago
jmclnx|3 months ago
Anyway an interesting read.
acheron|3 months ago
ivansavz|3 months ago
burnt-resistor|3 months ago
(The divisions will get optimized away.)
pbsd|3 months ago
tantalor|3 months ago
TFA implies that branches (if statements and piecewise statements) are not allowed, but I don't see why not. Seems like a basic operation to me.
Nevermind that `s[i]` is essentially a piecewise statement.
susam|3 months ago
There is no universal definition of 'closed-form expression'. But there are some basic operations and functions that are broadly accepted, and they are spelled out directly after the 'finite combinations' phrase you quoted from the post. Quoting the remainder of that sentence here:
'[...] finite combinations of basic operations such as addition, subtraction, multiplication, division, integer exponents and roots with integer index as well as functions such as exponentials, logarithms and trigonometric functions.'
Terretta|3 months ago
I see a case for 3 * 5 in here:
Why?If we add 'Bazz' for mod 7, are we going to hardcode:
Or should we have done something like: I've been told sure, but that's a premature optimization, 3 factors wasn't in the spec. OK, but if we changed our minds on even one of the two factors, we're having to find and change 2 lines of code ... still seems off.Sort of fun to muse whether almost all FizzBuzz implementations are a bit wrong.
michaelcampbell|3 months ago
They're only wrong if they provide output that isn't in the spec. Adding "bazz" isn't in the spec, and assuming that something indeterminate MIGHT come later is also not part.
unknown|3 months ago
[deleted]
theendisney|3 months ago