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

时间:2015-10-18 14:32:23

标签: 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

结果:

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

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

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

如何实现创建FizzBu​​zz?你会如何向初学者解释这一点?

1 个答案:

答案 0 :(得分:4)

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

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

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

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

如果我们将此字符串转换为十六进制,我们会从您的代码段中获取魔术常量v:0x30490610

我们可以使用表达式v提取j = v & 3的底部两位,因为数字3设置了底部的两位而其余的未设置。 (这是Python的“按位AND”操作符。)

我们可以通过将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的范围内,因此输出是对的。

作为练习,请尝试将v更改为0x39999999,看看是否可以解释这种行为。 (提示:十六进制中的9是二进制中的10 01。)

更新:以下是该计划的变体。我已将十六进制值v替换为响应的显式队列q,并且可怕的v = v >> 2 | j << 28已被替换为前面的弹出和后推,{{1} }。

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)) fizz队列:

buzz

由于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)) 是一个假值,''只要(f[0] + b[0]) or ii都是空字符串,就会打印整数f[0]