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)
Saya tidak dapat menjamin ini tetap dalam 2x program optimal untuk angka genap, tetapi efisien. Untuk semua input yang valid 0 <= n < 65536
, itu pada dasarnya instan, dan menghasilkan program paling banyak 33 instruksi. Untuk ukuran register sewenang-wenang n
(setelah menetapkan konstanta itu), akan membutuhkan O(n)
waktu paling banyak dengan 2n+1
instruksi.
Logika Biner
Angka ganjil n
dapat dihubungi dalam 31 langkah: lakukan y+=x
, dapatkan x,y = 1,1
, dan terus gandakan x
dengan x+=x
(untuk penggandaan pertama x+=y
, karena x
ganjil untuk memulai). x
akan mencapai setiap kekuatan 2 dengan cara ini (itu hanya shift kiri), dan jadi Anda dapat mengatur bit y
menjadi 1 dengan menambahkan kekuatan yang sesuai dari 2. Karena kita menggunakan register 16-bit, dan setiap bit kecuali untuk yang pertama membutuhkan satu penggandaan untuk mencapai dan satu y+=x
untuk ditetapkan, kita mendapatkan maksimal 31 ops.
Setiap bilangan genap n
hanya sejumlah 2, sebut saja a
, dikalikan dengan angka ganjil, sebut saja m
; yaitu n = 2^a * m
, atau setara n = m << a
,. Gunakan proses di atas untuk mendapatkan m
, lalu atur ulang x
dengan menggeser ke kiri sampai menjadi 0. Lakukan x+=y
untuk mengatur x = m
, dan kemudian lanjutkan untuk menggandakan x
, pertama kali menggunakan x+=y
dan kemudian menggunakan x+=x
.
Apa pun a
itu, dibutuhkan 16-a
pergeseran x
untuk mendapatkan y=m
dan a
pergeseran tambahan untuk mengatur ulang x=0
. a
Pergeseran lain x
akan terjadi setelah x=m
. Jadi total 16+a
shift digunakan. Ada 16-a
bit hingga yang perlu diatur untuk mendapatkan m
, dan masing-masing akan mengambil satu y+=x
. Akhirnya kita membutuhkan langkah tambahan kapan x=0
harus mengaturnya ke m x+=y
,. Jadi dibutuhkan paling banyak 33 langkah untuk mendapatkan angka genap.
Anda dapat, tentu saja, menggeneralisasi ini ke register ukuran apa pun, dalam hal ini selalu dibutuhkan paling banyak 2n-1
dan 2n+1
ops untuk n
bilangan bulat ganjil dan genap , masing-masing.
Optimalitas
Algoritma ini menghasilkan program yang mendekati optimal (yaitu di dalam 2n+2
jika n
adalah jumlah minimum langkah) untuk angka ganjil. Untuk bilangan ganjil tertentu n
, jika m
bit ke-1 adalah yang terdepan, maka setiap program mengambil setidaknya m
langkah untuk sampai ke , x=n
atau y=n
karena operasi yang meningkatkan nilai register paling cepat adalah x+=x
atau y+=y
(yaitu penggandaan) dan dibutuhkan m
penggandaan untuk mencapai yang m
bit th dari 1. Sejak algoritma ini memakan waktu paling 2m
langkah (paling banyak dua per dua kali lipat, satu untuk pergeseran dan satu y+=x
), setiap ganjil diwakili dekat-optimal.
Bahkan angkanya tidak begitu baik, karena selalu menggunakan 16 ops untuk mengatur ulang x
sebelum yang lain, dan 8, misalnya, dapat dicapai dalam 5 langkah.
Menariknya, algoritma di atas tidak pernah menggunakan y+=y
sama sekali, karena y
selalu dibuat aneh. Dalam hal ini, ia sebenarnya dapat menemukan program terpendek untuk rangkaian terbatas hanya 3 operasi.
Pengujian
# 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()
Saya menulis tes sederhana untuk memeriksa apakah solusi saya memang menghasilkan hasil yang benar, dan tidak pernah melewati 33 langkah, untuk semua input yang valid ( 0 <= n < 65536
).
Selain itu, saya mencoba melakukan analisis empiris untuk membandingkan output solusi saya terhadap output optimal - namun, ternyata pencarian pertama yang terlalu luas tidak efisien untuk mendapatkan panjang output minimum untuk setiap input yang valid n
. Misalnya, menggunakan BFS untuk menemukan output n = 65535
tidak berakhir dalam jumlah waktu yang wajar. Meskipun demikian, saya telah meninggalkan bfs()
dan terbuka untuk saran.
Saya lakukan, bagaimanapun, menguji solusi saya sendiri terhadap @ CChak (diimplementasikan di Python di sini sebagai U
). Saya perkirakan tambang saya akan menjadi lebih buruk, karena secara drastis tidak efisien untuk angka genap yang lebih kecil, tetapi rata-rata di seluruh jajaran dalam dua cara, tambang menghasilkan keluaran panjang rata-rata 10,8% hingga 12,3% lebih pendek. Saya pikir mungkin ini karena efisiensi yang lebih baik dari solusi saya sendiri pada angka ganjil, jadi V
gunakan milik saya pada angka ganjil dan @ CChak pada angka genap, tetapi V
ada di antara (sekitar 10% lebih pendek dari U
, 3% lebih lama dari S
).
x+=x
hanya sah jikax
adil? Juga untuk program terpendek saya pikir sesuatu seperti BFS bisa berfungsi.