llhuiiはどのようにして42バイトのPythonで悪の数を出力しましたか?


71

これは、Anarchy GolfのEvil Numbersの質問に関するPythonでのゴルフのヒントの質問です。

バイナリ展開が1の偶数を持っている場合、数字はです。課題は、最初の400の悪の数字0,3,5,...,795,797,798を1行に1つずつ印刷することです。

Python 2の提出は、42バイトのソリューションを備えたllhuiiが主導しています。次に最適なのは、ミッチごとに46バイトで、その後に47バイトの送信が5回続きます。llhuiiは、2年以上にわたり多くの強力なPythonゴルファーを避けてきた本当に魔法のような何かを見つけたようです。このような短いゴルフでは、4バイトまたは5バイトを節約するのは非常に大きなことです。

Python 2スコアの表

私はまだ47バイトです。コミュニティとしてこのパズルを解くことを望んでいます。共同で回答が得られたら、貢献したすべての人の名前で提出します。この質問に対する答えは、コードの一部、新しいアイデア、または分析の一部です。llhuiiの場合は、まだ私たちのためにそれを台無しにしないでください。

この問題は無限であるため、提出は明らかにされていませんが、いくつかのリードが与えられています。受賞作品の実行には0.1699秒かかり、他のどの方法よりもはるかに長く、非効率的な方法を示唆しています。バイト統計から、42文字のうち、23は英数字で[0-9A-Za-z]、19はASCIIシンボルです。これは、llhuiiのソリューションに空白がないことを意味します。

問題のページでコードをテストし、言語ドロップダウンからPythonを選択するか、.pyファイルをアップロードできます。ご了承ください:

  • Python 2.7が使用されます
  • コードは印刷する完全なプログラムでなければなりません
  • ような、この問題に対する入力はありません
  • プログラムは、より大きな値で中断する場合でも、指定された400個の値を出力するだけです。
  • プログラムの実行には2秒かかります
  • プログラムがエラーで終了する場合があります
  • 次を使用できますexec。「execが拒否されました」はシェルexecを指します

2
また、このシーケンスは「Thue-MorseシーケンスA010060のゼロのインデックス」であることに注意する価値があります。(ソース:oeis
コナーオブライエン

回答:


51

これはllhuiiのソリューションと同じではありませんが、42バイト長です。

n=0;exec'print n;n^=(n^n+2)%3/2;n+=2;'*400

オンラインでお試しください!

@JonathanFrechのおかげで、40バイトになりました。

n=0;exec'print n;n=n+2^(n^n+2)/2%3;'*400

オンラインでお試しください!

保存するバイトがもう1つあり、合計で39です。

n=0;exec'print n;n=n+2^-(n^n+2)%3;'*400

オンラインでお試しください!


1
好奇心から、42バイトバージョンがllhuiiのバージョンと同じではないことをどのように知っていますか?(アナーキーゴルフに参加したことがない)
ルイスメンドー

6
@LuisMendo [ 統計 ]タブには、23個の英数字バイトと19個のASCIIシンボルがリストされているため、空白はありません。llhuiiが書いていない限りprint+n、彼らの解決策は私のものと異なっていなければなりません。
デニス

ああ、コードを知らなくても情報を取得できます。それはすばらしい。ありがとう!
ルイスメンドー

38の可能性があると思いますか?理論的には、潜在的にいくつかの自由度が削除がある-とシフトすることにより、符号をprint~nprint-nと使用し&たり~、私が仕事には何ももらっていないものの、。また、n=0;exec"print n;d=n^n+2;n^=d^-d%3;"*400かなりですが40バイトです。
xnor

print-nセットのビット間の簡単な関係が存在しないよう考えにくいnとは-nprint~n理論上はより有望に聞こえますが、このアプローチでは40バイト以下になりません。
デニス

28

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)

我々は増分として、我々はビットのパリティを更新することができますkk+1

k   ---->  par(k)
      |
      v
k+1 ---->  par(k+1)

つまり、カウントアップしているk=0,1,2,...ので、毎回ゼロから計算するのではなく、現在のビットパリティを維持するだけで済みます。ビットパリティ更新がpar(k+1)^par(k)から行くに反転ビット数のパリティであるkk+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ことが到達するまで左に伝播し続け、0kに変わり、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の間12、以来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)%3k=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+=2n=(n+2)^cに結合できcます。これが機能^cするのは、最後のビットだけに作用し、最後のビットを+2気にしないため、操作が通勤するからです。繰り返しますが、優先順位を使用すると、括弧を省略してを記述できますn=n+2^c

39バイト

n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400

オンラインでお試しください!


13

