2つの番号を繰り返し追加して、任意の番号を作成します


14

あなたは、2つの16ビットレジスタを持つマシンを与え、しているxy。レジスタが初期化されx=1y=0。マシンが実行できる唯一の操作は、65536を法とする加算です。つまり、次のとおりです。

  • x+=y- xは次のものに置き換えられ(x + y) mod 65536ます。y変わらない
  • y+=x -同様に y
  • x+=x- xは次のものに置き換えられ2x mod 65536ます。x偶数の場合にのみ合法
  • y+=y -同様に y

目標は、レジスタのいずれxか(またはy)で事前に定義された数を取得することです。

プログラムまたは番号(で受け取るサブルーチン書くstdinargv関数のパラメータ、スタックの上部または任意の他の従来の場所)、そしてこの番号を取得するためのプログラムを出力します。出力は、に行くstdoutか、(言語がを持たない場合stdout)他の従来の出力デバイスに行く必要があります。

出力プログラムは、最大100%プラス最適から2ステップまで可能です。つまり、ターゲット番号を取得する最短プログラムにnステップがある場合、ソリューションはを超えることはできません2n+2。この制限は、「簡単すぎる」ソリューション(1、2、3、...など)を回避することですが、完全な最適化は必要ありません。最短のプログラムを見つけるのが最も簡単だと思いますが、確実ではありません...

例:入力=25。出力:

y+=x
x+=y
x+=y
x+=x
x+=x
x+=x
y+=x

別の例:フィボナッチ数の場合、出力にはこの交互パターンがあります。入力= 21の場合、出力は

y+=x
x+=y
y+=x
x+=y
y+=x
x+=y
y+=x

最短コード(バイト単位で測定)が優先されます。

(このパズルは、最近生成しなければならなかった16ビットプロセッサのコードに触発されました)

PS不思議-最適なプログラムが最も長いのはどの番号ですか?


9
好奇心から、なぜ偶数x+=xである場合にのみ合法xですか?また、最短のプログラムでは、BFSのようなものが機能すると思います。
Sp3000 14

ターゲットに到着した後、次のターゲット番号に進み続けたい場合があります。任意のターゲットに到達する可能性を得るには、数値の1つが奇数でなければなりません。...私は不必要な複雑さを避けるために、ターゲットの無限ストリームを作りたくなかったが、精神は、このような流れを可能にすることです
anatolyg

ステップ数の制限を変更したため、ターゲット番号0または1の場合、出力プログラムを空にする必要はありません。
アナトリグ14

3
x+=x偶数xのsでのみ動作する場合、25 doubles 3の入力の例はどうしてですか
bcsb1001

回答:


2

CJam、31

同様@Tobiaの答え、私のアルゴリズムはまた、臆面もなくされ、盗難に触発@CChakの答え"。しかし、CJamである黒魔術を駆使して、アルゴリズムの実装をさらに小さくすることができました。

ここで試してみてください。

ゴルフ済み:

qi{_4%!:X)/X!-'xX+"
y+="@}h;]W%`

ゴルフをしていない:

qi          "Read input and convert to integer.";
{           "Do...";
  _4%!:X    "Assign (value mod 4 == 0) to X.";
  )/X!-     "If X, divide value by 2. If not X, decrement value.";
  'xX+      "If X, put 'y' on the stack. If not X, put 'x' on the stack.";
  "
y+="        "Put '\ny+=' on the stack.";
  @         "Rotate top 3 elements of stack left so the value is on top.";
}h          "... while value is not zero.";
;           "Discard zero value on stack.";
]W%         "Collect stack into array and reverse.";

間違っている場合は修正してください。ただし、同様のアルゴリズムを使用した回答で使用されるモジュロ65536演算は不要であると考えました。入力が有効な符号なし16ビット整数であり、このアルゴリズムの中間値または結果も同様であると想定できるように、質問を解釈しました。


mod 65536の削除に関する興味深い点です。「所定の数」は0〜65535の範囲内でなければならないという良いケースがあると思います。
CChak

8

Perl 107 97

最初の投稿なので、ここに行きます。

sub h{($i)=@_;return if(($i%=65536)==0);($i%4==0)?do{h($i/2);say"y+=y";}:do{h($i-1);say"y+=x";}}

