余弦值 FizzBuzz
Solving Fizz Buzz with Cosines

原始链接: https://susam.net/fizz-buzz-with-cosines.html

## Fizz Buzz:从简单测试到三角级数 Fizz Buzz 游戏——一个编程挑战,其中数字被替换为“Fizz”(能被 3 整除)、“Buzz”(能被 5 整除)或“FizzBuzz”(能被两者整除)——作为一项基本的编码技能测试,出人意料地受欢迎。本文探讨了对这个简单序列的一个令人惊讶的复杂数学表示。 传统上,Fizz Buzz 使用条件语句实现。然而,作者展示了一种使用闭合形式表达式(涉及三角函数)来表达该序列的方法。这是通过定义代表每个输出(数字、Fizz、Buzz、FizzBuzz)的“符号函数”以及选择适当符号的函数 *f(n)* 来实现的。 关键在于使用指示函数(能整除时为 1,不能整除时为 0)来表示 *f(n)*,并最终通过傅里叶级数将其替换为余弦函数。*f(n)* 的最终公式为:**(11/15) + (2/3)cos(2πn/3) + (4/5)cos(2πn/5) + (4/5)cos(4πn/5)**。 这允许使用 `math.cos` 和 `math.pi` 进行简洁的 Python 实现,甚至可以表示为 shell 一行命令。作者总结说,即使像 Fizz Buzz 这样简单的游戏,也可以使用傅里叶级数等高级数学概念来优雅(且不必要地)表示。

## 余弦函数与 Fizz Buzz:Hacker News 摘要 最近 Hacker News 的讨论围绕一篇博客文章([https://susam.net/fizz-buzz-with-cosines.html](https://susam.net/fizz-buzz-with-cosines.html))展开,该文章详细介绍了一种使用余弦函数和离散傅里叶变换 (DFT) 来解决经典 FizzBuzz 问题的非常规方法。作者最初尝试了 DFT 方法,但发现基于可除性属性的捷径更有效且更有趣。 这篇文章引发了关于过度工程、FizzBuzz 作为面试题的目的(态度与能力)、以及替代实现方式的讨论。 几位用户提到了相关的讽刺文章([https://aphyr.com/](https://aphyr.com/)),这些文章探讨了对这个问题越来越复杂和荒谬的解决方案。 讨论还涉及了 LLM 在解决这个问题上的应用,一位用户分享了一个符号回归解决方案,另一位展示了一个极具表现力的 LLM 人设。 一场关于在考虑未来扩展或规则修改时,典型 FizzBuzz 实现的“正确性”的争论由此展开。 最终,该帖子突出了以非常规方式探索即使是简单问题的趣味性和启发性。
相关文章

原文

By Susam Pal on 20 Nov 2025

Fizz Buzz is a counting game that has become oddly popular in the world of computer programming as a simple test of basic programming skills. The rules of the game are straightforward. Players say the numbers aloud in order beginning with one. Whenever a number is divisible by 3, they say 'Fizz' instead. If it is divisible by 5, they say 'Buzz'. If it is divisible by both 3 and 5, the player says both 'Fizz' and 'Buzz'. Here is a typical Python program that prints this sequence:

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)

Here is the output: fizz-buzz.txt. Can we make the program more complicated? Perhaps we can use trigonometric functions to encode all four cases in a single closed-form expression. That is what we are going to explore in this article. By the end, we will obtain a finite Fourier series that can take any integer \( n \) and select the text to be printed.

Contents

Definitions

Before going any further, we establish a precise mathematical definition for the Fizz Buzz sequence. We begin by introducing a few functions that will help us define the Fizz Buzz sequence later.

Symbol Functions

We define a set of four functions \( \{ s_0, s_1, s_2, s_3 \} \) for integers \( n \) by: \begin{align*} s_0(n) &= n, \\ s_1(n) &= \mathtt{Fizz}, \\ s_2(n) &= \mathtt{Buzz}, \\ s_3(n) &= \mathtt{FizzBuzz}. \end{align*} We call these the symbol functions because they produce every term that appears in the Fizz Buzz sequence. The symbol function \( s_0 \) returns \( n \) itself. The functions \( s_1, \) \( s_2 \) and \( s_3 \) are constant functions that always return the literal words \( \mathtt{Fizz}, \) \( \mathtt{Buzz} \) and \( \mathtt{FizzBuzz} \) respectively, no matter what the value of \( n \) is.

Fizz Buzz Sequence

