파이썬, 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)
나는 이것이 짝수에 대한 최적의 프로그램의 2 배 안에 머무르는 것을 보장 할 수는 없지만 효율적입니다. 유효한 모든 입력 0 <= n < 65536
에 대해 기본적으로 즉각적이며 최대 33 개의 명령어로 구성된 프로그램을 생성합니다. 임의의 레지스터 크기 n
(상수를 고정한 후)의 경우, O(n)
최대 2n+1
명령으로 시간 이 걸립니다 .
일부 이진 논리
모든 홀수는 n
31 단계에서 도달 할 수 있습니다 할 y+=x
, 받고 x,y = 1,1
, 다음 두 배로 유지 x
로 x+=x
(첫 번째 배로 할 수 x+=y
있기 때문에, x
처음부터 홀수). x
이 방법으로 모든 2의 거듭 제곱에 도달합니다 (왼쪽 시프트 일뿐입니다). 따라서 y
해당하는 2의 거듭 제곱을 추가하여 임의의 비트를 1로 설정할 수 있습니다. 16 비트 레지스터를 사용하고 각 비트는 첫 번째는 도달하는 데 두 배가 걸리고 하나는 설정하는 데 걸리므 y+=x
로 최대 31 ops를 얻습니다.
모든 짝수 n
는 2의 거듭 제곱입니다. 호출하고 a
, 홀수를 곱한 다음 호출하십시오 m
. 즉 n = 2^a * m
, 또는 이와 동등하게 n = m << a
. 위의 프로세스를 사용하여을 얻은 m
다음 x
0이 될 때까지 왼쪽으로 이동하여 재설정하십시오. a x+=y
를 설정 x = m
한 다음을 x
사용 x+=y
하고 처음 으로을 계속 사용하여 두 배를 계속 하십시오 x+=x
.
무엇이든이 a
걸리는,이다 16-a
의 변화를 x
얻을 수 y=m
및 추가 a
변화 재설정 x=0
. 후에 또 다른 a
변화 x
가 일어납니다 x=m
. 따라서 총 16+a
교대가 사용됩니다. 16-a
얻도록 설정해야 할 비트가 m
있으며 각 비트는 1을 갖습니다 y+=x
. 마지막으로 x=0
m,로 설정할 때 추가 단계가 필요합니다 x+=y
. 따라서 짝수를 얻는 데 최대 33 단계가 필요합니다.
물론 이것을 모든 크기 레지스터로 일반화 할 수 있습니다.이 경우 항상 최대 2n-1
및 2n+1
홀수 및 짝수 n
비트 정수가 필요합니다.
최적
이 알고리즘은 홀수에 대해 거의 최적 인 (즉 , 최소 단계 수인 2n+2
경우 n
) 프로그램을 생성 합니다. 주어진 홀수 들어 n
경우 m
번째 비트가 1 선도하고있는 프로그램은 적어도 소요 m
단계에 도착 x=n
하거나 y=n
, 빠른 레지스터 "의 값이 증가되는 동작 이후 x+=x
또는 y+=y
(즉, 세포 분열) 그리고 필요 m
에 도착 배화를 m
이 알고리즘은 이후부터 1 번째 비트는 기껏 얻어 2m
단계 (최대 두 배마다, 변속 용, 하나는 y+=x
임의의 홀수 거의 최적으로 표시된다).
숫자는 그다지 좋지 않습니다. 왜냐하면 항상 16 ops를 사용하여 x
다른 것보다 먼저 재설정 하고 예를 들어 5 단계 내에 8에 도달 할 수 있기 때문입니다.
흥미롭게도 위의 알고리즘 은 항상 사용하지 y+=y
않기 때문에 전혀 사용하지 않습니다 y
. 이 경우 실제로 3 개의 제한된 조작 세트에 대해 가장 짧은 프로그램을 찾을 수 있습니다.
테스팅
# 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()
솔루션이 실제로 올바른 결과를 생성하고 모든 유효한 입력에 대해 33 단계를 넘지 않는지 확인하는 간단한 테스트를 작성했습니다 ( 0 <= n < 65536
).
또한 솔루션의 출력을 최적의 출력과 비교하기 위해 경험적 분석을 시도했지만 너비 우선 검색은 모든 유효한 입력에 대한 최소 출력 길이를 얻기에는 너무 비효율적 n
입니다. 예를 들어, BFS를 사용하여 출력을 찾는 n = 65535
것이 적절한 시간 내에 종료되지 않습니다. 그럼에도 불구하고 나는 떠났고 bfs()
제안에 개방적입니다.
그러나 @CChak에 대해 내 솔루션을 테스트했습니다 (Python에서로 구현 됨 U
). 작은 짝수의 경우 비효율적으로 비효율적이기 때문에 광산이 더 나빠질 것으로 예상했지만 두 가지 방법으로 전체 범위에 걸쳐 평균적으로 10.8 %에서 12.3 % 더 짧은 길이의 출력을 생산했습니다. 나는 이것이 홀수에 대한 내 자신의 솔루션에서 더 나은 효율성으로 인한 것이라고 생각했기 때문에 홀수에 V
마이닝을 사용하고 짝수에 @CChak을 사용하지만 V
사이에 있습니다 (약 10 %보다 짧고 U
, 3 % 더 큼 S
).
x+=x
합법적 인 경우에만 합법적x
입니까? 또한 가장 짧은 프로그램의 경우 BFS와 같은 것이 효과가 있다고 생각합니다.