以下代码背后的逻辑是什么?

What is the logic behind the following code?

我在 codeforces.com 上找到了这段代码,但我对解决方案所涉及的逻辑感到困惑。 题: https://codeforces.com/contest/4/problem/A .其他用户的代码:

print("YNEOS"[2**int(input())%24<9::2])

但是我用了很简单的暴力破解方法。请彻底向我解释,因为我是初学者并且是自学成才的。谢谢。

这里的答案实际上更多是关于数学而不是关于 Python,但希望下面的解释对您有所帮助。

首先,澄清一些 Python 基础知识:

print("YNEOS"[0::2]) # "YES"
print("YNEOS"[1::2]) # "NO"

此设置是代码高尔夫中相当常见的技巧,其目标是使代码尽可能短(且不可读)。从这里您可以看到,您发布的代码只是选择 0 或 1 作为 "YNEOS" 字符串的起始索引,具体取决于输入。那么::2表示每隔一个字母获取一次。

其次,您可能已经知道这一点,但是 input() return 是一个字符串,因此我们必须使用 int(input()) 将其转换为整数。


现在进入问题,就是尝试把输入的数字w分成2份,都是偶数。两个偶数之和本身始终是偶数,这意味着要使 w 成为有效解,它必须是偶数。在编程中检查偶数的一种简单方法是检查数字 modulo 2 是否等于 0。但是,在我们的例子中,数字也必须大于 2,因为 2 不能拆分为两个 even-sized 一半。所以我们需要检查:

w % 2 == 0 and w > 2

稍加润色,在现实世界中,这就是问题的解决方案。我们不需要蛮力方法,因为问题不是问你哪些特定组合是可能的,只有 any 组合是可能的。现在我们需要更多东西的唯一原因是让代码更短。我们该怎么做?

深入挖掘

困难的部分是将 w > 2 与 even-odd 检查合并到一个语句中。尝试将 2 添加到 w,或将其乘以 2,或类似的东西,但这是行不通的,因为它只会移动输入数字的 even-odd 模式。 changew > 2.

之后,我们需要一些可测量的模式(仅使用一个操作)

事实证明,使用 2 的幂将给出我们的答案。如果对从 1 到 100 的每个数字计算 2 的 w 次方,您将得到 2, 4, 8, 16, 32, 64, ...。没有 special-looking。但回想一下取模运算——我们如何使用它在前 2、3、4 个数字之后在此序列中获得不同的答案?

请注意,序列中的每个数字在(包括)任意 n 之后都是 n 的倍数:2, 4, 8, 16 是 2 的倍数。4, 8, 16, 32 是4 的倍数。8, 16, 32, 64 是 8 的倍数,依此类推。所以为了我们的目的,我们可以 运行:

2**int(input()) % 8

对于每个输入 (w) 1-100,我们将得到 2, 4, 0, 0, 0, 0, 0, ...。进步!

但是现在因为我们序列中的每个数字都是 2 的幂,所以它们都是偶数。我们如何仅根据 2**w 检查 w 是否偶数?事实证明我们可以检查 2**w % 3。如果该数量等于 1,则 w 为偶数。否则,w 是奇数。 (这是一个有趣的结果。为什么每个 2 的偶数次幂都恰好比 3 的倍数大 1?)

本垒打

现在我们快到了。我们 可以 通过检查 ((2**w % 8) + (2**w % 3)) == 1 来合并我们的结果,但这非常低效并且绝对不会缩短我们的代码。我们可以将两个模数(8 和 3)相乘得到 24 并将其用作新模数。 (2**w % 24) 给出以下序列,从 w = 1 开始:

2, 4, 8, 16, 8, 16, 8, 16, 8, 16, ...

我们很亲近!我们可以看到w的偶数大于2的结果总是16。所以如果2**w % 24 == 16w是问题的解决方案,我们应该return “是的。”否则,我们应该return“NO”。我们可以相反地写:if 2**w % 24 != 16, return "NO", otherwise "YES"。从这里我们可以使用 2**w % 24 < 9 保存几个字符,这是等效的,因为序列中唯一不等于 16 的值小于 9。最后一个选项是您的代码所做的,请记住因为它有点令人困惑:如果 2**w % 24 < 9True,我们想要 return "NO."

最后一块理解 Python internally treats True 等于 1,False 等于 0。所以如果你 运行 "YNEOS"[True],例如,您将得到 N,索引为 1 的字符。这意味着如果不等式为 True"YNEOS"[2**int(input()) % 24 < 9::2] 将计算为 "YNEOS"[1::2] == "NO",只是就像我们想要的那样。否则它将计算为 "YNEOS"[0::2] == "YES".

好了。现在,如果我对此解释得足够好,你就会明白 print("YNEOS"[2**int(input())%24<9::2]) 到底是干什么的。

分手警告

这是一种可爱的解决方案,但效率不高。 Python 恰好非常擅长处理像 2**100 这样的大数字,但在其他语言中,这么大的数字很可能会破坏常规整数变量,甚至是 C++ 64 位 long long 变量。另外,即使它没有爆炸,发现对 24 取模的巨大数字在计算上也是昂贵的。在这些“代码高尔夫”比赛之外的任何地方,如果您试图使代码尽可能短,您都不想使用这样的代码