पायथन, 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)
मैं यह गारंटी नहीं दे सकता कि 2x के भीतर सबसे अधिक संख्या के लिए इष्टतम कार्यक्रम है, लेकिन यह कुशल है। सभी मान्य इनपुट्स के लिए 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
बदलाव x
होगा x=m
। तो कुल 16+a
शिफ्ट का उपयोग किया जाता है। ऐसे 16-a
बिट्स हैं जिन्हें प्राप्त करने के लिए सेट करने की आवश्यकता है m
, और उनमें से प्रत्येक को एक लेना होगा y+=x
। अंत में हमें एक अतिरिक्त कदम की जरूरत है, जब x=0
इसे मी पर सेट किया जाए x+=y
। इसलिए किसी भी सम संख्या को प्राप्त करने के लिए यह अधिकतम 33 चरणों में होता है।
तुम्हें पता है, निश्चित रूप से, यह किसी भी आकार रजिस्टर, जिस स्थिति में यह हमेशा अधिक से अधिक लेता है सामान्यीकरण कर सकते हैं 2n-1
और 2n+1
अजीब और यहां तक कि के लिए ऑप्स n
-बिट पूर्णांकों, क्रमशः।
optimality
यह एल्गोरिथ्म एक प्रोग्राम का निर्माण करता है जो विषम संख्याओं के लिए लगभग इष्टतम (यानी 2n+2
यदि n
चरणों की न्यूनतम संख्या है)। किसी दिए गए विषम संख्या के लिए n
, अगर m
वें सा है अग्रणी 1, तो किसी भी कार्यक्रम में कम से कम लेता m
चरणों को पाने के लिए x=n
या y=n
, आपरेशन जो रजिस्टरों 'मूल्यों सबसे तेजी से बढ़ जाती है के बाद से x+=x
या y+=y
(यानी दोहरीकरण) है और यह लेता m
करने के लिए दोहरीकरण The m
th bit 1. चूंकि यह एल्गोरिथ्म अधिकांश 2m
चरणों में (अधिकतम दो प्रति दोहरीकरण, एक शिफ्ट और एक के लिए y+=x
) लेता है , किसी भी विषम संख्या का निकट-आशावादी रूप से प्रतिनिधित्व किया जाता है।
यहां तक कि संख्याएं भी इतनी अच्छी नहीं हैं, क्योंकि यह हमेशा x
कुछ भी करने से पहले 16 ऑप्स का उपयोग करता है, और 8, उदाहरण के लिए, 5 चरणों में पहुंचा जा सकता है।
दिलचस्प है, उपरोक्त एल्गोरिथ्म कभी भी उपयोग नहीं करता है 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()
सुझाव के लिए अंदर और बाहर खुला हूँ।
हालाँकि, मैंने @ चीक के खिलाफ अपने स्वयं के समाधान का परीक्षण किया (जैसा कि यहां पायथन में लागू किया गया है U
)। मुझे उम्मीद थी कि मेरा और भी बुरा होगा, क्योंकि यह छोटी संख्याओं के लिए बहुत ही अयोग्य है, लेकिन पूरी तरह से दो तरीकों से औसतन, मेरा उत्पादन औसत 10.8% लंबाई से 12.3% कम है। मैंने सोचा कि शायद यह विषम संख्याओं पर अपने स्वयं के समाधान से बेहतर दक्षता के कारण था, इसलिए V
विषम संख्याओं और @ CChak पर भी संख्याओं पर मेरा उपयोग करता है, लेकिन V
बीच में है (लगभग 10% से कम U
, 3% से अधिक S
)।
x+=x
कानूनी ही क्यों अगरx
यह भी है? इसके अलावा सबसे छोटे कार्यक्रम के लिए मुझे लगता है कि BFS काम कर सकता है।