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 n
se puede llegar en 31 pasos: hacer y+=x
, conseguir x,y = 1,1
, y luego seguir doblando x
con x+=x
(por primera duplicación hacerlo x+=y
, ya que x
es extraño para empezar). x
alcanzará cada potencia de 2 de esta manera (es solo un desplazamiento a la izquierda), por lo que puede configurar cualquier bit de y
1 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+=x
para establecer, obtenemos un máximo de 31 operaciones.
Cualquier número par n
es 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 x
desplazándolo hacia la izquierda hasta que sea 0. Haga un x+=y
ajuste x = m
y luego continúe duplicando x
, la primera vez usando x+=y
y luego usandox+=x
.
Sea lo que a
sea, se necesitan 16-a
turnos x
para obtener y=m
y un a
turno adicional para restablecer x=0
. Otros a
cambios de x
ocurrirán después x=m
. Entonces 16+a
se usa un total de turnos. Hay hasta 16-a
bits que deben configurarse para obtener m
, y cada uno de ellos tomará uno y+=x
. Finalmente, necesitamos un paso adicional x=0
para 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-1
y 2n+1
opta por n
enteros pares e impares , respectivamente.
Óptima
Este algoritmo produce un programa que es casi óptimo (es decir, dentro de 2n+2
si n
es el número mínimo de pasos) para números impares. Para un número impar dado n
, si el m
bit th es el primer 1, entonces cualquier programa toma al menos m
pasos para llegar a x=n
o y=n
, ya que la operación que aumenta los valores de los registros más rápido es x+=x
o y+=y
(es decir, duplicaciones) y se necesitan m
duplicaciones para llegar a el m
th bit de 1. Dado que este algoritmo toma la mayoría de los 2m
pasos (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 x
antes que nada, y 8, por ejemplo, se puede alcanzar en 5 pasos.
Curiosamente, el algoritmo anterior nunca usa y+=y
en 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 = 65535
no 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 V
usa el mío en números impares y @ CChak en números pares, pero V
está en el medio (aproximadamente 10% más corto que U
, 3% más largo que S
).
x+=x
legal solo six
es par? También para el programa más corto, creo que algo como BFS podría funcionar.