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)
Je ne peux pas garantir que cela reste à 2 fois le programme optimal pour les nombres pairs, mais il est efficace. Pour toutes les entrées valides 0 <= n < 65536, elle est essentiellement instantanée et génère un programme d'au plus 33 instructions. Pour une taille de registre arbitraire n(après avoir fixé cette constante), cela prendrait du O(n)temps avec tout au plus des 2n+1instructions.
Un peu de logique binaire
N'importe quel nombre impair npeut être atteint en 31 étapes: faire y+=x, obtenir x,y = 1,1, puis continuer à doubler xavec x+=x(pour le premier doublement, faire x+=y, car il xest impair pour commencer). xatteindra chaque puissance de 2 de cette façon (c'est juste un décalage vers la gauche), et vous pouvez donc définir n'importe quel bit de ypour être 1 en ajoutant la puissance correspondante de 2. Puisque nous utilisons des registres 16 bits, et chaque bit sauf pour le premier, il faut un doublement pour atteindre et un y+=xpour mettre, nous obtenons un maximum de 31 ops.
Tout nombre pair nn'est qu'une puissance de 2, appelez-le a, multipliez un nombre impair, appelez-le m; à- dire n = 2^a * m, ou de manière équivalente, n = m << a. Utilisez le processus ci-dessus pour obtenir m, puis réinitialisez x-le en le déplaçant vers la gauche jusqu'à ce qu'il soit à 0. Faites un x+=ypour définir x = m, puis continuez à doubler x, la première fois x+=yet ensuite x+=x.
Quoi qu'il en asoit, il faut des 16-adécalages xpour obtenir y=met des adécalages supplémentaires pour réinitialiser x=0. Un autre achangement de xse produira après x=m. Donc, un total de 16+adécalages est utilisé. Il y a jusqu'à des 16-abits qui doivent être définis pour obtenir m, et chacun d'entre eux en prendra un y+=x. Enfin, nous avons besoin d'une étape supplémentaire x=0pour le régler sur m x+=y,. Il faut donc au maximum 33 étapes pour obtenir un nombre pair.
Vous pouvez, bien sûr, généraliser cela à n'importe quel registre de taille, auquel cas il faut toujours au plus 2n-1et 2n+1ops pour les nentiers impairs et pairs, respectivement.
L'optimalité
Cet algorithme produit un programme qui est presque optimal (c'est-à-dire dans 2n+2si nest le nombre minimum d'étapes) pour les nombres impairs. Pour un nombre impair donné n, si le me bit est le premier 1, alors tout programme prend au moins des métapes pour arriver à x=nou y=n, puisque l'opération qui augmente le plus rapidement les valeurs des registres est x+=xou y+=y(c'est-à-dire des doublons) et il faut des mdoublons pour arriver à le me bit de 1. Puisque cet algorithme prend au plus des 2métapes (au plus deux par doublement, une pour le décalage et une y+=x), tout nombre impair est représenté de façon presque optimale.
Les nombres pairs ne sont pas aussi bons, car il utilise toujours 16 opérations pour réinitialiser xavant toute autre chose, et 8, par exemple, peuvent être atteints en 5 étapes.
Fait intéressant, l'algorithme ci-dessus n'utilise jamais y+=ydu tout, car il yest toujours maintenu impair. Dans ce cas, il peut en fait trouver le programme le plus court pour l'ensemble restreint de seulement 3 opérations.
Essai
# 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()
J'ai écrit un test simple pour vérifier que ma solution produit effectivement des résultats corrects, et ne dépasse jamais 33 étapes, pour toutes les entrées valides ( 0 <= n < 65536).
De plus, j'ai essayé de faire une analyse empirique pour comparer la sortie de ma solution aux sorties optimales - cependant, il s'avère que la recherche en largeur d'abord est trop inefficace pour obtenir la longueur de sortie minimale pour chaque entrée valide n. Par exemple, l'utilisation de BFS pour rechercher la sortie de n = 65535ne se termine pas dans un laps de temps raisonnable. Néanmoins, je suis parti bfs()et je suis ouvert aux suggestions.
J'ai cependant testé ma propre solution contre @ CChak (implémentée ici en Python U). Je m'attendais à ce que le mien fasse pire, car il est considérablement inefficace pour les petits nombres pairs, mais en moyenne sur toute la plage de deux manières, le mien a produit une production de longueur moyenne de 10,8% à 12,3% plus courte. Je pensais que cela était peut-être dû à une meilleure efficacité de ma propre solution sur les nombres impairs, donc Vutilise le mien sur les nombres impairs et @ CChak sur les nombres pairs, mais se Vsitue entre les deux (environ 10% plus court que U, 3% plus long que S).
x+=xlégal que s'ilxest pair? Aussi pour le programme le plus court, je pense que quelque chose comme BFS pourrait fonctionner.