Now we can define the Fizz Buzz sequence as the sequence \[ (s_{f(n)}(n))_{n = 1}^{\infty} \] where \[ f(n) = \begin{cases} 1 & \text{if } 3 \mid n \text{ and } 5 \nmid n, \\ 2 & \text{if } 3 \nmid n \text{ and } 5 \mid n, \\ 3 & \text{if } 3 \mid n \text{ and } 5 \mid n, \\ 0 & \text{otherwise}. \end{cases} \] The notation \( m \mid n \) means that the integer \( m \) divides the integer \( n, \) i.e. \( n \) is a multiple of \( m. \) Equivalently, there exists an integer \( c \) such that \( n = cm . \) Similarly, \( m \nmid n \) means that \( m \) does not divide \( n, \) i.e. \( n \) is not a multiple of \( m. \) With the above definitions in place, we can expand the first few terms of the sequence explicitly as follows: \begin{align*} (s_{f(n)}(n))_{n = 1}^{\infty} &= (s_{f(1)}(1), \; s_{f(2)}(2), \; s_{f(3)}(3), \; s_{f(4)}(4), \; s_{f(5)}(5), \; s_{f(6)}(6), \; s_{f(7)}(7), \; \dots) \\ &= (s_0(1), \; s_0(2), \; s_1(3), \; s_0(4), s_2(5), \; s_1(6), \; s_0(7), \; \dots) \\ &= (1, \; 2, \; \mathtt{Fizz}, \; 4, \; \mathtt{Buzz}, \; \mathtt{Fizz}, \; 7, \; \dots). \end{align*} Note how the function \( f(n) \) produces an index \( i \) which we then use to select the symbol function \( s_i(n) \) to produce the \( n \)th term of the sequence.

Indicator Functions

Here is the function \( f(n) \) from the previous section with its cases and conditions rearranged to make it easier to spot interesting patterns: \[ f(n) = \begin{cases} 0 & \text{if } 5 \nmid n \text{ and } 3 \nmid n, \\ 1 & \text{if } 5 \nmid n \text{ and } 3 \mid n, \\ 2 & \text{if } 5 \mid n \text{ and } 3 \nmid n, \\ 3 & \text{if } 5 \mid n \text{ and } 3 \mid n. \end{cases} \] This function helps us to select another function \( s_{f(n)}(n) \) which in turn determines the \( n \)th term of the Fizz Buzz sequence. Our goal now is to replace this piecewise formula with a single closed-form expression. To do so, we first define indicator functions \( I_m(n) \) as follows: \[ I_m(n) = \begin{cases} 1 & \text{if } m \mid n, \\ 0 & \text{if } m \nmid n. \end{cases} \] The formula for \( f(n) \) can now be written as: \[ f(n) = \begin{cases} 0 & \text{if } I_5(n) = 0 \text{ and } I_3(n) = 0, \\ 1 & \text{if } I_5(n) = 0 \text{ and } I_3(n) = 1, \\ 2 & \text{if } I_5(n) = 1 \text{ and } I_3(n) = 0, \\ 3 & \text{if } I_5(n) = 1 \text{ and } I_3(n) = 1. \end{cases} \] Do you see a pattern? Here is the same function written as a table:

\( I_5(n) \) \( I_3(n) \) \( f(n) \)
\( 0 \) \( 0 \) \( 0 \)
\( 0 \) \( 1 \) \( 1 \)
\( 1 \) \( 0 \) \( 2 \)
\( 1 \) \( 1 \) \( 3 \)

Do you see it now? If we treat the values in the first two columns as binary digits and the values in the third column as decimal numbers, then in each row the first two columns give the binary representation of the number in the third column. For example, \( 3_{10} = 11_2 \) and indeed in the last row of the table, we see the bits \( 1 \) and \( 1 \) in the first two columns and the number \( 3 \) in the last column. In other words, writing the binary digits \( I_5(n) \) and \( I_3(n) \) side by side gives us the binary representation of \( f(n). \) Therefore \[ f(n) = 2 \, I_5(n) + I_3(n). \] We can now write a small program to demonstrate this formula:

for n in range(1, 101):
    s = [n, 'Fizz', 'Buzz', 'FizzBuzz']
    i = (n % 3 == 0) + 2 * (n % 5 == 0)
    print(s[i])

We can make it even shorter at the cost of some clarity:

for n in range(1, 101):
    print([n, 'Fizz', 'Buzz', 'FizzBuzz'][(n % 3 == 0) + 2 * (n % 5 == 0)])