これにより、私の(xnorの)47バイトのソリューションと、それに導かれた思考が得られます。これを自分で理解したい場合は、これを読まないでください。

自然な最初のアイデアは、0から799までの数字を反復処理し、1の偶数の数字だけをバイナリで印刷することです。

52バイト

for n in range(800):
 if~bin(n).count('1')%2:print n

オンラインでお試しください!

ここでは、カウント~を切り替えeven<->oddて偶数カウントでのみ真実の値を与えるようにビット補数を取ります。

フィルタリングの代わりにすべての値を生成することにより、この方法を改善できます。出力値が0〜399の数字であり、それぞれに1ビットの数が偶数になるようにビットが追加されていることを確認します。

0 = 2*0 + 0
3 = 2*1 + 1
5 = 2*2 + 1
6 = 2*3 + 0
...

したがって、nth番号はまたはの2*n+bいずれb=0b=1です。ビットb1、ビットのnをカウントし、2を法とするカウントをとることで見つけることができます。

49バイト

for n in range(400):print 2*n+bin(n).count('1')%2

オンラインでお試しください!

私たちは、2つのバイトをカットすることができます2*反復処理で0,2,4,...ない偶然のカウントしている、1のを。これを行うには、exec400回実行されるループを使用し、ループnごとに2ずつ増やします。

47バイト

n=0;exec"print n+bin(n).count('1')%2;n+=2;"*400

オンラインでお試しください!

そして、それが私の47バイトのソリューションです。他のすべての47バイトソリューションが同じではないにしても、ほとんどの場合は疑っています。


1
47バイトの長さのexecアプローチは許可されていますか?
ジョナサンフレッチ

1
@JonathanFrechはい、ページに「execが拒否されました」と表示されている場合、それはPythonのexecコマンドラインではなく、コマンドラインのことexecです。
XNOR

9

llhuiiのPython 3提出

執筆時点でのEvil Numbersに対するPython 3の提出物は次のとおりです。

ここに画像の説明を入力してください

llhuiiはおそらくそのトリックをPython 3に移植し、次のような解決策を思い付きました。

  • Python 2ソリューションより3バイト長い
  • 45-(25 + 18)= 2バイトの空白があります。

xnorの47BをPython 3にそのまま移植すると、この50Bが得られます。

n=0;exec("print(n+bin(n).count('1')%2);n+=2;"*400)

として提出しましたppcg(xnor)。(現在、関数であるexecprintに括弧を追加します。)他のPython 3の回答とは異なるコードの統計情報があり、それらはすべてある程度の空白を含んでいます。面白い!

しかし、それを書き換えるより短い方法があります(execPython 3では競争力を失う傾向があります)。

n=0
while n<800:print(n+bin(n).count('1')%2);n+=2

49バイトです。として提出しましたppcg(xnor,alternative)。llhuiの答えのように、これには2バイトの空白があります!(改行、そしてこれがそのllhuiiのPythonの3の答えは次のようにちょっと見えると信じて私をリードwhile。ループ)ので、おそらく使用llhuii execのPython 2にし、whileちょうど私達と同じように、Pythonの3に。これは、空白の不一致を説明します。


私たちの47BはPython 3で49Bになりました。今興味深いのは、llhuiiの42Bが44Bにならず、45Bになったことです!llhuiiのソリューションに関する何かは、Python 3では1バイト余分に必要です。これは、さまざまなことを意味する可能性があります。

  • 最初に頭に浮かぶのは除算です:llhuiiは/Python 2で使用され、それが//Python 3になったn/2可能性があります(もし私たちのように2人で数えている場合はn、1つ右にシフトするために使用されますか?)

  • もう1つ頭に浮かぶのは、印刷後の単項演算子です。私たちprint blahprint(blah)(1バイト余分に)なりましたが、llhuiiがprint~-blahPython 2のようなものを書いた場合print(~-blah)、Python 3になります。

  • 他のアイデアがあるかもしれません。私にお知らせください。

現在の鉱山を含むすべてのPy3ソリューションのコード統計:

ここに画像の説明を入力してください


1
興味深いのは、Python 3ソリューションがPython 2ソリューションよりも大幅に高速であることです。彼らはPython 3でより効率的になったPython機能を使用しているか、結局は単純なポートではありません(直接ポートよりも短いPython 3ソリューションを見つけたかもしれません)。
ジョナサンフレッチ

2
anagol上のランタイムを持っている巨大な分散を、私はここllhuiiのランタイムは、私は彼らのPY2ランタイムがちょうど赤いニシン/外れ値であることを思わせるOPでコメント
リン

また、xnorが非常によく似たトリックを見つけて改善していると思います(邪悪な数字を印刷する方法はそんなに多くないでしょう?!)。
リン

7

