Con tră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)
Tôi không thể đảm bảo chương trình này nằm trong phạm vi gấp đôi chương trình tối ưu cho các số chẵn, nhưng nó hiệu quả. Đối với tất cả các đầu vào hợp lệ 0 <= n < 65536, về cơ bản là tức thời và tạo ra một chương trình gồm tối đa 33 hướng dẫn. Đối với kích thước thanh ghi tùy ý n(sau khi sửa hằng số đó), sẽ mất nhiều O(n)thời gian với hầu hết các 2n+1hướng dẫn.
Một số logic nhị phân
Bất kỳ số lẻ nào ncũng có thể đạt được trong vòng 31 bước: làm y+=x, nhận x,y = 1,1, và sau đó tiếp tục nhân đôi xvới x+=x(đối với lần nhân đôi đầu tiên x+=y, vì xbắt đầu là số lẻ). xsẽ đạt mọi công suất của 2 theo cách này (chỉ là dịch chuyển trái) và vì vậy bạn có thể đặt bất kỳ bit nào ythành 1 bằng cách thêm công suất tương ứng là 2. Vì chúng tôi đang sử dụng các thanh ghi 16 bit và mỗi bit ngoại trừ lần đầu tiên phải tăng gấp đôi để đạt được và một lần y+=xđể thiết lập, chúng tôi nhận được tối đa 31 ops.
Bất kỳ số chẵn nào nchỉ là một số lũy thừa của 2, gọi nó a, nhân một số lẻ, gọi nó m; ví dụ n = 2^a * m, hoặc tương đương, n = m << a. Sử dụng quy trình trên để nhận m, sau đó đặt lại xbằng cách dịch chuyển sang trái cho đến khi 0. Thực hiện x+=yđể đặt x = mvà sau đó tiếp tục tăng gấp đôi x, lần đầu tiên sử dụng x+=yvà sau đó sử dụng x+=x.
Dù alà gì đi nữa , phải 16-athay đổi xđể có được y=mvà một aca bổ sung để thiết lập lại x=0. Một asự thay đổi khác xsẽ xảy ra sau đó x=m. Vì vậy, tổng số 16+aca được sử dụng. Có tối đa 16-abit cần được thiết lập để có được m, và mỗi bit sẽ lấy một bit y+=x. Cuối cùng, chúng ta cần thêm một bước khi x=0đặt nó thành m , x+=y. Vì vậy, phải mất tối đa 33 bước để có được bất kỳ số chẵn nào.
Tất nhiên, bạn có thể khái quát hóa điều này cho bất kỳ thanh ghi kích thước nào, trong trường hợp đó, nó luôn luôn chiếm tối đa 2n-1và 2n+1ops cho các nsố nguyên lẻ và chẵn , tương ứng.
Sự tối ưu
Thuật toán này tạo ra một chương trình gần tối ưu (tức là trong vòng 2n+2nếu nlà số bước tối thiểu) cho các số lẻ. Đối với một số lẻ nhất định n, nếu mbit thứ 1 là số 1, thì bất kỳ chương trình nào cũng phải thực hiện ít nhất mcác bước để đến x=nhoặc y=n, vì hoạt động làm tăng giá trị của các thanh ghi nhanh nhất là x+=xhoặc y+=y(tức là nhân đôi) và phải mnhân đôi để có được các mbit thứ từ 1. Kể từ khi thuật toán này mất ít nhất 2mbước (ít nhất hai phần tăng gấp đôi, một cho sự thay đổi và một y+=x), bất kỳ số lẻ được thể hiện gần như tối ưu.
Các số chẵn không quá tốt, vì nó luôn sử dụng 16 op để đặt lại xtrước bất kỳ thứ gì khác, và 8, chẳng hạn, có thể đạt được trong vòng 5 bước.
Thật thú vị, thuật toán trên không bao giờ sử dụng y+=ycả, vì yluôn luôn được giữ lẻ. Trong trường hợp đó, nó thực sự có thể tìm thấy chương trình ngắn nhất cho bộ hạn chế chỉ có 3 thao tác.
Kiểm tra
# 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()
Tôi đã viết một thử nghiệm đơn giản để kiểm tra xem giải pháp của tôi có thực sự tạo ra kết quả chính xác hay không và không bao giờ vượt quá 33 bước đối với tất cả các đầu vào hợp lệ ( 0 <= n < 65536).
Ngoài ra, tôi đã cố gắng phân tích theo kinh nghiệm để so sánh đầu ra của giải pháp của tôi với đầu ra tối ưu - tuy nhiên, hóa ra việc tìm kiếm đầu tiên quá kém hiệu quả để có được độ dài đầu ra tối thiểu cho mỗi đầu vào hợp lệ n. Ví dụ: sử dụng BFS để tìm đầu ra n = 65535không kết thúc trong một khoảng thời gian hợp lý. Tuy nhiên, tôi đã để lại bfs()và mở để đề xuất.
Tuy nhiên, tôi đã thử nghiệm giải pháp của riêng tôi chống lại @ CChak's (được triển khai bằng Python ở đây như U). Tôi dự đoán của tôi sẽ làm tồi tệ hơn, vì nó không hiệu quả đối với các số chẵn nhỏ hơn, nhưng tính trung bình trên toàn phạm vi theo hai cách, tôi đã tạo ra sản lượng có độ dài trung bình ngắn hơn 10,8% đến 12,3%. Tôi nghĩ có lẽ điều này là do hiệu quả tốt hơn từ giải pháp của riêng tôi đối với các số lẻ, vì vậy Vsử dụng của tôi cho các số lẻ và @ CChak trên các số chẵn, nhưng Vở giữa (ngắn hơn khoảng 10%, dài hơn U3% so với S).
x+=xchỉ hợp pháp nếuxlà thậm chí? Ngoài ra đối với chương trình ngắn nhất tôi nghĩ một cái gì đó như BFS có thể hoạt động.