获取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
,继续向左传播直到撞到一个0
in 为止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 恰好是它们的位奇偶校验。完善!3
1
2
2==-1 mod 3
1,3,7,15,31,63...
3
1,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
在线尝试!