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...
https://claude.ai/share/5664fb69-78cf-4723-94c9-7a381f947633
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",
) model.fit(X,Y)
Result I got is this:((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
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'
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.
I think that implementation will break down around 2^50 or so.
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
There are a surprising number of ways to generate the fizzbuzz sequence. I always liked this one:
fizzbuzz n = case (n^4 `mod` 15) of
1 -> show n
6 -> "fizz"
10 -> "buzz"
0 -> "fizzbuzz"
fb :: IO ()
fb = print $ map fizzbuzz [1..30]So, there’s a similar way to do it with a function that produces one of the characters in “FizBu\nx” and a while true loop that
- 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.
Made me envision this terrible idea.
arr = [];
y = 0;
setInterval(()=>{arr[y]=x},10)
setInterval(()=>{x=y++},1000)
setInterval(()=>{x="fizz"},3000)
setInterval(()=>{x="buzz"},5000)
setInterval(()=>{x="fizzbuzz"},15000)
This seems like a great benchmark task for LLMs.
Where the madness leads: https://cspages.ucalgary.ca/~robin/class/449/Evolution.htm
I wonder where this is coming from. I saw on USENET in comp.os.linux.misc a conversation about fizzbuzz too. That was on Nov 12.
Anyway an interesting read.
This is very nice.
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.
(The divisions will get optimized away.)
There are several mentions of "closed-form expression" without precisely defining what that means, only "finite combinations of basic operations".
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.
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.
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.