これは、すべてのレジスタ追加基準に適合しますが、答えが常に最適なステップ数の2n + 2以内であるかどうかを確認する徹底的なチェックは実行しませんでした。ただし、すべてのフィボナッチ数の制限内です。

詳細な内訳はこちら

sub h{                           # Declare the subroutine, it should be called referencing an integer value
   ($i)=@_;                      # Assign the i variable to the integer used in the call
   return if(($i%=65536)==0);    # Check for base condition of called by 0, and enforce modulo from the start.
   ($i%4==0) ?                   # If the value passed is even, and will result in an even number if we halve it
   do{h($i/2);say"y+=y";}        # Then do so and recurse 
   :do{h($i-1);say"y+=x";}       # Otherwise "subtract one" and recurse
}                                # Note that the print statements get called in reverse order as we exit.

私が述べたように、これはゴルフの私の最初の試みであるので、これは改善されると確信しています。また、最初のサブルーチン呼び出しを再帰呼び出しでカウントする必要があるかどうかわからないため、数文字を増やす可能性があります。

おもしろいことに、コードを11バイト*削減し、値だけを「2倍」にすることができるという要件を緩和することで、レジスタ操作の数の点で「効率」を改善できます。ここに楽しみのためにそれを含めました:

sub h{($i)=@_;return if(($i%=65536)==0);($i%2==0)?do{h($i/2);say"y+=y";}:do{h($i-1);say"y+=x";}}

補遺の開始:

本当にこの問題が好きだったので、私はこの数週間、この問題をいじり続けていました。結果を投稿すると思いました。

いくつかの数字:

BFSアルゴリズムを使用して最適なソリューションを見つけると、最初の2 ^ 16の数字には23の手順を必要とする18の数字しかありません。それらは、58558、59894、60110、61182、61278、62295、62430、62910、63422、63462、63979、64230、64314、4486、64510、64698、64854、65295です。

上記の再帰アルゴリズムを使用すると、到達する「最も困難な」数は、45操作で65535です。(65534は44を取り、43のステップを取る14の数字があります)65535は最適からの最大の逸脱であり、45対22です。23ステップの差は2n + 1です。(2nにヒットするのは3つの数値のみです:65534、32767、32751。)定義済みの範囲での些細な(ゼロステップ)ケースを除いて、再帰的方法は最適解の平均約1.4倍です。

結論:数値1-2 ^ 16の場合、再帰アルゴリズムは定義された2n + 2のしきい値を超えないため、答えは有効です。ただし、より大きなレジスタ/より多くのビットの最適なソリューションから離れすぎてしまうと思われます。

私がBFSを作成するために使用したコードは、ずさんで、メモリを大量に消費し、コメントされておらず、意図的に組み込まれていませんでした。だから...あなたは私の結果を信頼する必要はありませんが、私はそれらにかなり自信があります。


非BFSソリューション、すばらしい!
アナトリグ14

最も病的な場合でも、4倍以内にとどまると思います(最適なソリューションの下限しかわからないため)。それはまだかなり良いです。
有理数

7

Python 3、202バイト

def S(n):
 q=[(1,0,"")];k=65536
 while q:
  x,y,z=q.pop(0)
  if n in{x,y}:print(z);return
  q+=[((x+y)%k,y,z+"x+=y\n"),(x,(x+y)%k,z+"y+=x\n")]+[(2*x%k,y,z+"x+=x\n")]*(~x&1)+[(x,2*y%k,z+"y+=y\n")]*(~y&1)

(数バイトの@rationalisに感謝)

これは非常に基本的なソリューションです。私は最後の行をより良くゴルフできたらいいのにと思っていますが、私は現在アイデアを失っています。で呼び出しS(25)ます。

プログラムはキャッシングなしで単純なBFSを実行するだけなので、非常に遅いです。S(97)いくつかのサンプル出力については、次のとおりです。

y+=x
x+=y
x+=x
x+=x
x+=x
x+=x
y+=x
y+=x
x+=y

5

Dyalog APL、49文字/バイト*

{0=N←⍵|⍨2*16:⍬⋄0=4|N:⎕←'y+=y'⊣∇N÷2⋄⎕←'y+=x'⊣∇N-1}

@CChakの答えに恥ずかしく触発されたアルゴリズム。

例:

    {0=N←⍵|⍨2*16:⍬⋄0=4|N:⎕←'y+=y'⊣∇N÷2⋄⎕←'y+=x'⊣∇N-1} 25
