最速のミニフラッククイン


26

ミニフラックはのサブセットである脳高射砲の言語、<><...>および[]操作が禁止されています。厳密に言えば、次の正規表現と一致してはなりませ

.*(<|>|\[])

Mini-Flakは、Brain-Flakの最小の既知のチューリング完全サブセットです。


少し前、私は作ることができたクワインの中でミニフラックが、宇宙の一生の間に実行するには遅すぎました。

それで、あなたへの私の挑戦は、より速いQuineを作ることです。


得点

コードをスコアリングするに@cyは、コードの最後にフラグを付け、そのフラグを使用してRubyインタープリターで実行します(オンラインで試すにはrubyインタープリターを使用し-dます)。スコアは次のようにSTDERRに出力されます。

@cy <score>

これは、プログラムが終了するまでのサイクル数であり、実行間で同じです。各サイクルの実行にはほぼ同じ時間がかかるため、スコアはプログラムの実行にかかる時間と直接相関する必要があります。

Quineが長すぎてコンピューターで適切に実行できない場合は、手動でサイクル数を計算できます。

サイクル数の計算はそれほど難しくありません。サイクル数は、実行されるモナドの数の2倍に実行されるニラドの数を加えたものに相当します。これは、すべてのniladを単一の文字に置き換え、実行される文字の総数をカウントすることと同じです。

スコアリングの例

  • (()()()) モナドが1つ、ニラドが3つあるため、スコアは5です。

  • (()()()){({}[()])} ループには6モナドと2ニラドのスコア8が含まれるが、ループは3回実行されるため、スコアを3回カウントします。 1*5 + 3*8 = 29


必要条件

あなたのプログラムは...

  • 少なくとも2バイトである

  • -Aフラグを使用してBrain-Flakで実行されると、ソースコードを出力します

  • 正規表現と一致しません .*(<|>|\[])


ヒント

  • クレーン、高射砲のインタプリタは、より高速なRubyインタプリタより断定的であるが、いくつかの機能を欠いています。最初にCrane-Flakを使用してコードをテストし、それが機能することがわかったらルビーインタープリターでスコアを付けることをお勧めします。また、プログラムをTIOで実行しないことを強くお勧めします。TIOはデスクトップインタープリターよりも遅いだけでなく、約1分でタイムアウトします。TIOがタイムアウトする前にプログラムを実行するのに十分な低得点を獲得できた場合、非常に印象的です。

  • [(...)]{}(...)[{}]と同じように動作する<...>が、制限されたソースの要件を壊しません

  • この課題への取り組み方を知りたい場合は、Brain-FlakMini-Flak Quines をチェックしてください。


1
「最高の現在」 - >「現在のみ」
HyperNeutrino

回答:


33

Mini-Flak、6851113サイクル

プログラム(文字通り)

ほとんどの人は、Mini-Flakクインが印刷できない文字やマルチバイト文字を使用することを期待していない可能性が高いことを知っています(エンコードを適切にします)。しかし、このクインは、クインのサイズ(UTF-8の102646バイトとしてエンコードされた93919文字)と組み合わされて、印刷不可能なものがあるため、プログラムをこの投稿に配置することはかなり困難です。

しかし、このプログラムは非常に反復的であり、そのようなものとして、圧縮本当にうまく。Stack Exchangeからプログラム全体が文字通り利用できるように、以下の折りたたみ可能なものの後ろに隠された完全なクインの圧縮バージョンのxxd可逆的な16進ダンプがありgzipます。

(はい、それは非常に反復的であるため、圧縮されたでも繰り返しを見ることができます)。

質問には、「プログラムをTIOで実行しないことを強くお勧めします。TIOはデスクトップインタープリターよりも遅いだけでなく、約1分でタイムアウトします。 TIOがタイムアウトする前のプログラム。」私はそれを行うことができます!Rubyインタープリターを使用してTIOで実行するには約20秒かかります。オンラインで試してみてください。

プログラム(読みやすい)

ここで、コンピューターが読み取れるバージョンを提供しました。人間が読むことができるバージョンを試してみましょう。クインを構成するバイトをコードページ437(高ビットが設定されている場合)またはUnicode制御画像(ASCII制御コードの場合)に変換し、空白を追加しました(既存の空白は制御画像に変換されました) )、構文を使用してランレングスエンコードされ«string×length»、いくつかのデータ量の多いビットは省略されています:

␠
(((()()()()){}))
{{}
    (({})[(()()()())])
    (({})(
        {{}{}((()[()]))}{}
        (((((((({})){}){}{})){}{}){}){}())
        {
            ({}(
                (␀␀!S␠su! … many more comment characters … oq␝qoqoq)
                («()×35» («()×44» («()×44» («()×44» («()×44» («()×45»
                … much more data encoded the same way …
                («()×117»(«()×115»(«()×117»
                «000010101011┬â┬ … many more comment characters … ┬â0┬â┬à00␈␈
                )[({})(
                    ([({})]({}{}))
                    {
                        ((()[()]))
                    }{}
                    {
                        {
                            ({}(((({}())[()])))[{}()])
                        }{}
                        (({}))
                        ((()[()]))
                    }{}
                )]{}
                %Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'×almost 241»
                ,444454545455┬ç┬ … many more comment characters … -a--┬ü␡┬ü-a␡┬ü
            )[{}()])
        }{}
        {}({}())
    )[{}])
    (({})(()()()()){})
}{}{}␊

(「ほぼ241」は、241番目のコピーに末尾のが欠落している'が、それ以外は他の240と同一であるためです。)

説明

コメントについて

最初に説明するのは、印刷不可能な文字やMini-Flakコマンドではないその他のジャンクについてはどうですか?馬にコメントを追加すると事態が難しくなると思うかもしれませんが、これは速度の競争であり(サイズの競争ではありません)、コメントがプログラムの速度を損なわないことを意味します。一方、Brain-Flak、つまりMini-Flakは、スタックの内容を標準出力にダンプするだけです。スタックにのみが含まれるようにする必要がある場合プログラムのコマンドを構成する文字を使用すると、スタックをクリーニングするサイクルを費やす必要があります。ジャンクスタック要素が有効なBrain-Flakコマンド(これをBrain-Flak / Mini-Flakポリグロットにする)でなく、負でも外側でもない限り、Brain-Flakはほとんどの文字を無視します。 Unicodeの範囲では、スタックにそのまま残して出力できるようにし、プログラム内の同じ場所に同じ文字を配置してquineプロパティを保持できます。

これを利用できる特に重要な方法が1つあります。クインは長いデータ文字列を使用して機能し、基本的にクインからのすべての出力は、データ文字列をさまざまな方法でフォーマットすることによって生成されます。プログラムには複数の部分があるにもかかわらず、データ文字列は1つだけです。そのため、同じデータ文字列を使用してプログラムの異なる部分を印刷できる必要があります。「ジャンクデータは重要ではありません」トリックにより、非常に簡単な方法でこれを行うことができます。ASCIIコードとの間で値を加算または減算して、プログラムを構成する文字をデータ文字列に格納します。具体的には、プログラムの開始を構成する文字はASCIIコード+ 4として格納され、セクションを構成する文字はASCIIコード-4としてほぼ241回繰り返されますオフセット付きのデータ文字列のすべての文字。たとえば、すべての文字コードに4を追加して印刷すると、繰り返しセクションが1回繰り返され、前後にコメントが追加されます。(これらのコメントは、プログラムの他のセクションにすぎず、文字コードがシフトされているため、間違ったオフセットが追加されたため、有効なBrain-Flakコマンドを形成できません。Mini- 質問の部分への違反を避けるためのFlakコマンド。オフセットの選択は、これを保証するために設計されました。

このコメントトリックのため、実際には、2つの異なる方法でフォーマットされたデータ文字列を出力する必要があるだけです。これは非常に単純化されており、追加された長さは完全に価値があります。

プログラム構成

このプログラムは、イントロ、データ文字列、データ文字列フォーマッタ、およびアウトロの4つの部分で構成されています。introとoutroは基本的に、データ文字列とそのフォーマッターをループで実行し、毎回適切な形式(つまり、エンコードするかオフセットするか、使用するオフセット)を指定します。データ文字列は単なるデータであり、それを構成する文字がデータ文字列で文字どおりに指定されていない唯一の部分です(それはそれよりも長くなければならないため、明らかに不可能です); このように、それはそれ自体から再生するのが特に簡単な方法で書かれています。データ文字列フォーマッタは、ほぼ同じ241個の部分で構成され、各部分はデータ文字列の241個から特定のデータをフォーマットします。

プログラムの各部分は、次のようにデータ文字列とそのフォーマッタを介して生成できます。

  • outroを生成するには、オフセット+8でデータ文字列をフォーマットします
  • データ文字列フォーマッタを生成するには、オフセット+4、241回でデータ文字列をフォーマットします
  • データ文字列を生成するには、ソース文字列にエンコードしてデータ文字列をフォーマットします
  • イントロを作成するには、オフセット-4でデータ文字列をフォーマットします

そのため、プログラムのこれらの部分がどのように機能するかを調べるだけです。

データ文字列

(«()×35» («()×44» («()×44» («()×44» («()×44» («()×45» …

Mini-Flakコードでエンコードを逆にできるようにする必要があるため、データ文字列のシンプルなエンコードが必要です。あなたはこれよりもはるかに簡単になることはできません!

このコメントの裏にある重要なアイデアは(コメントトリックを除く)、大量のデータを格納できる場所は基本的に1つだけであることに注意することです。プログラムソースのさまざまなネストレベル内の「コマンド戻り値の合計」です。(これは一般に3番目のスタックとして知られています、Mini-Flakには2番目のスタックはありませんが、Mini-Flakコンテキストでは「作業スタック」の方が適切な名前である可能性があります。)データを格納する他の可能性はメイン/最初のスタックですこれは出力が必要な場所であり、リモートで効率的な方法でストレージを超えて出力を移動することはできず、単一のスタック要素でビッグナムにエンコードされるためです(指数関数的な時間がかかるため、この問題には適していません)それからデータを抽出します); これらを削除すると、作業スタックのみが残りの場所になります。

このスタックにデータを「格納」するために、バランスの取れていないコマンド(この場合は(…)コマンドの前半)を使用します。これは、後でデータ文字列フォーマッタ内でバランスが取られます。フォーマッタ内でこれらのコマンドのいずれかを閉じるたびに、データ文字列から取得したデータの合計と、フォーマッタ内のそのネストレベルのすべてのコマンドの戻り値がプッシュされます。後者がゼロに追加されるようにすることができるため、フォーマッタはデータ文字列から取得した単一の値を単に見るだけです。

形式は非常に単純です:(、その後にn個のコピーが続きます。()ここで、nは保存する数値です。(これは、負でない数値のみを保存できることを意味し、データ文字列の最後の要素は正である必要があることに注意してください。)

データ文字列に関する少し直感的でない点は、その順序です。データ文字列の「開始」は、プログラムの開始に近い終了、つまり最も外側のネストレベルです。この部分は最後にフォーマットされます(フォーマッターが最も内側から最も外側のネストレベルまで実行されるため)。しかし、最後にフォーマットされているにもかかわらず、それが取得し印刷最初のスタックにプッシュされた値は、ミニフラックインタプリタにより最後に印刷されているため、第一。同じ原則がプログラム全体に適用されます。最初にoutroをフォーマットし、次にデータ文字列フォーマッター、次にデータ文字列、次にイントロ、つまりプログラムに保存されている順序の逆をフォーマットする必要があります。

データ文字列フォーマッタ

)[({})(
    ([({})]({}{}))
    {
        ((()[()]))
    }{}
    {
        {
            ({}(((({}())[()])))[{}()])
        }{}
        (({}))
        ((()[()]))
    }{}
)]{}

データ文字列フォーマッタは、それぞれが同じコード(1つのセクションはわずかに異なるコメントを持つ)を持つ241個のセクションで構成され、各セクションはデータ文字列の特定の1文字をフォーマットします。(ここではループを使用できませんでした。unbalanced )を照合してデータ文字列を読み込むには、unbalanced が必要です。ループの(1つである{…}ループは、存在する唯一のループです。そのため、代わりに、フォーマッタを展開し、イントロ/アウトロを取得して、フォーマッタのオフセットを含むデータ文字列を241回出力します。)

)[({})( … )]{}

フォーマッタ要素の最も外側の部分は、データ文字列の1つの要素を読み取ります。データ文字列のエンコードが単純であるため、データ文字列の読み取りが少し複雑になります。(…)データ文字列で一致しないものを閉じて[…]から、2つの値を否定()します。データ文字列から読み取ったデータ(({}))とプログラムの残りの戻り値です。フォーマッタ要素の残りの戻り値をで(…)コピーし、そのコピーを否定バージョンに追加します{}。最終結果は、データ文字列要素とフォーマッタ要素の戻り値が、データムからデータムから戻り値に戻り値を加えた値、または0になることです。これは、次のデータ文字列要素が正しい値を生成するために必要です。

([({})]({}{}))

フォーマッタは、最上位スタック要素を使用して、どのモードにあるかを認識します(0 =データ文字列フォーマットのフォーマット、その他の値=出力のオフセット)。ただし、データ文字列を読み取っただけで、データはスタック上の形式の一番上にあり、逆方向にデータが必要です。このコードは、撮影脳フラックスワップコードの短い変異体であり、上記BをするB上記 +の  B。追加の副作用ためだけでなく、それは短いですが、それは、(この特定のケースでは)また、より便利ですBをする際に問題とならないbが 0である、そしてときbは 0ではない、それは私たちのためにオフセット計算を行います。

{
    ((()[()]))
}{}
{
    …
    ((()[()]))
}{}

Brain-Flakには制御フロー構造が1つしかないため、whileループ以外のものが必要な場合は、少し手間がかかります。これは「否定」構造です。スタックの先頭に0がある場合、それを削除します。それ以外の場合は、スタックの先頭に0を配置します。(かなり簡単に動作します:スタックの先頭に0がない限り、スタックに1 − 1を2回プッシュします。完了したら、先頭のスタック要素をポップします。)

ここに見られるように、否定構造内にコードを配置することが可能です。コードは、スタックの最上位がゼロ以外の場合にのみ実行されます。したがって、2つのnegate構造があり、上部の2つのスタック要素が両方ともゼロでないと仮定すると、それらは互いにキャンセルされますが、最初の構造内のコードは、上部のスタック要素がゼロ以外で、 2番目の構造は、一番上のスタック要素がゼロの場合にのみ実行されます。つまり、これはif-then-elseステートメントと同等です。

形式がゼロ以外の場合に実行される「then」節では、実際には何もしません。私たちが望んでいるのは、データ+オフセットをメインスタックにプッシュすることです(そのため、プログラムの最後に出力できるようになります)が、既にそこにあります。したがって、ソース形式でデータ文字列要素をエンコードする場合にのみ対処する必要があります。

{
    ({}(((({}())[()])))[{}()])
}{}
(({}))

以下にその方法を示します。{({}( … )[{}()])}{}構造ワーキングスタックにループカウンタを移動させ、そこに保持することによって動作する反復の特定の数(とループとして理解しておく必要があり、作業スタックへのアクセスがに接続されているので、それは、他のコードから安全ですよプログラムのネストレベル)。ループの本体はです((({}())[()]))。これにより、一番上のスタック要素のコピーが3つ作成され、一番下に1が追加されます。言い換えると、スタックの上の40を、41の上の40の上にある40に変換するか、ASCIIとして表示さ((()ます。これを繰り返し実行することになります((()(()()(()()()(そこだと仮定し、その上、ひいては我々のデータ列を生成するための簡単な方法で(、すでにスタックの一番上)。

我々がループして完了したら、(({}))それは今開始されるように、スタック(のトップを複製する((()…のではなく(()…。大手(次の文字をフォーマットするデータ列フォーマッタの次のコピーで使用されます(それはにそれを拡張します(()(()…その後(()()(()…、など(、データ文字列に区切りが生成されます)。

%Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'

データ文字列フォーマッタには、最後にもう1つ興味があります。OK、だいたいこれは、単に4つのコードポイントを下方にシフトしたアウトロです。ただし、最後のアポストロフィは場違いに見える場合があります。'(コードポイント39)は+(コードポイント43)にシフトしますが、これはBrain-Flakコマンドではないため、他の目的で使用されていると推測できます。

これがここにある理由は、データ文字列フォーマッタが(既にスタック上にあることを予期しているためです(リテラル40はどこにも含まれていません)。の'実際には、データ文字列フォーマッタの文字がスタックにプッシュされた後、データ文字列フォーマッタを構成するために繰り返されるブロックの先頭にあります(そして、コードはデータ文字列の印刷に移ろうとしています)それ自体)、アウトロは、スタックの上の39を40に調整し、フォーマッター(今回はソースの表現ではなく、実行中のフォーマッター自体)を使用できるようにします。そのため、フォーマッタの「ほぼ241個」のコピーがあります。最初のコピーには最初の文字がありません。そして、その文字であるアポストロフィは、プログラム内のどこかにあるMini-Flakコードに対応していないデータ文字列内の3つの文字のうちの1つです。純粋に定数を提供する方法として存在します。

イントロとアウトロ

(((()()()()){}))
{{}
    (({})[(()()()())])
    (({})(
        {{}{}((()[()]))}{}
        (((((((({})){}){}{})){}{}){}){}())
        {
            ({}(
                (␀␀!S␠su! … many more comment characters … oq␝qoqoq)
                …
            )[{}()])
        }{}
        {}({}())
    )[{}])
    (({})(()()()()){})
}{}{}␊

イントロとアウトロは、概念的にはプログラムの同じ部分です。区別する唯一の理由は、データ文字列とそのフォーマッタの前にoutroを出力する必要があるため(その後に印刷するため)、イントロはそれらの後に出力する必要がある(前に印刷する)ことです。

(((()()()()){}))

スタックに8個のコピーを2つ置くことから始めます。これは、最初の反復のオフセットです。2番目のコピーは、メインループがオフセットの上のスタックの最上部にジャンクエレメントが存在することを期待し、メインループが存在するかどうかを決定するテストから取り残されているためです。実際に必要な要素は捨てられません。コピーはそれを行うための最も簡単な(つまり出力が最も速い)方法です。

これより長くない番号8の他の表現があります。ただし、最速のコードを使用する場合は、これが間違いなく最良のオプションです。たとえば、両方が8文字であるにもかかわらず、前者は2サイクルとしてカウントされますが、1つとしてカウントされるため、使用()()()()は、たとえば、よりも(()()){}高速です。一の周期を保存するためのはるかに大きな考慮に比べて無視できる:けれども、およびはるかに低いコードポイントを持っているよりも、そして、ので、それらのデータの断片を生成することははるかに高速(およびデータフラグメントがコードに少ないスペースを取るだろうとなり、も))。(…)()(){}

{{} … }{}{}

メインループ。これは反復をカウントしません(whileループではなくforループであり、テストを使用してブレークアウトします)。終了すると、上位2つのスタック要素を破棄します。上の要素は無害な0ですが、下の要素は「負のオフセットである」負の数である「次の反復で使用するフォーマット」であり、Mini -Flakプログラムは終了し、インタープリターはそれらを出力しようとしてクラッシュします。

このループは明示的なテストを使用してブレークアウトするため、そのテストの結果はスタックに残されるため、最初に行うこととして破棄します(値は役に立たない)。

(({})[(()()()())])

このコードは 、スタック要素fの上に4とf − 4をプッシュし、その要素をそのまま残します。次の反復の形式を事前に計算し(定数4が手元にある間)、プログラムの次のいくつかの部分でスタックを正しい順序に同時に取得します。形式としてfを使用しますこの反復、およびその前に4が必要です。

(({})( … )[{}])

これにより、f  -4のコピーが作業スタックに保存され、次の反復で使用できるようになります。(その時点でもfの値は存在しますが、スタックの扱いにくい場所にあり、正しい場所に移動できたとしても、4を引くサイクルを費やさなければなりません。コードを印刷してその減算を実行します。今すぐ保存する方がはるかに簡単です。)

{{}{}((()[()]))}{}

オフセットが4であるかどうかを確認するテスト(つまり、f  − 4が0)。存在する場合、データ文字列フォーマッタを印刷するため、このオフセットで1回だけではなく、データ文字列とそのフォーマッタを241回実行する必要があります。コードはかなり単純である:場合、F  - 4置き換え、非ゼロであるF  -零点の対と4,4自体を、どちらの場合でも、一番上のスタック要素をポップします。スタック上にf(上記の繰り返しを241回印刷したい場合)または0(一度だけ印刷したい場合)のいずれかのfを超える数字があります。

(
    ((((((({})){}){}{})){}{}){}){}
    ()
)

これは興味深い種類のBrain-Flak / Mini-Flak定数です。ここの長い行は数字の60を表して()います。通常、Brain-Flak定数のいたるところにあるの欠如に混乱するかもしれません。これは通常の数字ではなく、数字を複製操作として解釈する教会の数字です。たとえば、ここにある60の教会の数字は、入力の60のコピーを作成し、それらをすべて1つの値に結合します。Brain-Flakでは、加算できるのは通常の数字だけです。したがって、スタックの最上部のコピーを60個追加し、スタックの最上部に60を掛けます。

補足として、Underload数字ファインダーを使用して、Underload構文で教会数字を生成し、Mini-Flakでも適切な数字を見つけることができます。アンダーロード数字(ゼロ以外)は、「最上位スタック要素の複製」:および「最上位2つのスタック要素の結合」操作を使用し*ます。これらの操作は両方ともBrain-Flakに存在するため、に翻訳:)*に先頭に{}a {}を追加(し、バランスをとるために開始時に十分に追加します(これはメインスタックと作業スタックの奇妙な組み合わせを使用していますが、動作します)。

この特定のコードフラグメントは、教会の数字60(実質的に「60で乗算」スニペット)を増分とともに使用して、式60 x  + 1 を生成します。したがって、前のステップで4が得られた場合、 241の場合、または0があった場合は、値1を取得するだけです。つまり、必要な反復回数を正しく計算します。

241の選択は偶然ではありません。これは、a)プログラムがとにかく終了するおおよその長さ、およびb)ラウンド数の4倍を超える値になるように選択された値でした。ラウンド数(この場合は60)は、コピーする要素の柔軟性が高いため、教会の数として表現が短くなる傾向があります。プログラムには、長さを正確に最大241にするために、後でパディングが含まれます。

{
    ({}(
        …
    )[{}()])
}{}

これは、前に見たようなforループで、メインスタックの最上部と同じ回数だけコードを実行します(消費します。ループカウンター自体は作業スタックに格納されますが、これはプログラムのネストレベルに関連付けられているため、forループ自体以外の要素と対話することはできません。これは実際にデータ文字列とそのフォーマッターを1回または241回実行し、メインスタックから制御フローの計算に使用していたすべての値をポップしたので、その上で使用するフォーマットが用意されています。使用するフォーマッタ。

(␀␀!S␠su! … many more comment characters … oq␝qoqoq)

ここでのコメントは、まったく興味がないわけではありません。一つには、Brain-Flakコマンドがいくつかあります。)終了時には、当然、プログラム作業の様々なセグメント間の遷移方法の副作用として生成されるので、(開始時は、手動でコメント内部を入れ、それ(およびコメント内部の長さにもかかわらず、のバランスをとるために添加しました()コマンドがまだある()ので、すべてそれがない、コマンドの戻りデータ列の値とそのフォーマッタ、forループは完全に無視することを何か)に1を追加することです。

さらに注目すべきは、コメントの先頭にあるこれらのNUL文字は明らかに何からもオフセットされていないことです(+8と-4の違いでさえ、(aをNUL に変えるには十分ではありません)。これらは、239要素のデータ文字列を241要素までもたらす純粋なパディングです(簡単に支払います:必要な反復回数を計算するとき、1対241ではなく1対239を生成するのに2バイト以上かかります) )。NULは可能な限り低いコードポイントを持っているため、パディング文字として使用されました(データ文字列のソースコードが短くなり、出力が速くなります)。

{}({}())

一番上のスタック要素(使用している形式)をドロップし、次に1を追加します(出力する最後の文字、つまりフォーマットしたばかりのプログラムセクションの最初の文字)。古い形式はもう必要ありません(新しい形式は作業スタックに隠れています)。ほとんどの場合、増分は無害で'、データ文字列フォーマッタのソース表現の一方の端を((データ文字列自体をフォーマットするためにフォーマッタを次に実行するときにスタックで必要です)に変更します。私たちは、で開始する要素フォーマッタ各データ列を強制するので、アウトロまたはイントロでそのような変換を必要とする(、それが多少複雑になるだろう(私たちは閉鎖する必要があるだろうと(し、後でその効果を元に戻す)、および241個で(はなく、ほぼ 241個のフォーマッタのコピーしか持っていないので、何らかの理由で余分なものを生成する必要があります(したがって、無害な文字'が欠落していることが最善です)。

(({})(()()()()){})

最後に、ループ終了テスト。メインスタックの現在の最上位は、次の反復に必要な形式です(作業スタックから戻ってきたばかりです)。これによりコピーされ、コピーに8が追加されます。結果の値は、ループの次回ラウンドで破棄されます。ただし、イントロを印刷したばかりの場合、オフセットは-4であったため、「次の反復」のオフセットは-8になります。-8 + 8は0であるため、ループはその後反復に継続するのではなく終了します。


16

128,673,515サイクル

オンラインで試す

説明

Miniflak quinesが遅い運命にある理由は、Miniflakにランダムアクセスがないことです。これを回避するには、数値を取り込んでデータムを返すコードのブロックを作成します。各データは前と同じように単一の文字を表し、メインコードはこのブロックに対して一度に1つのブロックを照会するだけです。これは、基本的にランダムアクセスメモリのブロックとして機能します。


このコードブロックには2つの要件があります。

  • 数字を受け取り、その文字の文字コードのみを出力する必要があります

  • Brain-Flakで少しずつルックアップテーブルを簡単に再現できる必要があります。

このブロックを構築するために、Miniflakがチューリング完全であることの証明から実際にメソッドを再利用しました。各データには、次のようなコードブロックがあります。

(({}[()])[(())]()){(([({}{})]{}))}{}{(([({}{}(%s))]{}))}{}

これは、スタックの一番上の数値から1を減算し、ゼロが%sその下のデータムをプッシュする場合。スタック上でnから開始すると、各ピースがサイズを1ずつ減らすため、n番目のデータを取得します。

これはすてきでモジュール式なので、プログラムで簡単に書くことができます。


次に、このメモリを実際にソースに変換するマシンをセットアップする必要があります。これは、次の3つの部分で構成されています。

(([()]())())
{({}[(
  -Look up table-
 )]{})
 1. (({}[()])[(())]()){(([({}{})]{}))}{}{([({}{}(([{}]))(()()()()()))]{})}{}

 2. (({}[()])[(())]()){(([({}{})]{}))}{}{([({}{}
      (({}[(
      ({}[()(((((()()()()()){}){}){}))]{}){({}[()(({}()))]{}){({}[()(({}((((()()()){}){}){}()){}))]{}){({}[()(({}()()))]{}){({}[()(({}(((()()()()())){}{}){}))]{}){([(({}{}()))]{})}}}}}{}
      (({}({}))[({}[{}])])
     )]{}({})[()]))
      ({[()]([({}({}[({})]))]{})}{}()()()()()[(({}({})))]{})
    )]{})}{}

 3. (({}[()])[(())]()){(([({}{})]{}))}{}{([({}{}
     (({}(({}({}))[({}[{}])][(
     ({}[()(
      ([()](((()()[(((((((()()()){})())){}{}){}){})]((((()()()()())){}{}){})([{}]([()()](({})(([{}](()()([()()](((((({}){}){}())){}){}{}))))))))))))
     )]{})
     {({}[()(((({})())[()]))]{})}{}
     (([(((((()()()()){}){}()))){}{}([({})]((({})){}{}))]()()([()()]({}(({})([()]([({}())](({})([({}[()])]()(({})(([()](([({}()())]()({}([()](([((((((()()()())()){}){}){}()){})]({}()(([(((((({})){}){}())){}{})]({}([((((({}())){}){}){}()){}()](([()()])(()()({}(((((({}())())){}{}){}){}([((((({}))){}()){}){}]([((({}[()])){}{}){}]([()()](((((({}())){}{}){}){})(([{}](()()([()()](()()(((((()()()()()){}){}){}()){}()(([((((((()()()())){}){}())){}{})]({}([((((({})()){}){}){}()){}()](([()()])(()()({}(((((({}){}){}())){}){}{}(({})))))))))))))))))))))))))))))))))))))))))))))))
     )]{})[()]))({()()()([({})]{})}{}())
    )]{})}{}

   ({}[()])
}{}{}{}
(([(((((()()()()){}){}())){}{})]((({}))([()]([({}())]({}()([()]((()([()]((()([({})((((()()()()){}){}()){})]()())([({})]({}([()()]({}({}((((()()()()()){}){}){}))))))))))))))))))

マシンは、1から始まり3で終わる順番に実行される4つの部分で構成されています。上記のコードでそれらにラベルを付けています。各セクションでは、エンコードに使用するのと同じルックアップテーブル形式も使用します。これは、プログラム全体がループに含まれており、ループを実行するたびにすべてのセクションを実行するのではなく、同じRA構造に入れて、必要なセクションをクエリするためです。

1

セクション1は、単純なセットアップセクションです。

プログラムは、最初のクエリセクション1とデータム0を通知します。データム0は存在しないため、その値を返す代わりに、データムごとにクエリを1回減らします。これは、結果を使用してデータの数を判断できるため便利です。これは、今後のセクションで重要になります。セクション1は、結果を否定することでデータの数を記録し、セクション2と最後のデータを照会します。唯一の問題は、セクション2を直接クエリできないことです。別のデクリメントが残っているため、存在しないセクション5をクエリする必要があります。実際、これは別のセクション内のセクションをクエリするたびに当てはまります。説明ではこれを無視しますが、コードを探している場合は、5がセクションに戻ることを意味し、4は同じセクションを再度実行することを意味します。

2

セクション2は、データをデータブロックの後のコードを構成する文字にデコードします。スタックが次のように表示されると予想されるたびに:

Previous query
Result of query
Number of data
Junk we shouldn't touch...

可能な各結果(1〜6の数字)を6つの有効なMiniflak文字((){}[])の1つにマップし、「迷惑メールは触れないでください」のデータ数の下に配置します。これにより、次のようなスタックが得られます。

Previous query
Number of data
Junk we shouldn't touch...

ここから、次のデータをクエリするか、クエリを実行した場合はすべてセクション3に移動します。前のクエリは、実際に送信された正確なクエリではなく、クエリからブロック内のデータ数を引いたものです。これは、各データムがクエリを1つずつ減らすため、クエリが非常に破損しているためです。次のクエリを生成するには、データ数のコピーを追加し、1を減算します。スタックは次のようになります。

Next query
Number of data
Junk we shouldn't touch...

次のクエリがゼロの場合、セクション3で必要なすべてのメモリを読み取ったので、クエリにデータの数を再度追加し、スタックの4を叩いてセクション3に移動します。次のクエリがゼロでない場合、セクション2を再度実行するには、スタックに5を置きます。

3

セクション3では、セクション3と同様にRAMにクエリを実行してデータのブロックを作成します。

簡潔にするために、セクション3の機能の詳細のほとんどは省略します。セクション2とほぼ同じですが、各データを1文字に変換する代わりに、それぞれをRAM内のエントリを表す長いコードチャンクに変換します。セクション3が完了すると、ループを終了するようプログラムに指示します。


ループが実行された後、プログラムはquineの最初のビットをプッシュするだけ([()]())(()()()()){({}[(です。これは、標準のコルモゴロフ複雑性手法を実装する次のコードで行います。

(([(((((()()()()){}){}())){}{})]((({}))([()]([({}())]({}()([()]((()([()]((()([({})((((()()()()){}){}()){})]()())([({})]({}([()()]({}({}((((()()()()()){}){}){}))))))))))))))))))

これが明確だったと思います。何か混乱している場合はコメントしてください。


これを実行するのにどれくらい時間がかかりますか?TIOで時間を計ります。
パベル

@Pavel TIOで実行するのは信じられないほど遅いため、TIOでは実行しません。TIOが使用するのと同じインタープリター(ルビー1)を使用します。アクセスできる古いラックサーバーで実行するには、約20分かかります。Crain-Flakでは約15分かかりますが、Crain-Flakにはデバッグフラグがないため、Rubyインタープリターで実行しないとスコアを取得できません。
小麦ウィザード

@Pavelもう一度実行して、時間を計りました。30m45.284srubyインタープリターを使用して、かなりローエンドのサーバー(平均的な現代のデスクトップとほぼ同等)で完了するには時間がかかりました。
小麦ウィザード
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.