以下代码背后的逻辑是什么?
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 模式。 change 在 w > 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 == 16
,w
是问题的解决方案,我们应该return “是的。”否则,我们应该return“NO”。我们可以相反地写:if 2**w % 24 != 16
, return "NO", otherwise "YES"。从这里我们可以使用 2**w % 24 < 9
保存几个字符,这是等效的,因为序列中唯一不等于 16 的值小于 9。最后一个选项是您的代码所做的,请记住因为它有点令人困惑:如果 2**w % 24 < 9
是 True
,我们想要 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 取模的巨大数字在计算上也是昂贵的。在这些“代码高尔夫”比赛之外的任何地方,如果您试图使代码尽可能短,您都不想使用这样的代码
我在 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 模式。 change 在 w > 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 == 16
,w
是问题的解决方案,我们应该return “是的。”否则,我们应该return“NO”。我们可以相反地写:if 2**w % 24 != 16
, return "NO", otherwise "YES"。从这里我们可以使用 2**w % 24 < 9
保存几个字符,这是等效的,因为序列中唯一不等于 16 的值小于 9。最后一个选项是您的代码所做的,请记住因为它有点令人困惑:如果 2**w % 24 < 9
是 True
,我们想要 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 取模的巨大数字在计算上也是昂贵的。在这些“代码高尔夫”比赛之外的任何地方,如果您试图使代码尽可能短,您都不想使用这样的代码