その他のアプローチ

1)A001969の式を使用する

バイナリに変換するのではなく、次の式(OEISから)を利用できる場合があります。

a(1) = 0
for n > 1: a(n) = 3*n-3-a(n/2) if n is even
           a(n) = a((n+1)/2)+n-1 if n is odd

私はPythonでのゴルフが非常に苦手なので、試してみるつもりもありません。ただし、JSでの簡単な試みです。

NB:配列を表示せずに入力するだけなので、有効なJS提出とは思いません。それでも、現在の最高のJSソリューション(45バイト)よりも5バイト長くなっています。とにかく、それはここのポイントではありません。

for(a=[n=0,3];n<199;)a.push(2*++n+a[n],6*n+3-a[n])

うまくいけば、それがインスピレーションを与えるかもしれません。

配列を使用することは、初期化および更新する必要があるため、おそらく良い考えではありません。代わりに再帰関数を使用する方が効率的(コードサイズの面で)である可能性があります。これは、勝利ソリューションが他のソリューションよりも時間かかる理由を説明します。

2)置換を伴うThue-Morseシーケンスの構築

理論的には、このコードは機能するはずです。

n=0;a="1";b="0";exec"t=a;a+=b;b+=t;print(int(b[n]))+n;n+=2;"*400

オンラインでお試しください!(20期間に制限された実行可能バージョン)

連続置換でThue-Morseシーケンスを計算し、同じループ内で1の位置(Evil Numbers)を探します。

しかし:

  • 現在の形式では長すぎます
  • それはすぐにメモリオーバーフローにつながります

3)ビット演算を使用したThue-Morseシーケンスの構築

ウィキペディアのThue-Morseシーケンス直接定義から始めて、私はこのアルゴリズムに来ました(JSに戻って...申し訳ありません):

for(e=n=0;n<799;)(e^=!(((x=n++^n)^x/2)&170))||console.log(n)

eのシーケンスの現在の悪さを追跡し、170をバイトの奇数ビットのビットマスクとして使用します。


私は再帰関数のアイデアが好きですが、Pythonは定型的なものには非常に悪いf=lambda n:_ for n in range(400):print f(n)です。すでに43バイトかかります。おそらく、自分自身を参照する配列、または将来の要素を最後に追加する配列を作成することにより、再帰をシミュレートする方法があるかもしれません。
XNOR

2
また、llhuiiのソリューションは、彼が使用していなかったので、それにはスペースを持っていないdefforwhilelambda(パラメータを使用して、少なくとも)、など
スティーブン

@Stephenのようなものwhile~0:print~1はスペースを必要としません。
ジョナサンフレッチ

方法番号3では((x=n++^n)^x/2)、最低の設定ビットを見つけるためだけに冗長に見えます。その混乱全体をに置き換えることができます++n&-nオンラインでお試しください!
primo

@primoここで私が何を考えていたのか、この厄介な式にどのようになったのか分かりません。¯\ _(ツ)_ /¯
アルノー

5

ネストされたカウンターのアプローチ

別のアプローチのアイデアはありますが、Pythonゴルフの経験が足りないので、ゴルフの別の可能な出発点として検討するためにここに残しておきます。

求められていないアイデア:

n=0
i=1
for _ in"01":
 i^=1
 for _ in"01":
  i^=1
  for _ in"01":
   i^=1
   for _ in"01":
    i^=1
    for _ in"01":
     i^=1
     for _ in"01":
      i^=1
      for _ in"01":
       i^=1
       for _ in"01":
        i^=1
        for _ in"01":
          i^=1
          if n<800:print i+n
          n+=2

オンラインでお試しください!

9レベルのネストの深さ、すべてのループは同じであるため、私の考えでは、によって構築される必要がありますexec"something"*9+"deepest stuff"。実際には、forサイクルでこのようなことを実行できるかどうかはわかりません。

ゴルフで考慮すべきこと:

  • forループ以外に2回循環する可能性があるかもしれません(実行する文字列をフォーマット引数として2回渡してクインのようなアプローチを試みましたが、頭が爆発しました)。

  • に代わるより良い代替手段もありif n<800:ます。そうしないと、最大2 ^ 10までの悪の数字を出力し続けるため、ここで必要になります。



ネストされたforループの代わりに、ネストされたリストの内包表記を試してください。
スパー

@Sparr問題は、実際に数字を印刷することです。Python 2ではprint、関数ではなくステートメントであるため、理解内に表示できません。
ジョナサンフレッチ

多分print '\n'.join([[[[[[[[[foo]foo]foo]foo]foo]foo]foo]foo]foo])
Sparr

