Getting 39 bytes
This is an explanation of how I got a 39-byte solution, which Dennis and JonathanFrech found separately as well. Or, rather, it explains how one could arrive at the answer in hindsight, in a way that's much nicer than my actual path to it, which was full of muddy reasoning and dead ends.
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
Writing this a bit less golfed and with more parens, this looks like:
n=0
for _ in range(400):
print n
n=(n+2)^(-((n+2)^n))%3
Bit parities
We start with an idea from my 47-byte solution to output all numbers of the form n=2*k+b
where k
counts up 0,1,...,399
and b
is a parity bit that makes the overall number of 1's even.
Let's write par(x)
for the bit parity of x
, that is the xor (^
) all of bits in x
. This is 0 if there's an even number of 1-bits (number is evil), and 1 if there's an odd number of 1-bits. For n=2*k+b
, we have par(n) = par(k)^b
, so to achieve evil par(n)==0
we need b=par(k)
, i.e. the last bit of n
to be the bit parity of the preceding bits.
My first efforts at golfing were on expressing the par(k)
, at first directly with bin(k).count('1')%2
, and then with bit manipulation.
Parity updates
Still, there didn't seem to be a short expression. Instead, it helped to realize that there's more info to work with. Rather than just computing the bit parity of the current number,
k ----> par(k)
we can update the bit parity as we increment k
to k+1
.
k ----> par(k)
|
v
k+1 ----> par(k+1)
That is, since we're counting up k=0,1,2,...
, we merely need to maintain the current bit parity rather than calculating it from scratch each time. The bit parity update par(k+1)^par(k)
is the parity of the number of bits flipped in going from k
to k+1
, that is par((k+1)^k)
.
par(k+1) ^ par(k) = par((k+1)^k)
par(k+1) = par(k) ^ par((k+1)^k)
Form of (k+1)^k
Now we need to compute par((k+1)^k)
. It might seem like we've gotten nowhere because computing bit parity is exactly the problem we're trying to solve. But, numbers expressed as (k+1)^k
have the form 1,3,7,15,..
, that is one below a power of 2, a fact often used in bit hacks. Let's see why that is.
When we increment k
, the effect of the binary carries is to invert the last 0
and all 1
's to its right, creating a new leading 0
if there were none. For example, take k=43=0b101011
**
101011 (43)
+ 1
------
= 101100 (44)
101011 (43)
^101100 (44)
------
= 000111 (77)
The columns causing a carry are marked with *
. These have a 1
change to a 0
and pass on a carry bit of 1
, which keeps propagating left until it hits a 0
in k
, which changes to 1
. Any bits further to the left are unaffected. So, when k^(k+1)
checks which bit positions change k
to k+1
, it finds the positions of the rightmost 0
and the 1
's to its right. That is, the changed bits form a suffix, so result is 0's followed by one or more 1's. Without the leading zeroes, there are binary numbers 1, 11, 111, 1111, ...
that are one below a power of 2.
Computing par((k+1)^k)
Now that we understand that (k+1)^k
is limited to 1,3,7,15,...
, let's find a way to compute the bit parity of such numbers. Here, a useful fact is that 1,2,4,8,16,...
alternate modulo 3
between 1
and 2
, since 2==-1 mod 3
. So, taking 1,3,7,15,31,63...
modulo 3
gives 1,0,1,0,1,0...
, which are exactly their bit parities. Perfect!
So, we can do the update par(k+1) = par(k) ^ par((k+1)^k)
as
par(k+1) = par(k) ^ ((k+1)^k)%3
Using b
as the variable we're storing the parity in, this looks like
b^=((k+1)^k)%3
Writing the code
Putting this together in code, we start k
and the parity bit b
both at 0
, then repeatedly print n=2*k+b
and update b=b^((k+1)^k)%3
and k=k+1
.
46 bytes
k=b=0
exec"print 2*k+b;b^=(k+1^k)%3;k+=1;"*400
Try it online!
We removed parens around k+1
in ((k+1)^k)%3
because Python precedence does the addition first anyway, weird as it looks.
Code improvements
We can do better though by working directly with a single variable n=2*k+b
and performing the updates directly on it. Doing k+=1
corresponds to n+=2
. And, updating b^=(k+1^k)%3
corresponds to n^=(k+1^k)%3
. Here, k=n/2
before updating n
.
44 bytes
n=0
exec"print n;n^=(n/2+1^n/2)%3;n+=2;"*400
Try it online!
We can shorten n/2+1^n/2
(remember this is (n/2+1)^n/2
) by rewriting
n/2+1 ^ n/2
(n+2)/2 ^ n/2
(n+2 ^ n)/2
Since /2
removes the last bit, it doesn't matter if we do it before or after xor-ing. So, we have n^=(n+2^n)/2%3
. We can save another byte by noting that modulo 3
, /2
is equivalent to *2
is equivalent to -
, noting that n+2^n
is even so the division is actual halving without flooring. This gives n^=-(n+2^n)%3
41 bytes
n=0
exec"print n;n^=-(n+2^n)%3;n+=2;"*400
Try it online!
Finally, we can combine the operations n^=c;n+=2
into n=(n+2)^c
, where c
is a bit. This works because ^c
acts only on the last bit and +2
doesn't care about the last bit, so the operations commute. Again, precedence lets us omit parens and write n=n+2^c
.
39 bytes
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
Try it online!
2
It may also be worthy to note that this sequences is "Indices of zeros in the Thue-Morse sequence A010060." (source: oeis)
– Conor O'Brien – 2017-09-29T01:38:22.857