What we have obtained so far is pretty good. While there is no universal definition of a closed-form expression, I think most people would agree that the indicator functions as defined above are simple enough to be permitted in a closed-form expression.

Complex Exponentials

In the previous section, we obtained the formula \[ f(n) = I_3(n) + 2 \, I_5(n) \] which we then used as an index to look up the text to be printed. We also argued that this is a pretty good closed-form expression already.

However, in the interest of making things more complicated, we must ask ourselves: What if we are not allowed to use the indicator functions? What if we must adhere to the commonly accepted meaning of a closed-form expression which allows only 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. It turns out that the above formula can be rewritten using only addition, multiplication, division and the cosine function. Let us begin the translation. Consider the sum \[ S_m(n) = \sum_{k = 0}^{m - 1} e^{2 \pi i k n / m}, \] where \( i \) is the imaginary unit and \( n \) and \( m \) are integers. This is a geometric series in the complex plane with ratio \( r = e^{2 \pi i n / m}. \) If \( n \) is a multiple of \( m , \) then \( n = cm \) for some integer \( c \) and we get \[ r = e^{2 \pi i n / m} = e^{2 \pi i c} = 1. \] Therefore, when \( n \) is a multiple of \( m, \) we get \[ S_m(n) = \sum_{k = 0}^{m - 1} e^{2 \pi i k n / m} = \sum_{k = 0}^{m - 1} 1^k = m. \] If \( n \) is not a multiple of \( m, \) then \( r \ne 1 \) and the geometric series becomes \[ S_m(n) = \frac{r^m - 1}{r - 1} = \frac{e^{2 \pi i n} - 1}{e^{2 \pi i n / m} - 1} = 0. \] Therefore, \[ S_m(n) = \begin{cases} m & \text{if } m \mid n, \\ 0 & \text{if } m \nmid n. \end{cases} \] Dividing both sides by \( m, \) we get \[ \frac{S_m(n)}{m} = \begin{cases} 1 & \text{if } m \mid n, \\ 0 & \text{if } m \nmid n. \end{cases} \] But the right-hand side is \( I_m(n). \) Therefore \[ I_m(n) = \frac{S_m(n)}{m} = \frac{1}{m} \sum_{k = 0}^{m - 1} e^{2 \pi i k n / m}. \]

Cosines

We begin with Euler's formula \[ e^{i x} = \cos x + i \sin x \] where \( x \) is a real number. From this formula, we get \[ e^{i x} + e^{-i x} = 2 \cos x. \] Therefore \begin{align*} I_3(n) &= \frac{1}{3} \sum_{k = 0}^2 e^{2 \pi i k n / 3} \\ &= \frac{1}{3} \left( 1 + e^{2 \pi i n / 3} + e^{4 \pi i n / 3} \right) \\ &= \frac{1}{3} \left( 1 + e^{2 \pi i n / 3} + e^{-2 \pi i n / 3} \right) \\ &= \frac{1}{3} + \frac{2}{3} \cos \left( \frac{2 \pi n}{3} \right). \end{align*} The third equality above follows from the fact that \( e^{4 \pi i n / 3} = e^{6 \pi i n / 3} e^{-2 \pi i n / 3} = e^{2 \pi i n} e^{-2 \pi i n/3} = e^{-2 \pi i n / 3}. \)

The function above is defined for integer values of \( n \) but we can extend its formula to real \( x \) and plot it to observe its shape between integers. As expected, the function takes the value \( 1 \) whenever \( x \) is an integer multiple of \( 3 \) and \( 0 \) whenever \( x \) is an integer not divisible by \( 3. \)

Graph
Graph of \( \frac{1}{3} + \frac{2}{3} \cos \left( \frac{2 \pi x}{3} \right) \)

Similarly, \begin{align*} I_5(n) &= \frac{1}{5} \sum_{k = 0}^4 e^{2 \pi i k n / 5} \\ &= \frac{1}{5} \left( 1 + e^{2 \pi i n / 5} + e^{4 \pi i n / 5} + e^{6 \pi i n / 5} + e^{8 \pi i n / 5} \right) \\ &= \frac{1}{5} \left( 1 + e^{2 \pi i n / 5} + e^{4 \pi i n / 5} + e^{-4 \pi i n / 5} + e^{-2 \pi i n / 5} \right) \\ &= \frac{1}{5} + \frac{2}{5} \cos \left( \frac{2 \pi n}{5} \right) + \frac{2}{5} \cos \left( \frac{4 \pi n}{5} \right). \end{align*} Extending this expression to real values of \( x \) allows us to plot its shape as well. Once again, the function takes the value \( 1 \) at integer multiples of \( 5 \) and \( 0 \) at integers not divisible by \( 5. \)

