Pitón, 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)
No puedo garantizar que este se mantenga dentro del doble del programa óptimo para números pares, pero es eficiente. Para todas las entradas válidas 0 <= n < 65536, es esencialmente instantáneo y genera un programa de como máximo 33 instrucciones. Para un tamaño de registro arbitrario n(después de corregir esa constante), tomaría O(n)tiempo con, como máximo2n+1 instrucciones.
Alguna lógica binaria
Cualquier número impar nse puede llegar en 31 pasos: hacer y+=x, conseguir x,y = 1,1, y luego seguir doblando xcon x+=x(por primera duplicación hacerlo x+=y, ya que xes extraño para empezar). xalcanzará cada potencia de 2 de esta manera (es solo un desplazamiento a la izquierda), por lo que puede configurar cualquier bit de y1 agregando la potencia correspondiente de 2. Dado que estamos utilizando registros de 16 bits, y cada bit excepto para la primera toma una duplicación para alcanzar y una y+=xpara establecer, obtenemos un máximo de 31 operaciones.
Cualquier número par nes solo una potencia de 2, llámelo a, multiplicado por un número impar, llámelo m; es decir n = 2^a * m, o equivalentemente n = m << a. Use el proceso anterior para obtener m, luego reinicie xdesplazándolo hacia la izquierda hasta que sea 0. Haga un x+=yajuste x = my luego continúe duplicando x, la primera vez usando x+=yy luego usandox+=x .
Sea lo que asea, se necesitan 16-aturnos xpara obtener y=my un aturno adicional para restablecer x=0. Otros acambios de xocurrirán después x=m. Entonces 16+ase usa un total de turnos. Hay hasta 16-abits que deben configurarse para obtener m, y cada uno de ellos tomará uno y+=x. Finalmente, necesitamos un paso adicional x=0para establecerlo en m x+=y,. Por lo tanto, se necesitan como máximo 33 pasos para obtener cualquier número par.
Puede, por supuesto, generalizar esto a cualquier tamaño de registro, en cuyo caso siempre toma como máximo 2n-1y 2n+1opta por nenteros pares e impares , respectivamente.
Óptima
Este algoritmo produce un programa que es casi óptimo (es decir, dentro de 2n+2si nes el número mínimo de pasos) para números impares. Para un número impar dado n, si el mbit th es el primer 1, entonces cualquier programa toma al menos mpasos para llegar a x=no y=n, ya que la operación que aumenta los valores de los registros más rápido es x+=xo y+=y(es decir, duplicaciones) y se necesitan mduplicaciones para llegar a el mth bit de 1. Dado que este algoritmo toma la mayoría de los 2mpasos (a lo sumo dos por duplicación, uno para el turno y uno y+=x), cualquier número impar se representa casi de manera óptima.
Los números pares no son tan buenos, ya que siempre usa 16 operaciones para restablecer xantes que nada, y 8, por ejemplo, se puede alcanzar en 5 pasos.
Curiosamente, el algoritmo anterior nunca usa y+=yen absoluto, ya quey que siempre se mantiene extraño. En ese caso, puede encontrar el programa más corto para el conjunto restringido de solo 3 operaciones.
Pruebas
# 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()
Escribí una prueba simple para verificar que mi solución realmente produce resultados correctos, y nunca supera los 33 pasos, para todas las entradas válidas ( 0 <= n < 65536).
Además, intenté hacer un análisis empírico para comparar la salida de mi solución con las salidas óptimas; sin embargo, resulta que la búsqueda de amplitud es demasiado ineficiente para obtener la longitud mínima de salida para cada entrada válida n. Por ejemplo, el uso de BFS para buscar la salida n = 65535no termina en un período de tiempo razonable. Sin embargo, me he ido bfs()y estoy abierto a sugerencias.
Sin embargo, probé mi propia solución contra la de @ CChak (implementada en Python aquí como U). Esperaba que al mío le iría peor, ya que es drásticamente ineficiente para números pares más pequeños, pero promediado en todo el rango de dos maneras, la mina produjo una producción de longitud en promedio de 10.8% a 12.3% más corta. Pensé que tal vez esto se debía a una mejor eficiencia de mi propia solución en números impares, así que Vusa el mío en números impares y @ CChak en números pares, pero Vestá en el medio (aproximadamente 10% más corto que U, 3% más largo que S).
x+=xlegal solo sixes par? También para el programa más corto, creo que algo como BFS podría funcionar.