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+1
instructions.
Un peu de logique binaire
N'importe quel nombre impair n
peut être atteint en 31 étapes: faire y+=x
, obtenir x,y = 1,1
, puis continuer à doubler x
avec x+=x
(pour le premier doublement, faire x+=y
, car il x
est impair pour commencer). x
atteindra 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 y
pour ê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+=x
pour mettre, nous obtenons un maximum de 31 ops.
Tout nombre pair n
n'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+=y
pour définir x = m
, puis continuez à doubler x
, la première fois x+=y
et ensuite x+=x
.
Quoi qu'il en a
soit, il faut des 16-a
décalages x
pour obtenir y=m
et des a
décalages supplémentaires pour réinitialiser x=0
. Un autre a
changement de x
se produira après x=m
. Donc, un total de 16+a
décalages est utilisé. Il y a jusqu'à des 16-a
bits 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=0
pour 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-1
et 2n+1
ops pour les n
entiers impairs et pairs, respectivement.
L'optimalité
Cet algorithme produit un programme qui est presque optimal (c'est-à-dire dans 2n+2
si n
est le nombre minimum d'étapes) pour les nombres impairs. Pour un nombre impair donné n
, si le m
e bit est le premier 1, alors tout programme prend au moins des m
étapes pour arriver à x=n
ou y=n
, puisque l'opération qui augmente le plus rapidement les valeurs des registres est x+=x
ou y+=y
(c'est-à-dire des doublons) et il faut des m
doublons pour arriver à le m
e 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 x
avant toute autre chose, et 8, par exemple, peuvent être atteints en 5 étapes.
Fait intéressant, l'algorithme ci-dessus n'utilise jamais y+=y
du tout, car il y
est 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 = 65535
ne 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 V
utilise le mien sur les nombres impairs et @ CChak sur les nombres pairs, mais se V
situe entre les deux (environ 10% plus court que U
, 3% plus long que S
).
x+=x
légal que s'ilx
est pair? Aussi pour le programme le plus court, je pense que quelque chose comme BFS pourrait fonctionner.