Graph
Graph of \( \frac{1}{5} + \frac{2}{5} \cos \left( \frac{2 \pi x}{5} \right) + \frac{2}{5} \cos \left( \frac{4 \pi x}{5} \right) \)

Recall that we expressed \( f(n) \) as \[ f(n) = I_3(n) + 2 \, I_5(n). \] Substituting these trigonometric expressions yields \[ f(n) = \frac{1}{3} + \frac{2}{3} \cos \left( \frac{2 \pi n}{3} \right) + 2 \cdot \left( \frac{1}{5} + \frac{2}{5} \cos \left( \frac{2 \pi n}{5} \right) + \frac{2}{5} \cos \left( \frac{4 \pi n}{5} \right) \right). \] A straightforward simplification gives \[ f(n) = \frac{11}{15} + \frac{2}{3} \cos \left( \frac{2 \pi n}{3} \right) + \frac{4}{5} \cos \left( \frac{2 \pi n}{5} \right) + \frac{4}{5} \cos \left( \frac{4 \pi n}{5} \right). \] We can extend this expression to real \( x \) and plot it as well. The resulting curve takes the values \( 0, 1, 2 \) and \( 3 \) at integer points, as desired.

Graph
Graph of \( \frac{11}{15} + \frac{2}{3} \cos \left( \frac{2 \pi x}{3} \right) + \frac{4}{5} \cos \left( \frac{2 \pi x}{5} \right) + \frac{4}{5} \cos \left( \frac{4 \pi x}{5} \right) \)

Now we can write our Python program as follows:

from math import cos, pi
for n in range(1, 101):
    s = [n, 'Fizz', 'Buzz', 'FizzBuzz']
    i = round(11 / 15 + (2 / 3) * cos(2 * pi * n / 3)
                      + (4 / 5) * cos(2 * pi * n / 5)
                      + (4 / 5) * cos(4 * pi * n / 5))
    print(s[i])

Conclusion

To summarise, we have defined the Fizz Buzz sequence as \[ (s_{f(n)}(n))_{n = 1}^{\infty} \] where \[ f(n) = \frac{11}{15} + \frac{2}{3} \cos \left( \frac{2 \pi n}{3} \right) + \frac{4}{5} \cos \left( \frac{2 \pi n}{5} \right) + \frac{4}{5} \cos \left( \frac{4 \pi n}{5} \right) \in \{ 0, 1, 2, 3 \} \] and \( s_0(n) = n, \) \( s_1(n) = \mathtt{Fizz}, \) \( s_2(n) = \mathtt{Buzz} \) and \( s_3(n) = \mathtt{FizzBuzz}. \) A Python program to print the Fizz Buzz sequence based on this definition was presented earlier. That program can be written more succinctly as follows:

from math import cos, pi
for n in range(1, 101):
    print([n, 'Fizz', 'Buzz', 'FizzBuzz'][round(11 / 15 + (2 / 3) * cos(2 * pi * n / 3) + (4 / 5) * (cos(2 * pi * n / 5) + cos(4 * pi * n / 5)))])

We can also wrap this up nicely in a shell one-liner, in case you want to share it with your friends and family and surprise them:

printf 'from math import cos, pi\nfor n in range(1, 101): print([n, "Fizz", "Buzz", "FizzBuzz"][round(11 / 15 + (2 / 3) * cos(2 * pi * n / 3) + (4 / 5) * (cos(2 * pi * n / 5) + cos(4 * pi * n / 5)))])' | python3

The keen-eyed might notice that the expression we have obtained for \( f(n) \) is a finite Fourier series. This is not surprising, since the output of a Fizz Buzz program depends only on \( n \bmod 15. \) Any function on a finite cyclic group can be written exactly as a finite Fourier expansion.

We have taken a simple counting game and turned it into a trigonometric construction: a finite Fourier series with a constant term \( 11/15 \) and three cosine terms with coefficients \( 2/3, \) \( 4/5 \) and \( 4/5. \) None of this makes Fizz Buzz any easier, of course, but it does show that every \( \mathtt{Fizz} \) and \( \mathtt{Buzz} \) now owes its existence to a particular set of Fourier coefficients. We began with the modest goal of making this simple problem more complicated. I think it is safe to say that we did not fall short.

联系我们 contact @ memedata.com