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)
Non posso garantire che questo rimanga entro il doppio del programma ottimale per i numeri pari, ma è efficiente. Per tutti gli input validi 0 <= n < 65536
, è essenzialmente istantaneo e genera un programma di massimo 33 istruzioni. Per una dimensione di registro arbitraria n
(dopo aver corretto quella costante), ci vorrebbe del O(n)
tempo con al massimo le 2n+1
istruzioni.
Qualche logica binaria
È n
possibile raggiungere qualsiasi numero dispari in 31 passaggi: fare y+=x
, ottenere x,y = 1,1
e quindi continuare a raddoppiare x
con x+=x
(per il primo raddoppio x+=y
, poichéx
è dispari per cominciare). x
raggiungerà ogni potenza di 2 in questo modo (è solo uno spostamento a sinistra), e quindi puoi impostare qualsiasi bit di y
essere 1 aggiungendo la potenza corrispondente di 2. Dato che stiamo usando registri a 16 bit, e ogni bit tranne perché il primo richiede un raddoppio per raggiungere e unoy+=x
da impostare, otteniamo un massimo di 31 operazioni.
Qualsiasi numero pari n
è solo una potenza di 2, chiamalo a
, moltiplica un numero dispari, chiamalo m
; cioè n = 2^a * m
, o equivalentemente, n = m << a
. Utilizzare il processo sopra per ottenere m
, quindi reimpostare x
spostandolo a sinistra fino a raggiungere lo 0. Eseguire un x+=y
a x = m
, quindi continuare a raddoppiarex
, la prima volta usando x+=y
e successivamente usandox+=x
.
Qualunque cosa a
sia, ci vogliono 16-a
turni x
per ottenere y=m
e ulteriori a
turni per ripristinare x=0
. Altri a
spostamenti di x
si verificheranno dopo x=m
. Quindi 16+a
viene utilizzato un totale di turni. Ci sono fino a 16-a
bit che devono essere impostati per ottenere m
, e ognuno di questi ne prenderà uno y+=x
. Finalmente abbiamo bisogno di un passaggio in più quandox=0
impostarlo su m x+=y
,. Quindi sono necessari al massimo 33 passaggi per ottenere qualsiasi numero pari.
Naturalmente, puoi generalizzare questo a qualsiasi registro di dimensioni, nel qual caso ci vuole sempre al massimo 2n-1
e2n+1
ops per n
interi pari e dispari , rispettivamente.
ottimalità
Questo algoritmo produce un programma quasi ottimale (ovvero entro 2n+2
se n
è presente il numero minimo di passaggi) per i numeri dispari. Per un dato numero dispari n
, se il m
th bit è il 1 iniziale, allora qualsiasi programma prende almeno i m
passi per arrivare a , x=n
o y=n
, poiché l'operazione che aumenta più velocemente i valori dei registri è x+=x
o y+=y
(cioè doppi) e ci vogliono m
doppi per arrivare a il m
th bit da 1. Poiché questo algoritmo compie la maggior parte dei 2m
passaggi (al massimo due per il raddoppio, uno per il turno e uno y+=x
), qualsiasi numero dispari viene rappresentato quasi in modo ottimale.
I numeri pari non sono altrettanto buoni, poiché utilizzano sempre 16 operazioni per ripristinare x
prima di qualsiasi altra cosa e 8, ad esempio, possono essere raggiunti in 5 passaggi.
È interessante notare che l'algoritmo di cui sopra non utilizza mai y+=y
affatto, poiché y
è sempre mantenuto strano. In tal caso, potrebbe effettivamente trovare il programma più breve per l'insieme limitato di sole 3 operazioni.
analisi
# 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()
Ho scritto un semplice test per verificare che la mia soluzione produca effettivamente risultati corretti e non superi mai i 33 passaggi, per tutti gli input validi ( 0 <= n < 65536
).
Inoltre, ho tentato di fare un'analisi empirica per confrontare l'output della mia soluzione con gli output ottimali, tuttavia risulta che l'ampiezza della ricerca è troppo inefficiente per ottenere la lunghezza minima dell'output per ogni input valido n
. Ad esempio, l'utilizzo di BFS per trovare l'output per n = 65535
non termina in un periodo di tempo ragionevole. Tuttavia sono uscito bfs()
e sono aperto ai suggerimenti.
Ho, tuttavia, testato la mia soluzione contro @ CChak (implementata in Python qui come U
). Mi aspettavo che il mio sarebbe andato peggio, dal momento che è drasticamente inefficiente per i numeri pari più piccoli, ma mediata su tutta la gamma in due modi, il mio ha prodotto un output di lunghezza in media dal 10,8% al 12,3% in meno. Pensavo che ciò fosse dovuto a una migliore efficienza della mia soluzione sui numeri dispari, quindi V
usa il mio sui numeri dispari e @ CChak sui numeri pari, ma V
è in mezzo (circa il 10% in meno di U
, il 3% in più di S
).
x+=x
legale solo sex
è pari? Anche per il programma più breve penso che qualcosa come BFS potrebbe funzionare.