@Sparrそして、問題はリストを平坦化することにあります。str.join文字列を含むリストでのみ機能し、余分なリスト文字は印刷しないでください。フォーマットだけでは、かなりのバイトが必要になります。
ジョナサンフレッチ

5

アイデア:より短いビットパリティ

bin(n).count('1')%2ビットカウントのパリティを計算するには、多くの文字が必要です。特にビット長が制限されている場合、算術演算の方が短いかもしれません。

同じ長さのかわいい方法はint(bin(n)[2:],3)%2、バイナリ値をベース3(または任意の奇数ベース)として解釈することです。残念ながら、4バイトが0bプレフィックスの削除に費やしています。それもうまくいきますint(bin(n)[2:])%9%2

もう1つのアイデアは、xorを使用してビットを結合することです。場合は、nバイナリ表現がありabcdefghi、その後、

n/16 = abcde
n%16 =  fghi

r = n/16 ^ n%16 has binary representation (a)(b^f)(c^g)(d^h)(e^i)

ですから、r=n/16^n%16悪である場合にのみn悪です。私たちは、その後、そのように繰り返すことができs=r/4^r%4、値sでは0,1,2,3、そのうちの12でチェック可能、悪ではありません0<s<3

52バイト

n=0;exec"r=n/16^n%16;print(0<r/4^r%4<3)+n;n+=2;"*400

オンラインでお試しください!

これはかなり長い間判明しました。番号を分割する方法、最終的な番号を確認する方法(ビットベースのルックアップテーブルなど)については、多くのノブを回す必要があります。しかし、これらはこれまでのところしかできないと思います。


to_bytes整数の関数を使用する可能性はありますか?私はそれを疑いますが、考慮すべきこと:)
HyperNeutrino

@HyperNeutrino私はそれがPython 3だけだと思う​​?
xnor

うん私の悪い:/ RIP
HyperNeutrino

9
単に使用してください0bint(bin(n),13)%2!:D
Noodle9

3
進捗!Noodle9のトリックは44バイトのソリューションを提供しています:n=0;exec"print~int(bin(n),13)%2+n;n+=2;"*400
リン・

4

構造上、n+n^n常に悪意がありますが、私のPythonの貧弱なスキルは61バイトのソリューションを思い付くだけでした。

for n in sorted(map(lambda n:n+n^n,range(512)))[:400]:print n

5バイトを保存してくれた@Peilonrayzと1バイトを保存してくれた@ Mr.Xcoderに感謝します。

for n in sorted(n^n*2for n in range(512))[:400]:print n

55バイトfor n in sorted(n^n*2for n in range(512))[:400]:print nn+n^n同じであるn^n*2
氏Xcoder

3

アイデア:A006068(「a(n)はnにグレーコード化されています」)

ニールのすべてのソートのアイデアに2n XOR n興味があったので、このソートの背後にあるインデックスを見つけようとしました。このコードを書いたところ、次のようなことが書けることがわかりました。

for n in range(400):x=a(n);print 2*x^x

a(n)A006068(n)はどこですか。オンラインでお試しください!

ただし、これは、A006068を計算するいくつかの短い方法があることを前提としています。4バイト(a(n)部分)で計算できると仮定すると、これはすでに38バイトです。実際の実装(TIOヘッダー内)は、それよりもはるかに長くなります。これに対する希望はあまりないと思います。


3

アイデア:XORを削減

あなたはすべてのビットXOR場合はn一緒に、それは次のようになります0悪のためにと1非悪のために。次のように、再帰関数を使用してこれを行うことができます(これにより時間がかかりますか?)。

f=lambda n:f(n/2^n&1)if n>1else-~-n

これは悪の場合1を返します。

これは35バイトで、数値が悪かどうかをチェックします。残念ながら、filterすでに6バイトであるため、これは逐語的に最適なソリューションではありませんでしたが、この考えはおそらくゴルフに当てはまります。


f=lambda n:n>1and f(n/2^n&1)or-~-n-1バイトでできると思います。
エリックアウトゴルファー

@EriktheOutgolfer私が試したが、それはエラーの原因f(n/2^n&1)を返します0 ...
HyperNeutrino

2

置換方法:{1-> {1、-1}、-1-> {-1、1}}

この置換を10回{1-> {1、-1}、-1-> {-1、1}}行ってから、1の位置を平坦化して確認することもできます。

ここに数学のコードがあります

(F = Flatten)@
Position[F@Nest[#/.{1->{1,-1},-1->{-1,1}}&,1,10],1][[;; 400]] - 1

Pythonでこれをどのように行いますか?
アニーシュダーグ

2
@AneeshDurgこのソリューションに興味深い点はありますか?箱から出して考えると、人生の意味への道を見つけるかもしれません42
J42161217
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.