核分裂、1328 989 887 797バイト
この回答は少し不当に長いです(折りたたみ可能な領域があればいいのですが)。これをスクロールして、他の回答に愛を示すことを忘れないでください!
このコードに取り組んでいることが、この挑戦のきっかけとなりました。EOEISにFissionで答えを追加したかったので、このシーケンスに至りました。ただし、実際にFissionを学習し、これを実装するには、数週間、Fissionのオンとオフを切り替える必要がありました。その間、シーケンスは本当に成長していたので、別のチャレンジを投稿することにしました(さらに、これはEOEISのツリーのそれほど遠くにあるわけではありませんでした)。
だから私はあなたに、怪物を紹介します:
R'0@+\
/ Y@</ /[@ Y]_L
[? % \ / \ J
\$@ [Z/;[{+++++++++L
UR+++++++++>/;
9\ ; 7A9
SQS {+L /$ \/\/\/\/\/ 5/ @ [~ &@[S\/ \ D /8/
~4X /A@[ %5 /; & K } [S//~KSA /
3 \ A$@S S\/ \/\/\/ \/>\ /S]@A / \ { +X
W7 X X /> \ +\ A\ / \ /6~@/ \/
/ ~A\; +;\ /@
ZX [K / {/ / @ @ } \ X @
\AS </ \V / }SZS S/
X ;;@\ /;X /> \ ; X X
; \@+ >/ }$S SZS\+; //\V
/ \\ /\; X X @ @ \~K{
\0X / /~/V\V / 0W//
\ Z [K \ //\
W /MJ $$\\ /\7\A /;7/\/ /
4}K~@\ &] @\ 3/\
/ \{ }$A/1 2 }Y~K <\
[{/\ ;@\@ / \@<+@^ 1;}++@S68
@\ <\ 2 ; \ /
$ ;}++ +++++++L
%@A{/
M \@+>/
~ @
SNR'0YK
\ A!/
入力には末尾の改行がないことが予想されるため、次のように呼び出すことができecho -n 120 | ./Fission oeis256504.fisます。
レイアウトはおそらくさらに効率的である可能性があるため、ここにはまだ改善の余地があると思います(たとえば、911 581 461 374スペースが含まれます)。
説明に入る前に、これをテストする際の注意:公式の通訳は完全に機能しません。a)Mirror.cpp多くのシステムでコンパイルしません。この問題が発生した場合は、問題のある行をコメントアウトしてください。影響を受けるコンポーネント(ランダムミラー)はこのコードでは使用されません。b)未定義の動作につながる可能性のあるバグがいくつかあります(プログラムではこのような複雑な動作になる可能性があります)。このパッチを適用して修正することができます。それができたら、次のようにしてインタープリターをコンパイルできるはずです。
g++ -g --std=c++11 *.cpp -o Fission
楽しい事実:このプログラムは、#(ランダムミラー)、:(ハーフミラー)、-または|(プレーンミラー)、および"(印刷モード)を除く、Fissionが提供するほぼすべてのコンポーネントを使用します。
一体全体?
警告:これはかなり長くなります...核分裂がどのように機能し、どのようにその中にプログラムできるかについてあなたが本当に興味を持っていると思います。あなたがそうでないなら、私はおそらくこれをどのように要約できるかわからないからです。(ただし、次の段落では、言語の一般的な説明を示します。)
Fissionは2次元のプログラミング言語で、データと制御フローの両方がグリッドを移動する原子によって表されます。Marbelousを以前に見たり使用したことがあるなら、その概念は漠然と馴染んでいるはずです。各原子には、2つの整数プロパティがあります。非負の質量と任意のエネルギーです。質量が負になると、原子はグリッドから削除されます。ほとんどの場合、質量を原子の「値」として、エネルギーを原子の流れを決定するためにいくつかのコンポーネントによって使用される何らかのメタプロパティとして扱うことができます(つまり、ほとんどの種類のスイッチはエネルギー)。(m,E)必要に応じて、で原子を示します。プログラムの開始時に、グリッドは次の束で始まります(1,0)4つのコンポーネントのどこからでも配置できる原子UDLR(文字は最初に原子が移動する方向を示します)。ボードには、原子の質量とエネルギーを変更したり、方向を変更したり、他のより洗練された処理を行うコンポーネント全体が配置されます。完全なリストについては、esolangsページを参照してください。ただし、この説明ではそれらのほとんどを紹介します。もう1つの重要な点(プログラムは数回使用します)は、グリッドがトロイダルであるということです。いずれかの側面にぶつかった原子が反対側に再び現れ、同じ方向に移動します。
プログラムをいくつかの小さな部分に分けて作成し、最後にそれらを組み立てたので、説明を続けます。
atoi
このコンポーネントはかなり面白くないように見えるかもしれませんが、素晴らしくシンプルであり、Fissionの算術および制御フローの多くの重要な概念を紹介することができます。したがって、この部分を非常に詳細に説明するので、他の部分を減らして新しい核分裂メカニクスを導入し、詳細な制御フローをフォローできる高レベルのコンポーネントを指摘することができます。
Fissionは、個々の文字からバイト値を読み取ることはできますが、数字全体は読み取れません。この辺りでそれは許容できる慣行ですが、私はそれをしている間、私はそれを正しく行い、STDINで実際の整数を解析できると考えました。ここにあるatoiコードは:
;
R'0@+\
/ Y@</ /[@ Y]_L
[? % \ / \ J
\$@ [Z/;[{+++++++++L
UR+++++++++>/;
O
核分裂で最も重要な2つのコンポーネントは、核分裂および核融合炉です。核分裂炉はのいずれかであるV^<>(上記のコードの使用<と>)。核分裂炉は原子を(キャラクターのウェッジに送信することで)保存できます(2,0)。デフォルトはです。原子がキャラクターの頂点に当たると、2つの新しい原子が側面に送られます。それらの質量は、入ってくる質量を保存された質量で割ることによって決定されます(つまり、デフォルトで半分になります)-左向きの原子はこの値を取得し、右向きの原子は質量の残りを取得します(つまり、質量は核分裂で保存されます) 。出て行く原子は両方とも入ってくるエネルギーがマイナスになります蓄積されたエネルギー。これは、減算と除算の両方に、核分裂炉を算術に使用できることを意味します。核分裂炉がサイトからヒットすると、原子は単純に斜めに反射され、キャラクターの頂点の方向に移動します。
核融合炉は、のいずれかであるYA{}(上記のコードの使用Yと{)。それらの機能は似ています:アトムを格納でき(デフォルト(1,0))、頂点からヒットすると2つの新しいアトムが側面に送られます。ただし、この場合、2つの原子は同一であり、常に入ってくるエネルギーを保持し、入ってくる質量に保存された質量を掛けます。つまり、デフォルトでは、核融合炉は頂点にぶつかる原子を単純に複製します。側面から衝突すると、核融合炉はもう少し複雑になります。原子もアトムが反対側にヒットするまで(他のメモリとは独立して)保存されます。それが起こると、質量とエネルギーが2つの古い原子の合計である頂点の方向に新しい原子が放出されます。一致するアトムが反対側に到達する前に新しいアトムが同じ側にヒットした場合、古いアトムは単に上書きされます。核融合炉を使用して、加算と乗算を実装できます。
邪魔にならないもう1つの単純なコンポーネントは[、](着信方向に関係なく)原子の方向をそれぞれ右と左に設定するだけです。垂直に相当するのはM(down)とW(up)ですが、atoiコードには使用されません。UDLRまたWM][、初期原子を解放した後の役割も果たします。
とにかく、そこのコードを見てみましょう。プログラムは5つのアトムから始まります。
RそしてL底部には、単に(とそれらの質量の増加を得る+)となるように(10,0)した後、それぞれ、核分裂と核融合炉に格納されています。これらのリアクターを使用して、10進数の入力を解析します。
L右上隅には、その質量が(とデクリメント取得_になること)(0,0)や核融合炉の側に格納されますY。これは、読んでいる数字を追跡するためです-数字を読むにつれて、これを徐々に増やしていきます。
R左上隅には、の文字コードにその質量のセットを取得する0と(48)、'0その後、質量とエネルギーがと交換され、@そして最終的には質量で一度インクリメント+与えます(1,48)。次に、それを対角線ミラーでリダイレクトされる\と、/核分裂反応炉に格納します。48for減算を使用して、ASCII入力を数字の実際の値に変換します。また、1による分割を回避するために質量を増やす必要がありました0。
- 最後に、
U左下隅は実際にすべてを動作させるものであり、最初は制御フローにのみ使用されます。
右にリダイレクトされた後、コントロールアトムはをヒットし?ます。これは入力コンポーネントです。文字を読み取り、アトムの質量を読み取りASCII値に設定し、エネルギーをに設定し0ます。代わりにEOFをヒットすると、エネルギーはに設定され1ます。
アトムは継続し、ヒットし%ます。これはミラースイッチです。正ではないエネルギーの場合、これは/鏡のように機能します。しかし、正のエネルギーに対しては、aのように作用し\ます(また、エネルギーを1減らします)。したがって、文字を読んでいる間、アトムは上方に反映され、文字を処理できます。ただし、入力が完了すると、アトムは下方に反映され、異なるロジックを適用して結果を取得できます。参考までに、反対のコンポーネントは&です。
そのため、今のところアトムが上に移動しています。各文字に対して行うことは、その桁の値を読み取り、それを現在の合計に加算し、その現在の合計に10を掛けて次の桁の準備をすることです。
文字アトムは最初に(デフォルトの)融合原子炉に衝突しますY。これにより、アトムが分割され、左方向のコピーがコントロールアトムとして使用され、入力コンポーネントにループバックして次の文字が読み込まれます。適切なコピーが処理されます。文字を読んだ場合を考えてください3。私たちの原子はになります(51,0)。@次の核分裂炉の減算を利用できるように、質量とエネルギーをに交換します。リアクトル48はエネルギーを差し引きます(質量を変更することなく)ので、2つのコピーを送ります(0,3)-エネルギーは今読んだ数字に対応します。次のコピーは単に破棄されます;(入ってくるすべての原子を破壊するだけのコンポーネント)。下りコピーで作業を続けます。あなたはそのパスをたどる必要があります/そして\ミラービット。
@ただ、再び核融合炉スワップ質量とエネルギーの前に、我々は追加しますよう(3,0)に私たちの現在の合計にY。したがって、積算合計自体には常に0エネルギーがあることに注意してください。
今J、ジャンプです。それがすることは、入ってくる原子をそのエネルギーで前方にジャンプします。の場合0、アトムはまっすぐに動き続けます。ある場合1は1つのセルをスキップし、ある場合2は2つのセルをスキップします。エネルギーはジャンプに費やされるため、原子は常にエネルギーになり0ます。積算合計のエネルギーはゼロなので、ジャンプは今のところ無視され、原子は核融合炉にリダイレクトされ、{質量がで乗算され10ます。下降コピーは破棄され;、上昇コピーはY新しい実行合計としてリアクターにフィードバックされます。
上記は、EOFに到達するまで繰り返されます(前の数字が処理される前に新しい数字が処理される面白いパイプライン方式で)。これで、%アトムが下方に送信されます。考えは(0,1)、a)合計が影響を受けないように(ゼロ質量)、b)を1飛び越えるエネルギーを得るように、実行中の合計リアクターにヒットする前にこの原子を今に変えること[です。でエネルギーを簡単に処理できます$。これにより、エネルギーが増加します。
問題は、?EOFを押しているときに質量がリセットされないため、質量は最後に読み込まれた文字の質量のままであり、エネルギーは0(バックに%減少する1ため0)になることです。そのため、その質量を取り除きたいです。そのためには、質量とエネルギーを@再び交換します。
このセクションを完了する前に、もう1つのコンポーネントを紹介する必要がありますZ。これは本質的に同じです%か&。違いは、正のエネルギーの原子を(エネルギーを減少させながら)まっすぐに通過させ、非正のエネルギーの原子を左に90度偏向させることです。これを使用して、原子をZ何度もループしてループさせることで、原子のエネルギーを除去できます。エネルギーがなくなるとすぐに原子は偏向され、ループを離れます。それがこのパターンです:
/ \
[Z/
エネルギーがゼロになると、原子は上方に移動します。プログラムの他の部分で、このパターンを何らかの形で何度か使用します。
原子はこの小さなループを離れるときだから、それは次のようになります(1,0)とにスワップ(0,1)による@入力の最終結果を解放するために核融合炉を打つ前に。ただし、既に別の数字に仮に掛け合わせているため、現在の合計は10倍になります。
エネルギーがある1ので、このアトムはスキップし[て飛び込み/ます。これにより、核分裂炉に偏向され、10で除算して外部の乗算を修正する準備ができました。繰り返しますが、半分を破棄し;、もう一方を出力として保持します(ここOでは、対応する文字を出力し、アトムを破棄します-完全なプログラムでは、代わりにアトムを使用します)。
itoa
/ \
input -> [{/\ ;@
@\ <\
$ ;}++ +++++++L
%@A{/
M \@+>/
~ @
SNR'0YK
\ A!/
もちろん、結果を文字列に変換して出力する必要もあります。それがこのパートの目的です。これは、入力がティック10かそこらの前に到着するのではなく、簡単に与えられる完全なプログラムで到着することを前提としています。このビットは、プログラム全体の下部にあります。
このコードは、新しい非常に強力なFissionコンポーネントであるstackを導入していますK。スタックは最初は空です。負でないエネルギーを持つ原子がスタックにヒットすると、その原子は単にスタックにプッシュされます。負のエネルギーを持つ原子がスタックに衝突すると、その質量とエネルギーはスタックの上部の原子に置き換えられます(これによりポップされます)。ただし、スタックが空の場合、原子の方向が逆になり、そのエネルギーが正になります(つまりに乗算されます-1)。
さて、実際のコードに戻ります。itoaスニペットの考え方は、次の反復のために入力を10で整数で除算しながら、10を法とする入力を繰り返して次の桁を見つけることです。これにより、すべての数字が逆順(最下位から最上位まで)で生成されます。順序を修正するには、すべての数字をスタックにプッシュし、最後にそれらを1つずつポップアウトして印刷します。
コードの上半分は、桁計算を行います。L私たちは分割することができ、乗算10によりループが本質的に後に始まるようにプラスとは、我々はクローン10およびフィード分裂へと融合炉を与える[左上に。現在の値は分割されます。1つのコピーは10で除算され、10で乗算されて核分裂炉に保存され、頂点で他のコピーがヒットします。これはi % 10として計算されi - ((i/10) * 10)ます。また、次の反復にAフィードできるように、除算後、乗算前に中間結果が分割されることに注意してくださいi / 10。
%これは、多かれ少なかれ印刷用行う-whileループ、このコードはだろうでも作業であるので、反復変数が0に当たったらループを中断0(あるいは先行ゼロを作成せず)。ループを抜けたら、スタックを空にして数字を出力します。Sはの反対なZので、正のエネルギーを持たない入射原子を右に90度偏向するスイッチです。そのため、原子は実際にS直線から端まで移動Kして数字を飛び出します(~これにより、入ってくる原子にエネルギーが与えられることに注意してください-1)。その数字は48、対応する数字のASCIIコードを取得するために増分されます。Aと一つのコピーを印刷するには数字を分割します!Y次の桁のために、もう一方のコピーをリアクターにフィードバックします。印刷されたコピーは、スタックの次のトリガーとして使用されます(ミラーMは、左からヒットするためにエッジの周りにも送信することに注意してください)。
スタックが空の場合、Kは原子を反射し、そのエネルギーをに変えて+1、をまっすぐ通過しSます。N改行を印刷します(それがきれいだからです:))。そしてその後、原子はR'0再び徹底的に行き、星の側に行き着くY。これ以上アトムがないため、これはリリースされず、プログラムは終了します。
核分裂数の計算:フレームワーク
プログラムの実際の内容を見てみましょう。コードは基本的に私のMathematicaリファレンス実装の移植版です:
fission[n_] := If[
(div =
SelectFirst[
Reverse@Divisors[2 n],
(OddQ@# == IntegerQ[n/#]
&& n/# > (# - 1)/2) &
]
) == 1,
1,
1 + Total[fission /@ (Range@div + n/div - (div + 1)/2)]
]
ここdivで、最大パーティション内の整数の数です。
主な違いは、Fissionでは半分の整数値を処理できないため、2を掛けて多くのことを実行していることと、Fissionには再帰がないことです。これを回避するために、パーティション内のすべての整数をキューにプッシュして、後で処理します。処理する各番号について、カウンターを1つずつ増やし、キューが空になったら、カウンターを解放して送信します。(キューはQ、KFIFO順でちょうどのように機能します。)
この概念のフレームワークは次のとおりです。
+--- input goes in here
v
SQS ---> compute div from n D /8/
~4X | /~KSA /
3 +-----------> { +X
initial trigger ---> W 6~@/ \/
4
W ^ /
| 3
^ generate range |
| from n and div <-+----- S6
| -then-
+---- release new trigger
最も重要な新しいコンポーネントは数字です。これらはテレポーターです。同じ数字のすべてのテレポーターは一緒に属します。原子は、任意のテレポーターを打つときに直ちに同じグループに次のテレポーターを移動する次の通常の左から右、上から下への順序で決定されます。これらは必要ではありませんが、レイアウトを支援します(したがって、少しゴルフをします)。またX、単純にアトムを複製し、1つのコピーをまっすぐに送り、もう1つのコピーを後ろに送ります。
今では、フレームワークのほとんどを自分で整理できるかもしれません。左上隅にはまだ処理される値のキューがありn、一度に1つずつ解放されます。の1つのコピーはn範囲の計算時に必要になるため、下部にテレポートされます。もう一方のコピーは、計算を行う上部のブロックに入りますdiv(これは、コードの最大の単一セクションです)。一度div計算されると、複製されます。1つのコピーが右上隅のカウンターをインクリメントし、これがに格納されKます。もう一方のコピーは下部にテレポートされます。場合divだった1、我々は上向きにすぐにそれを偏向し、新しい値をエンキューすることなく、次の反復のためのトリガとして使用します。そうでなければ私たちが使用divしてn 下部のセクションで新しい範囲(つまり、キューに入れられる対応する質量を持つ原子のストリーム)を生成し、範囲の完了後に新しいトリガーをリリースします。
キューが空になると、トリガーが反映され、まっすぐに通過してS右上隅に再び表示され、そこからカウンター(最終結果)がリリースされA、からitoaviaにテレポートされ8ます。
核分裂数の計算:ループ本体
残っているのはdiv、範囲を計算して生成する2つのセクションだけです。コンピューティングdivはこの部分です。
;
{+L /$ \/\/\/\/\/ 5/ @ [~ &@[S\/ \
/A@[ %5 /; & K } [S/
\ A$@S S\/ \/\/\/ \/>\ /S]@A / \
X X /> \ +\ A\ / \ /
/ ~A\; +;\ /@
ZX [K / {/ / @ @ } \ X @
\AS </ \V / }SZS S/
X ;;@\ /;X /> \ ; X X
\@+ >/ }$S SZS\+; //\V
/ \\ /\; X X @ @ \~K{
\0X / /~/V\V / 0W//
\ Z [K \ //\
\ /\7\A /;7/\/
おそらく、あなたはこれを自分自身でいくらかの忍耐で困惑させるほど十分に見ているでしょう。高レベルの内訳は次のとおりです。最初の12列程度で、の約数のストリームが生成されます2n。次の10列は、満たさない列を除外しますOddQ@# == IntegerQ[n/#]。次の8列は、満たさない列を除外しますn/# > (# - 1)/2)。最後に、スタック上のすべての有効な除数をプッシュし、完了したらスタック全体を核融合炉に空にし(最後/最大の除数を除くすべてを上書き)、結果を解放し、そのエネルギーを除去します(非-不等式のチェックからゼロ)。
そこには本当に何もしないクレイジーなパスがたくさんあります。主に、\/\/\/\/上部の狂気(5sもその一部です)と下部の1つのパス(7s を通過します)。いくつかの厄介な競合状態に対処するためにこれらを追加する必要がありました。核分裂は遅延コンポーネントを使用できます...
新しい範囲を生成するコードnとdiv、この次のとおりです。
/MJ $$\
4}K~@\ &] @\ 3/\
\{ }$A/1 2 }Y~K <\
\@ / \@<+@^ 1;}++@
2 ; \ /
最初に計算しn/div - (div + 1)/2(両方の用語はflooredで、同じ結果が得られます)、後で保存します。次に、div下から下の範囲を生成し1、保存された値をそれぞれに追加します。
これらの両方に2つの新しい一般的なパターンがあります。1つは、下からのヒットSXまたはZXヒット(またはローテーションバージョン)です。これは、1つのコピーを直進したい場合に原子を複製する良い方法です(核融合炉の出力のリダイレクトは面倒な場合があるため)。SまたはZ内原子を回転させX、その後、伝播の元の方向にミラーリングされたコピーバックを回転させます。
他のパターンは
[K
\A --> output
値を保存すると、上から負のエネルギーでK叩いKて値を繰り返し取得できます。A私たちが興味を持っている値を複製し、我々はそれを必要とする次の時間のためのスタックに右の背中をコピーして何を送信します。
まあ、それはかなりの話でした...しかし、あなたが実際にこれを乗り越えたなら、Fission iFis̢̘̗̗͢i̟nç̮̩r̸̭̬̱͔e̟̹̟̜͟d̙i̠͙͎̖͓̯b̘̠͎̭̰̼l̶̪̙̮̥̮y̠̠͎̺͜ ͚̬̮f̟͞u̱̦̰͍n͍̜̠̙t̸̳̩̝o̫͉̙͠p̯̱̭͙̜͙͞ŕ̮͓̜o̢̙̣̭g̩̼̣̝r̤͍͔̘̟ͅa̪̜͇m̳̭͔̤̞ͅ ͕̺͉̫̀ͅi͜n̳̯̗̳͇̹。