【问题标题】:Hexadecimal based FizzBuzz in PythonPython 中基于十六进制的 FizzBu​​zz
【发布时间】:2016-01-16 21:35:03
【问题描述】:

我相信你们中的大多数人应该熟悉 FizzBu​​zz 是什么。

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

编写一个打印从 1 到 100 的数字的程序。但是对于三的倍数打印“Fizz”而不是数字,对于五的倍数打印“Buzz”。对于同时是 3 和 5 的倍数的数字,打印“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 的?你会如何向一个完全的初学者解释这个?

【问题讨论】:

    标签: python fizzbuzz


    【解决方案1】:

    首先,我们观察到 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
    

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

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

    我们可以通过将v向右移动两位v &gt;&gt; 2,然后在另一端添加两位(v &gt;&gt; 2) | (j &lt;&lt; 28)来循环2*15 = 30位。 (这些是 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 替换为响应的显式队列 @9​​87654344@,而看起来很吓人的 v = v &gt;&gt; 2 | j &lt;&lt; 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))
    

    我们还可以添加单独的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))
    

    因为'' 是一个假值,所以(f[0] + b[0]) or i 将打印整数i,只要f[0]b[0] 都是空字符串。

    【讨论】:

    • 解释得很好!非常感谢。
    • 我已经用程序的一个变体更新了答案,以便进一步说明。圣诞快乐!
    • 很好的解释,最后一种方法也很有趣!对于那些想知道为什么n%3 = (n+15)%3 的人。是因为congruence property
    猜你喜欢
    • 1970-01-01
    • 2023-01-20
    • 1970-01-01
    • 2023-03-19
    • 2016-04-17
    • 2015-03-24
    • 2013-01-18
    • 2012-02-21
    • 2018-07-18
    相关资源
    最近更新 更多