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
命令で時間ます。
いくつかのバイナリロジック
任意の奇数番号がn
31個のステップ内に到達することができます。do y+=x
、取得x,y = 1,1
した後、倍増し続けるx
とx+=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-1
で2n+1
、奇数と偶数の操作が必要ですn
ビットビットの整数に対してそれぞれます。
最適性
このアルゴリズムは、奇数に対して最適に近い(つまり2n+2
if n
が最小ステップ数である)プログラムを生成します。所与奇数ためn
場合、m
ビット目が先頭1で、次に任意のプログラムは、少なくとも取りm
を取得するステップx=n
、またはy=n
最速レジスタ値を増加させる操作であるため、x+=x
またはy+=y
(すなわち、倍加)およびそれが取るm
に到達するために倍加をm
1から番目のビット、このアルゴリズムは、最大でかかるので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
)。
x+=x
である場合にのみ合法x
ですか?また、最短のプログラムでは、BFSのようなものが機能すると思います。