39バイトの取得
これは、デニスとジョナサンフレッチが別々に見つけた39バイトのソリューションをどのように手に入れたかの説明です。むしろ、それは、泥だらけの推論と行き止まりに満ちていた私の実際の道よりもはるかに良い方法で、後知恵で答えに到達する方法を説明しています。
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
これを少しゴルフを減らし、より多くの括弧で書くと、これは次のようになります:
n=0
for _ in range(400):
print n
n=(n+2)^(-((n+2)^n))%3
ビットパリティ
私たちは、私のアイデアを開始する47バイトのソリューション出力形式のすべての数字をカウントアップし、1のさえの全体的な数になりますパリティビットをです。n=2*k+bk0,1,...,399b
さんが書いてみましょうpar(x)ためにビットパリティのxXOR(で、^)のビットのすべてを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の累乗より1つ小さい数で、ビットハックでよく使用される事実です。その理由を見てみましょう。
をインクリメントkすると、バイナリキャリーの効果は、最後0とすべてを1右に反転し、0ない場合は新しいリーディングを作成します。たとえば、k=43=0b101011
**
101011 (43)
+ 1
------
= 101100 (44)
101011 (43)
^101100 (44)
------
= 000111 (77)
桁上げの原因となっている列にはが付いてい*ます。これらは持っている1の変更を0とのキャリービットを渡す1ことが到達するまで左に伝播し続け、0中kに変わり、1。左側のビットは影響を受けません。したがって、k^(k+1)どのビット位置がに変化kするかをチェックするk+1と、右端0と右端のの位置が見つかり1ます。つまり、変更されたビットはサフィックスを形成するため、結果は0の後に1つ以上の1が続きます。先行ゼロがない場合、1, 11, 111, 1111, ...2の累乗より1つ少ない2進数があります。
コンピューティング par((k+1)^k)
これ(k+1)^kがに限定されていることがわかったので1,3,7,15,...、そのような数値のビットパリティを計算する方法を見つけましょう。ここでは、便利な事実はある1,2,4,8,16,...代替法と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
オンラインでお試しください!
とにかく、Pythonの優先順位が最初に追加を行うため、見た目が奇妙なので、周りの括弧を削除しk+1まし((k+1)^k)%3た。
コードの改善
ただし、単一の変数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最後のビットを削除するので、xor-ingの前でも後でも問題ありません。だから、私たちは持ってい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気にしないため、操作が通勤するからです。繰り返しますが、優先順位を使用すると、括弧を省略してを記述できますn=n+2^c。
39バイト
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
オンラインでお試しください!