获取39个字节
这说明了我如何获得39字节的解决方案,Dennis和JonathanFrech也分别找到了该解决方案。或者,相反,它以一种比我实际的方法更好的方式解释了事后看来如何得出答案,因为我的实际方法充满了混乱的推理和死胡同。
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
少写些高尔夫球,增加更多paren,这看起来像:
n=0
for _ in range(400):
print n
n=(n+2)^(-((n+2)^n))%3
位奇偶校验
我们从我的47字节解决方案中得出一个想法开始,输出所有形式的所有数字,n=2*k+b其中的数字是k递增计数的,0,1,...,399并且b是一个奇偶校验位,该总数使1的总数为偶数。
让我们写par(x)对位奇偶校验的x,那就是XOR( ^)都在位x。如果偶数个1位(数字是邪恶的),则为0;如果奇数个1位,则为1。对于n=2*k+b,我们有par(n) = par(k)^b,因此要实现邪恶,par(n)==0我们需要b=par(k),即的最后一位是n前几位的位奇偶校验。
我在打高尔夫球第一的努力都在表达par(k),首先直接用bin(k).count('1')%2,然后用位操作。
奇偶校验更新
不过,似乎没有一个简短的表达。相反,它有助于意识到还有更多信息可以使用。不仅仅是计算当前数字的位奇偶校验,
k ----> par(k)
当我们k增加到时,我们可以更新位奇偶校验k+1。
k ----> par(k)
|
v
k+1 ----> par(k+1)
也就是说,由于我们要进行递增计数k=0,1,2,...,因此我们仅需要维护当前的位奇偶校验,而不必每次都从头开始进行计算。位奇偶校验更新par(k+1)^par(k)是从k到翻转的位数的奇偶校验k+1,即par((k+1)^k)。
par(k+1) ^ par(k) = par((k+1)^k)
par(k+1) = par(k) ^ par((k+1)^k)
形式 (k+1)^k
现在我们需要计算par((k+1)^k)。似乎我们一无所获,因为计算位奇偶校验正是我们要解决的问题。但是,数字表示为(k+1)^k形式1,3,7,15,..,即小于2的幂的形式,这是位黑客经常使用的事实。让我们看看为什么。
当我们增加时k,二进制进位的作用是将最后一个0和所有的都反1转到它的右边,0如果没有,则创建一个新的前导。例如拿k=43=0b101011
**
101011 (43)
+ 1
------
= 101100 (44)
101011 (43)
^101100 (44)
------
= 000111 (77)
引起进位的列用标记*。它们1改变为a 0并传递一个进位位1,继续向左传播直到撞到一个0in 为止k,改变为1。左侧的任何位均不受影响。因此,当k^(k+1)检查哪个位的位置变为k时k+1,它会找到最右边的位置0和1右边的。也就是说,更改的位形成后缀,因此结果为0,后跟一个或多个1。如果没有前导零,则存在二进制数1, 11, 111, 1111, ...,该二进制数比2的幂小。
电脑运算 par((k+1)^k)
现在我们了解到(k+1)^k仅限于1,3,7,15,...,让我们找到一种计算此类数字的位奇偶校验的方法。在这里,一个有用的事实是和之间的1,2,4,8,16,...交替模,因为。因此,取模gives 恰好是它们的位奇偶校验。完善!3122==-1 mod 31,3,7,15,31,63...31,0,1,0,1,0...
因此,我们可以做的更新par(k+1) = par(k) ^ par((k+1)^k)为
par(k+1) = par(k) ^ ((k+1)^k)%3
使用b作为存储奇偶校验的变量,这看起来像
b^=((k+1)^k)%3
编写代码
在代码放在一起,我们开始k和校验位b都在0,然后反复打印n=2*k+b和更新b=b^((k+1)^k)%3以及k=k+1。
46个字节
k=b=0
exec"print 2*k+b;b^=(k+1^k)%3;k+=1;"*400
在线尝试!
我们删除了周围k+1的((k+1)^k)%3原因,因为Python优先级还是会先进行添加,看起来很奇怪。
代码改进
通过直接使用单个变量n=2*k+b并直接对其进行更新,我们可以做得更好。这样做k+=1对应n+=2。并且,更新b^=(k+1^k)%3对应于n^=(k+1^k)%3。在这里,k=n/2在更新之前n。
44个字节
n=0
exec"print n;n^=(n/2+1^n/2)%3;n+=2;"*400
在线尝试!
我们可以通过重写来缩短n/2+1^n/2(请记住这是(n/2+1)^n/2)
n/2+1 ^ n/2
(n+2)/2 ^ n/2
(n+2 ^ n)/2
由于/2删除了最后一位,所以我们在异或之前还是之后都没有关系。因此,我们有n^=(n+2^n)/2%3。我们可以注意到,模拯救另一个字节3,/2相当于*2相当于-,并指出n+2^n是即使如此划分是不实际的地板减半。这给n^=-(n+2^n)%3
41个字节
n=0
exec"print n;n^=-(n+2^n)%3;n+=2;"*400
在线尝试!
最后,我们可以将操作n^=c;n+=2合并到中n=(n+2)^c,c有点。之所以有效,是因为^c仅作用于最后一位,+2而不关心最后一位,因此操作上下班。同样,优先级让我们省略parens和write n=n+2^c。
39个字节
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
在线尝试!