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+1
hướng dẫn.
Một số logic nhị phân
Bất kỳ số lẻ nào n
cũ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 x
với x+=x
(đối với lần nhân đôi đầu tiên x+=y
, vì x
bắt đầu là số lẻ). x
sẽ đạ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 y
thà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 n
chỉ 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 x
bằng cách dịch chuyển sang trái cho đến khi 0. Thực hiện x+=y
để đặt x = m
và sau đó tiếp tục tăng gấp đôi x
, lần đầu tiên sử dụng x+=y
và sau đó sử dụng x+=x
.
Dù a
là gì đi nữa , phải 16-a
thay đổi x
để có được y=m
và một a
ca bổ sung để thiết lập lại x=0
. Một a
sự thay đổi khác x
sẽ xảy ra sau đó x=m
. Vì vậy, tổng số 16+a
ca được sử dụng. Có tối đa 16-a
bit 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-1
và 2n+1
ops cho các n
số 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+2
nếu n
là 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 m
bit 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 m
các bước để đến x=n
hoặc y=n
, vì hoạt động làm tăng giá trị của các thanh ghi nhanh nhất là x+=x
hoặc y+=y
(tức là nhân đôi) và phải m
nhân đôi để có được các m
bit thứ từ 1. Kể từ khi thuật toán này mất ít nhất 2m
bướ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 x
trướ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+=y
cả, vì y
luô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 = 65535
khô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 V
sử 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 U
3% so với S
).
x+=x
chỉ hợp pháp nếux
là 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.