Python中基于十六进制的FizzBu​​zz

Use*_*453 3 python fizzbuzz

我相信对于大多数人来说,您应该熟悉FizzBu​​zz是什么。

对于那些不知道我在这里说什么的人。这是FizzBu​​zz:

编写一个程序,打印从1到100的数字。但是,对于三个数字的倍数,请打印“ Fizz”(而不是数字),对于五个数字的倍数,请打印“ Buzz”。对于三和五的倍数的数字,请打印“ FizzBu​​zz”。

对于大多数人来说,这可能很容易创建。

尽管在网上浏览后,我发现有些帖子在不使用模运算符的情况下受到挑战。

我发现此python代码有趣:

m = [None, "Fizz", "Buzz", "FizzBuzz"]
v = 0x30490610
for i in range(1, 101):
    j = v & 3
    print(m[j] if j else i)
    v = v >> 2 | j << 28
Run Code Online (Sandbox Code Playgroud)

结果:

1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
31
32
Fizz
34
Buzz
Fizz
37
38
Fizz
Buzz
41
Fizz
43
44
FizzBuzz
46
47
Fizz
49
Buzz
Fizz
52
53
Fizz
Buzz
56
Fizz
58
59
FizzBuzz
61
62
Fizz
64
Buzz
Fizz
67
68
Fizz
Buzz
71
Fizz
73
74
FizzBuzz
76
77
Fizz
79
Buzz
Fizz
82
83
Fizz
Buzz
86
Fizz
88
89
FizzBuzz
91
92
Fizz
94
Buzz
Fizz
97
98
Fizz
Buzz
Run Code Online (Sandbox Code Playgroud)

我的问题是它是如何做到的?

我知道'v'变量包含一个十六进制值。

如何实现创建FizzBu​​zz?您将如何向总初学者解释?

Mat*_*Rav 5

首先,我们观察到FizzBu​​zz模式是循环的,长度为15,因为n % 3 = (n + 15) % 3n % 5 = (n + 15) % 5

n是否可以被3和/或5整除可以存储以下两个信息位:00都不表示,01表示可被3整除,10表示可以被5整除,11表示可以被3和5整除。

FizzBu​​zz的数字1到15的“答案”从右到左如下:

11 00 00 01 00 10 01 00 00 01 10 00 01 00 00。

注意,每个第三对位都设置了右位,而每个第五对位都设置了左位。最右边的对对应于数字1,最左边的对对应于数字15。也许更清楚地将左位和右位分开:

v5:    1. 0. 0. 0. 0. 1. 0. 0. 0. 0. 1. 0. 0. 0. 0.
v3:    .1 .0 .0 .1 .0 .0 .1 .0 .0 .1 .0 .0 .1 .0 .0
v5|v3: 11 00 00 01 00 10 01 00 00 01 10 00 01 00 00
Run Code Online (Sandbox Code Playgroud)

如果我们将此位字符串转换为十六进制,则会从您的代码段中获得魔法常数v 0x30490610

我们可以v使用表达式提取的底两位j = v & 3,因为数字3的底两位被置位,其余的未置位。(这是Python的“按位与”运算符。)

通过v向右移动两位v >> 2,然后在另一端加两位,可以在2 * 15 = 30位周围循环(v >> 2) | (j << 28)。(这些是Python的左移和右移运算符,它们也按位方式工作。)

这样,v可以看作是包含2位元素的“队列”,每个元素对应于要处理的下一个15个数字之一的“正确FizzBu​​zz答案”。j从此队列中弹出元素后,该元素将被推送到另一端,因此从现在开始15次迭代就可以再次使用它。

最后一件事:语法的print(m[j] if j else i)意思是“如果j不是伪造的值(例如0),则打印m[j];否则,打印” i。因为m[1]m[2]m[3]包含对应于我们的FizzBu​​zz答案的2位表示右侧的字符串,并且j总是在0到3的范围内时,输出是正确的。

作为练习,尝试更改v0x39999999并查看是否可以解释该行为。(提示:十六进制的9是二进制的1001。)

更新:这是该程序的变体。我已将十六进制值替换为v显式q的响应队列,而看上去令人恐惧的外观v = v >> 2 | j << 28已替换为从前面弹出并向后推的弹出q.append(q.pop(0))

q = ['', '', 'Fizz', '', 'Buzz',
     'Fizz', '', '', 'Fizz', 'Buzz',
     '', 'Fizz', '', '', 'FizzBuzz']
for i in range(1, 101):
    print(q[0] or i)
    q.append(q.pop(0))
Run Code Online (Sandbox Code Playgroud)

我们还可以添加一个单独的fizzbuzz队列:

f = ['', '', 'Fizz']
b = ['', '', '', '', 'Buzz']
for i in range(1, 101):
    print((f[0] + b[0]) or i)
    f.append(f.pop(0))
    b.append(b.pop(0))
Run Code Online (Sandbox Code Playgroud)

由于''是falsy值,(f[0] + b[0]) or i将打印整数i,只要双方f[0]b[0]为空字符串。