y+=x
y+=x
y+=y
y+=x
y+=x
y+=y
y+=y
y+=x

ゴルフをしていない:

{
    N←(2*16)|⍵                 # assign the argument modulo 65536 to N
    0=N: ⍬                     # if N = 0, return an empty value (that's a Zilde, not a 0)
    0=4|N: ⎕←'y+=y' ⊣ ∇N÷2     # if N mod 4 = 0, recurse with N÷2 and *then* print 'y+=y'
    ⎕←'y+=x' ⊣ ∇N-1            # otherwise, recurse with N-1 and *then* print 'y+=x'
}

* Dyalog APLは、上位128バイト値にマップされたAPLシンボルを持つレガシー文字セットをサポートします。したがって、ASCII文字とAPLシンボルのみを使用するAPLプログラムは、バイト==文字と見なすことができます。


3

Python、183

def S(n):
 b,c,e=16,'x+=x\n','x+=y\n';s=d='y+=x\n';a=i=0
 if n<2:return
 while~n&1:n>>=1;a+=1
 while n:n>>=1;s+=[e,c][i]+d*(n&1);i=1;b-=1
 while a:s+=[c,c*b+e*2][i];i=0;a-=1
 print(s)

これが偶数の最適なプログラムの2倍以内にとどまることは保証できませんが、効率的です。すべての有効な入力に対して0 <= n < 65536、それは本質的に瞬時であり、最大33命令のプログラムを生成します。任意のレジスタサイズn(その定数を修正した後)の場合、O(n)多くの2n+1命令で時間ます。

いくつかのバイナリロジック

任意の奇数番号がn31個のステップ内に到達することができます。do y+=x、取得x,y = 1,1した後、倍増し続けるxx+=x(最初の倍増のために行うx+=yことから、xそもそも奇数です)。xこの方法で2のすべての累乗に到達します(左シフトです)。したがってy、対応する2の累乗を追加することで、任意のビットを1に設定できます。16ビットレジスタを使用し、最初に到達するには倍増し、y+=x、設定ため、最大31のopを取得します。

偶数nは2の累乗であり、それを呼び出しa、奇数倍、呼び出しますm。すなわちn = 2^a * m、または同等に、n = m << a。取得するために上記のプロセスを使用してmリセットし、その後、xそれは0ドゥなるまで左シフトそれがでx+=yセットにx = mして、倍増し続けてx使用して初めて、x+=yその後使用してx+=x

何であれa、取得するにはの16-aシフトがx必要y=mで、aリセットにはさらにシフトが必要x=0です。の後に別のaシフトxが発生しx=mます。そのため、合計16+aシフトが使用されます。16-a取得するために設定する必要があるビットまでありm、それらのそれぞれは1つを取りy+=xます。最後x=0に、mに設定するときに追加の手順が必要です。x+=yです。したがって、偶数を取得するには最大33ステップが必要です。

もちろん、これを任意のサイズのレジスタに一般化することができます。この場合、常に最大2n-12n+1、奇数と偶数の操作が必要ですnビットビットの整数に対してそれぞれます。

最適性

このアルゴリズムは、奇数に対して最適に近い(つまり2n+2if nが最小ステップ数である)プログラムを生成します。所与奇数ためn場合、mビット目が先頭1で、次に任意のプログラムは、少なくとも取りmを取得するステップx=n、またはy=n最速レジスタ値を増加させる操作であるため、x+=xまたはy+=y(すなわち、倍加)およびそれが取るmに到達するために倍加をm1から番目のビット、このアルゴリズムは、最大でかかるので2m、倍加あたり多くても2つに(ステップシフトと1対1にy+=x)奇数はほぼ最適に表されます。

偶数はあまり良くありません、それはそれがリセットするために常に16 opsを使用するからです xて他の何かの前、たとえば8は5ステップ以内に到達できるからです。

興味深いことに、上記のアルゴリズムは常に奇妙に保たれるy+=yため、まったく使用しませんy。この場合、実際には、3つの操作のみの制限されたセットの最短プログラムが見つかる場合があります。

テスト中

