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)
Ich kann nicht garantieren, dass dies innerhalb des 2x optimalen Programms für gerade Zahlen bleibt, aber es ist effizient. Für alle gültigen Eingaben 0 <= n < 65536erfolgt dies im Wesentlichen sofort und es wird ein Programm mit höchstens 33 Anweisungen erstellt. Für eine beliebige Registergröße n(nach dem Fixieren dieser Konstante) würde es O(n)mit höchstens 2n+1Befehlen einige Zeit dauern .
Eine binäre Logik
Jede ungerade Zahl nkann in 31 Schritten erreicht werden: doy+=x , immer x,y = 1,1, und dann immer wieder zu verdoppeln xmit x+=x(für die erste Verdoppelung tun x+=y, da xungerade zu beginnen). xAuf diese Weise wird jede Potenz von 2 erreicht (es ist nur eine Linksverschiebung), und Sie können jedes Bit yauf 1 setzen, indem Sie die entsprechende Potenz von 2 addieren. Da wir 16-Bit-Register verwenden, und jedes Bit mit Ausnahme von für das erste braucht man eine Verdopplung, um zu erreichen und eine y+=xzu setzen, wir bekommen maximal 31 Ops.
Jede gerade Zahl nist nur eine Potenz von 2, nenne es amal eine ungerade Zahl, nenne es m; dh n = 2^a * moder gleichwertign = m << a . Verwenden Sie den obigen Prozess, um zu erhalten m, und setzen Sie ihn dann zurück, xindem Sie ihn nach links verschieben, bis er 0 ist. x+=ySetzen Sie a x = m, und verdoppeln Sie dann, wenn Sie xzum ersten Mal x+=yund anschließend verwenden x+=x.
Was auch immer aist, es braucht 16-aSchichten x, um zu kommen y=mund zusätzliche aSchichten, um zurückgesetzt zu werden x=0. Weitere aVerschiebungen von xwerden danach auftretenx=m . Es werden also insgesamt 16+aSchichten verwendet. Es gibt bis zu 16-aBits, die gesetzt werden müssen, um zu erhalten m, und jeder von ihnen benötigt eines y+=x. Zum Schluss brauchen wir noch einen zusätzlichen Schritt x=0, um m x+=y,. Es dauert also höchstens 33 Schritte, um eine gerade Zahl zu erhalten.
Sie können dies natürlich auf ein beliebiges Größenregister verallgemeinern. In diesem Fall werden für Ganzzahlen mit ungeraden und geraden Bits immer höchstens 2n-1und 2n+1ops verwendet n.
Optimalität
Dieser Algorithmus erzeugt ein Programm , das nahezu optimal ist (dh innerhalb 2n+2wenn nist die minimale Anzahl von Schritten) für ungerade Zahlen. Wenn für eine gegebene ungerade Zahl ndas mth-Bit die führende 1 ist, unternimmt jedes Programm mindestens mSchritte, um zu x=noder zu gelangen y=n, da die Operation, die die Werte der Register am schnellsten erhöht, x+=xoder y+=y(dh Verdopplungen) ist und es dauertm Verdopplungen , um zu gelangen das mth-Bit von 1. Da dieser Algorithmus höchstens 2mSchritte benötigt (höchstens zwei pro Verdopplung, einer für die Verschiebung und einer y+=x), wird jede ungerade Zahl nahezu optimal dargestellt.
Gerade Zahlen sind nicht ganz so gut, da immer 16 Operationen zum Zurücksetzen verwendet werden x, und 8 zum Beispiel in 5 Schritten erreicht werden können.
Interessanterweise wird der obige Algorithmus überhaupt nicht verwendet y+=y, da er yimmer ungerade ist. In diesem Fall wird möglicherweise das kürzeste Programm für den eingeschränkten Satz von nur 3 Operationen gefunden.
Testen
# 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()
Ich habe einen einfachen Test geschrieben, um zu überprüfen, ob meine Lösung tatsächlich für alle gültigen Eingaben ( 0 <= n < 65536) korrekte Ergebnisse liefert und nie mehr als 33 Schritte durchläuft .
Außerdem habe ich versucht, eine empirische Analyse durchzuführen, um die Ausgabe meiner Lösung mit den optimalen Ausgaben zu vergleichen. Es hat sich jedoch herausgestellt, dass die Breitensuche zu ineffizient ist, um die minimale Ausgabelänge für jede gültige Eingabe zu ermitteln n. Die Verwendung von BFS zum Ermitteln der Ausgabe für n = 65535wird beispielsweise nicht in angemessener Zeit beendet. Trotzdem bin ich bfs()offen für Vorschläge.
Ich habe jedoch meine eigene Lösung gegen @ CChak's getestet (implementiert in Python hier als U). Ich habe damit gerechnet, dass sich meine Leistung verschlechtern würde, da sie für kleinere gerade Zahlen drastisch ineffizient ist, aber über den gesamten Bereich auf zwei Arten gemittelt wird. Meine Leistung war durchschnittlich 10,8% bis 12,3% kürzer. Ich dachte, dies Vliege möglicherweise an der besseren Effizienz meiner eigenen Lösung für ungerade Zahlen. Daher wird meine für ungerade Zahlen und @ CChak für gerade Zahlen verwendet, liegt aber Vdazwischen (etwa 10% kürzer als U, 3% länger als S).
x+=xnur legal, wennxes gerade ist? Auch für das kürzeste Programm denke ich, dass so etwas wie BFS funktionieren könnte.