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)
Bunun çift sayılar için en uygun programın 2 katı içinde kalmasını garanti edemem, ancak etkilidir. Tüm geçerli girdiler için 0 <= n < 65536
, esasen anlıktır ve en fazla 33 talimatdan oluşan bir program oluşturur. Rastgele bir kayıt boyutu için n
(bu sabiti düzelttikten sonra), O(n)
en fazla 2n+1
talimatla zaman alacaktır .
Bazı İkili Mantık
Herhangi tek sayı n
31 adımda ulaşılabilir: do y+=x
, alma x,y = 1,1
ve daha sonra iki katına tutmak x
ile x+=x
(ilk iki katına yapmak x+=y
beri x
başlamak için garip). x
2'nin her gücüne bu şekilde ulaşacaktır (sadece bir sola kaydırma) ve böylece y
2'nin ilgili gücünü ekleyerek 1 olacak şekilde herhangi bir biti ayarlayabilirsiniz. 16 bitlik kayıtlar kullandığımız ve birincisi ulaşmak için bir katlama ve bir tane y+=x
ayarlamak için en fazla 31 ops alırız.
Herhangi bir çift sayı n
sadece 2'nin bir gücüdür a
, diyelim, tek bir sayı, deyin m
; yani n = 2^a * m
veya eşdeğer olarak n = m << a
. Almak için yukarıdaki işlemi kullanın m
sonra sıfırlama, x
bu 0. yapmak var kadar sol kaydırarak x+=y
setine x = m
ve daha sonra çift devam x
, ilk kez kullanan x+=y
kullanarak sonradan ve x+=x
.
Ne olursa olsun a
Tek gereken, olduğu 16-a
bir vardiya x
almak y=m
ve ek a
vardiya sıfırlamak için x=0
. Sonra başka bir a
kayma x
gerçekleşecek x=m
. Böylece toplam 16+a
vardiya kullanılır. 16-a
Almak için ayarlanması gereken en fazla bit vardır m
ve bunların her biri bir tane alacaktır y+=x
. Sonunda ne zaman x=0
m'ye ayarlayacağımız fazladan bir adıma ihtiyacımız var x+=y
. Bu nedenle, herhangi bir çift sayı elde etmek için en fazla 33 adım gerekir.
Elbette, bunu herhangi bir boyut kaydına genelleştirebilirsiniz, bu durumda sırasıyla en fazla zaman alır 2n-1
ve 2n+1
tek ve çift n
bitli tamsayıları seçer.
Optimalliği
Bu algoritma (içinde, yani yakın optimal olan bir program üretir 2n+2
ise n
tek sayı için adımların en az bir sayı). Belirli bir tek sayı için n
ise, m
inci biraz 1 gelen, daha sonra herhangi bir program, en azından alır m
adımları almak için x=n
ya da y=n
, hızlı sicil değerlerini arttırır olduğu işlem yana x+=x
ya da y+=y
(örneğin, ikiye katlama) ve alır m
almak misli m
Bu algoritma yana 1'den inci bit en alır 2m
adımları (en fazla iki kat başına iki, geçiş için ve bir y+=x
herhangi bir tek sayı yakın en uygun şekilde temsil edilir).
Rakamlar bile o kadar iyi değildir, çünkü x
her şeyden önce sıfırlamak için her zaman 16 op kullanır ve örneğin 8'e 5 adımda ulaşılabilir.
İlginçtir, yukarıdaki algoritma asla hiç kullanmaz y+=y
, çünkü y
her zaman gariptir. Bu durumda, sadece 3 işlemin kısıtlı seti için en kısa programı bulabilir.
Test yapmak
# 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()
Çözümümün gerçekten doğru sonuçlar verdiğini ve tüm geçerli girdiler için hiçbir zaman 33 adımı geçmediğini kontrol etmek için basit bir test yazdım 0 <= n < 65536
.
Buna ek olarak, çözümümün çıktısını optimum çıktılarla karşılaştırmak için ampirik bir analiz yapmaya çalıştım - ancak, ilk genişlik aramasının her geçerli giriş için minimum çıktı uzunluğunu elde etmek için çok verimsiz olduğu ortaya çıktı n
. Örneğin, çıktıyı bulmak için BFS kullanımı n = 65535
makul bir süre içinde sona ermez. Yine de bıraktım bfs()
ve önerilere açıkım.
Ancak, kendi çözümümü @ CChak'lara karşı test ettim (burada Python'da uygulandı U
). Benimkinin daha kötü olmasını bekliyordum, çünkü daha küçük çiftler için büyük ölçüde verimsiz, ancak tüm aralıkta iki şekilde ortalama, benimki ortalama üretimin ortalama% 10.8 ila% 12.3 daha kısa üretti. Belki de bunun tek sayılarda kendi V
çözümümden daha iyi verimden kaynaklandığını düşündüm, bu yüzden tek sayılarda mayın ve çift sayılarda @ CChak kullanıyor, ancak V
arasında (yaklaşık% 10 daha kısa U
,% 3 daha uzun S
).
x+=x
sadece olsax
bile yasaldır ? Ayrıca en kısa program için BFS gibi bir şeyin işe yarayabileceğini düşünüyorum.