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)
我不能保证这是偶数最优程序的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个操作。
任何偶数n
仅是2的幂,称其为a
乘以奇数,即为m
;即n = 2^a * m
或等效地n = m << a
。使用上面的过程来获取m
,然后x
通过左移使其重置为0,x+=y
进行设置x = m
。进行设置,然后继续加倍x
,第一次使用x+=y
,然后使用x+=x
。
不管a
是什么,都需要16-a
转移的x
时间y=m
和a
重置的时间x=0
。之后将发生另一种a
变化。因此,总共使用了班次。最多需要设置一些位,而每个位都需要一个。最后,当将其设置为m,时,我们需要执行一个额外的步骤。因此,最多需要33个步骤才能获得偶数。x
x=m
16+a
16-a
m
y+=x
x=0
x+=y
当然,您可以将其通用化为任何大小的寄存器,在这种情况下,它总是最多占用奇数位和偶数位整数的2n-1
和2n+1
ops n
。
最优性
该算法生成的程序对于奇数几乎是最佳的(即,2n+2
如果n
在最小步数之内)。对于给定的奇数n
,如果第m
k位为前导1,则任何程序都至少需要m
走几步才能到达x=n
或y=n
,因为最快增加寄存器值的操作是x+=x
或y+=y
(即加倍),并且需要m
加倍才能达到m
从1开始的第th个比特。由于此算法最多执行2m
步骤(每加倍最多执行两个步骤,因此移位最多执行一个步骤,而对则执行一次y+=x
),因此任何奇数都将以接近最佳的方式表示。
偶数也不是那么好,因为它总是x
在其他任何事情之前都要使用16个操作进行重置,例如,可以在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()
我编写了一个简单的测试,以检查我的解决方案对于所有有效输入(0 <= n < 65536
)的确产生正确的结果,并且从未超过33个步骤。
此外,我尝试进行实证分析,以将我的解决方案的输出与最佳输出进行比较-但是,事实证明,广度优先搜索效率低下,无法为每个有效输入获取最小输出长度n
。例如,使用BFS查找输出n = 65535
不会在合理的时间内终止。不过,我已经离开bfs()
并愿意提出建议。
但是,我确实针对@CChak(在此处以Python实现U
)测试了自己的解决方案。我预计我的情况会更糟,因为对于偶数较小的情况,它的效率极低,但是在整个范围内以两种方式进行平均,矿山的产出长度平均缩短了10.8%至12.3%。我以为这可能是由于我自己的奇数解决方案效率更高,所以V
在奇数上使用mine,在偶数上使用@CChak,但是V
介于两者之间(比约短10%,比约U
长3%S
)。
x+=x
只有x
偶数才合法?同样对于最短的程序,我认为类似BFS的东西也可以工作。