পাইথন, 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 এর প্রতিটি পাওয়ার এই পথে পৌঁছে যাবে (এটি কেবলমাত্র একটি বাম শিফট), এবং তাই আপনি 2 y
এর সামর্থ্য যোগ করে 1 হতে কোনও বিট সেট করতে পারেন যেহেতু আমরা 16 বিট রেজিস্টার ব্যবহার করছি, এবং প্রতিটি বিট বাদে প্রথমটির জন্য একটি পৌঁছাতে দ্বিগুণ এবং একটি y+=x
সেট করতে লাগে , আমরা সর্বাধিক 31 ops পাই।
যে কোনও এমনকি সংখ্যার 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
(অর্থাত doublings) এবং এটা লাগে m
পেতে doublings দ্যm
1. থেকে তম বিট এই অ্যালগরিদম যেহেতু সর্বাধিক লাগে 2m
পদক্ষেপ (দ্বিগুন প্রতি সর্বাধিক দুটি, শিফট এবং অন্যটি y+=x
), কোন বিজোড় সংখ্যা প্রতিনিধিত্ব করা হয় কাছাকাছি সন্তোষজনক ভাবে।
এমনকি সংখ্যাগুলিও তেমন ভাল নয়, যেহেতু এটি পুনরায় সেট করতে সর্বদা 16 টি অপ্স ব্যবহার করে x
অন্য কোনও কিছুর আগে করতে। এবং উদাহরণস্বরূপ, 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
। উদাহরণস্বরূপ, এর জন্য আউটপুট সন্ধানের জন্য বিএফএস ব্যবহার করা n = 65535
যুক্তিসঙ্গত পরিমাণে শেষ হয় না। তবুও আমি রেখেছি bfs()
এবং পরামর্শের জন্য উন্মুক্ত।
যাইহোক, আমি @ সিচাকের বিরুদ্ধে আমার নিজস্ব সমাধানটি পরীক্ষা করেছি (পাইথন হিসাবে এখানে প্রয়োগ করা হয়েছে U
)। আমার প্রত্যাশা ছিল খনিটি আরও খারাপ করবে, যেহেতু এটি ছোট এমনকি সংখ্যার জন্য মারাত্মকভাবে অদক্ষ, তবে পুরো পরিসীমা জুড়ে গড়ে দুটি উপায়ে খনিটি দৈর্ঘ্যের আউটপুট উত্পাদন করে গড়ে গড় দশমিক দশমিক 12 থেকে 12.3% কম। আমি ভেবেছিলাম এটি বিজোড় সংখ্যাগুলির জন্য আমার নিজস্ব সমাধান থেকে ভাল দক্ষতার কারণে হয়েছে, সুতরাং V
বিজোড় সংখ্যার উপর খনি এবং @ সিকাক এর সমান সংখ্যায় ব্যবহার করে, তবে V
এর মধ্যে রয়েছে (প্রায় 10% এর চেয়ে কম U
, 3% দীর্ঘ S
)।
x+=x
কেবল বৈধ হলেই কেন বৈধx
? সংক্ষিপ্ততম প্রোগ্রামের জন্যও আমি মনে করি বিএফএস এর মতো কিছু কাজ করতে পারে।