# Do an exhaustive breadth-first search to find the shortest program for
# each valid input
def bfs():
    d = {(0,1):0}
    k = 0xFFFF
    s = set(range(k+1))
    current = [(0,1)]
    nexts = []
    def add(pt, dist, n):
        if pt in d: return
        d[pt] = dist
        s.difference_update(pt)
        n.append(pt)
    i = 0
    while len(s) > 0:
        i += 1
        for p in current:
            x,y = p
            add((x,x+y&k), i, nexts)
            add((y,x+y&k), i, nexts)
            if y%2 == 0: add(tuple(sorted((x,y+y&k))), i, nexts)
            if x%2 == 0: add(tuple(sorted((x+x&k,y))), i, nexts)
        current = nexts
        nexts = []
        print(len(d),len(s))

# Mine (@rationalis)
def S(n):
    b,c,e=16,'x+=x\n','x+=y\n';s=d='y+=x\n';a=i=0
    if n<2:return ''
    while~n&1:n>>=1;a+=1
    while n:n>>=1;s+=[e,c][i]+d*(n&1);i=1;b-=1
    while a:s+=[c,c*b+e*2][i];i=0;a-=1
    return s

# @CChak's approach
def U(i):
    if i<1:return ''
    return U(i//2)+'y+=y\n' if i%4==0 else U(i-1)+'y+=x\n'

# Use mine on odd numbers and @CChak's on even numbers
def V(i):
    return S(i) if i % 2 == 1 else U(i)

# Simulate a program in the hypothetical machine language
def T(s):
    x,y = 1,0
    for l in s.split():
        if l == 'x+=x':
            if x % 2 == 1: return 1,0
            x += x
        elif l == 'y+=y':
            if y % 2 == 1: return 1,0
            y += y
        elif l == 'x+=y': x += y
        elif l == 'y+=x': y += x
        x %= 1<<16
        y %= 1<<16
    return x,y

# Test a solution on all values 0 to 65535 inclusive
# Max op limit only for my own solution
def test(f):
    max_ops = 33 if f==S else 1000
    for i in range(1<<16):
        s = f(i); t = T(s)
        if i not in t or len(s)//5 > max_ops:
            print(s,i,t)
            break

# Compare two solutions
def test2(f,g):
    lf = [len(f(i)) for i in range(2,1<<16)]
    lg = [len(g(i)) for i in range(2,1<<16)]
    l = [lf[i]/lg[i] for i in range(len(lf))]
    print(sum(l)/len(l))
    print(sum(lf)/sum(lg))

# Test by default if script is executed
def main():
    test()

if __name__ == '__main__':
    main()

ソリューションが実際に正しい結果を生成し、すべての有効な入力に対して33ステップを超えないことを確認する簡単なテストを作成しました(0 <= n < 65536)。

さらに、経験的分析を行って、ソリューションの出力を最適な出力と比較しましたが、幅優先探索はすべての有効な入力の最小出力長を取得するには非効率すぎることがわかりましたn。たとえば、BFSを使用しての出力を検索n = 65535しても、妥当な時間内に終了しません。それにもかかわらず、私は残ってbfs()おり、提案を受け入れています。

ただし、@ CChak(ここではPythonで実装されていますU)に対して独自のソリューションをテストしました。私の鉱山はもっと小さい数字になると劇的に非効率になるので悪化すると予想しましたが、2つの方法で全範囲で平均すると、鉱山は平均10.8%から12.3%短くなりました。これはおそらく、奇数での独自のソリューションの効率が向上したためだと思ったのでV、奇数でマイニングを使用し、偶数で@CChakを使用しますVが、中間です(約10%短いU、3%長いS)。


1
201バイトの非常に多くのロジック!
アナトリグ

@analtolyg私が言えることは、数学と少しいじるのが好きだということです。偶数のソリューションには改善の余地があるため、他のアプローチを調査することがあります。
有理数

おっと、これx,y='xy'まで可能だとは思いもしませんでした。残念ながら、書式設定をc*b+e*2簡潔に書き直す方法は考えられません%
有理数

ああ、あなたがどこかでそれを使ったことに気づかなかった。それは私だけですか、それともS(2)出力は本当に長いですか?
Sp3000

残念ながら、私のソリューションでは、すべての偶数が少なくとも19ステップ(19でS(2)最も短いステップ)を取ります。私はを追跡していないxyにもかかわらずので、明示的にx第二段階の後に達する2、それはそれにもかかわらず、リセットするに続けてx、よりよい解決策が存在しなければならないかのように0 Iの感触に、しかし、のよう、まだ私は考えることができません1。
有理数
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.