পার্ল - 116 বাইট 87 বাইট (নীচে আপডেট দেখুন)
#!perl -p
$.<<=1,$_>>=2until$_&3;
{$n=$_;@a=map{$n-=$a*($a-=$_%($b=1|($a=0|sqrt$n)>>1));$_/=$b;$a*$.}($j++)x4;$n&&redo}
$_="@a"
শেবাংকে এক বাইট হিসাবে গণনা করা হচ্ছে, অনুভূমিক বিগ্রহের জন্য নতুন লাইন যুক্ত হয়েছে।
একটি সংমিশ্রণ কোড-গল্ফ দ্রুততম কোড জমা দেওয়ার কিছু।
গড় (সবচেয়ে খারাপ?) কেস জটিলতায় ও (লগ এন) ও (এন 0.07 ) বলে মনে হচ্ছে । আমি কিছুই পাইনি 0.001 এর চেয়ে ধীরগতিতে রান চালিয়েছি এবং 900000000 - 999999999 থেকে পুরো পরিসরটি পরীক্ষা করেছি । আপনি যদি এর চেয়ে উল্লেখযোগ্যভাবে বেশি সময় নেয় এমন কোনও কিছু খুঁজে পান, ~ 0.1s বা তার বেশি, দয়া করে আমাকে জানান।
নমুনা ব্যবহার
$ echo 123456789 | timeit perl four-squares.pl
11110 157 6 2
Elapsed Time: 0:00:00.000
$ echo 1879048192 | timeit perl four-squares.pl
32768 16384 16384 16384
Elapsed Time: 0:00:00.000
$ echo 999950883 | timeit perl four-squares.pl
31621 251 15 4
Elapsed Time: 0:00:00.000
এগুলির মধ্যে চূড়ান্ত দুটিটি অন্যান্য জমা দেওয়ার ক্ষেত্রে খারাপ পরিস্থিতি বলে মনে হচ্ছে। উভয় উদাহরণেই দেখানো সমাধানটি বেশ আক্ষরিক অর্থেই প্রথম পরীক্ষা করা হয়েছে। কারণ 123456789
এটি দ্বিতীয়।
আপনি যদি মানগুলির একটি ব্যাপ্তি পরীক্ষা করতে চান তবে আপনি নিম্নলিখিত স্ক্রিপ্টটি ব্যবহার করতে পারেন:
use Time::HiRes qw(time);
$t0 = time();
# enter a range, or comma separated list here
for (1..1000000) {
$t1 = time();
$initial = $_;
$j = 0; $i = 1;
$i<<=1,$_>>=2until$_&3;
{$n=$_;@a=map{$n-=$a*($a-=$_%($b=1|($a=0|sqrt$n)>>1));$_/=$b;$a*$i}($j++)x4;$n&&redo}
printf("%d: @a, %f\n", $initial, time()-$t1)
}
printf('total time: %f', time()-$t0);
কোনও ফাইলে পাইপ দেওয়ার সময় সেরা। পরিসীমাটি 1..1000000
আমার কম্পিউটারে সেকেন্ডে প্রায় 14 সেকেন্ডে লাগে (প্রতি সেকেন্ডে 71000 মান) এবং ও (লগ এন) গড় জটিলতার 999000000..1000000000
সাথে সামঞ্জস্য রেখে পরিসীমাটি প্রায় 20 সেকেন্ড (প্রতি সেকেন্ডে 50000 মান) লাগে ।
হালনাগাদ
সম্পাদনা : দেখা যাচ্ছে যে এই অ্যালগরিদমটি মানসিক ক্যালকুলেটর দ্বারা কমপক্ষে এক শতাব্দী ধরে ব্যবহার করা হয়েছে এমন একটির সাথে খুব মিল ।
প্রাথমিকভাবে পোস্ট করার পর থেকে, আমি ১.১০০০০০০০০০০ থেকে শুরু করে প্রতিটি মান পরীক্ষা করেছি । 'নিকৃষ্টতম ঘটনা' আচরণটি 699731569 মান দ্বারা প্রদর্শিত হয়েছিল , যা কোনও সমাধানে পৌঁছানোর আগে সর্বমোট 190 টি সংমিশ্রণ পরীক্ষা করেছিল। আপনি যদি 190 কে একটি ছোট ধ্রুবক হিসাবে বিবেচনা করেন - এবং আমি অবশ্যই করি - প্রয়োজনীয় পরিসরে সবচেয়ে খারাপ ক্ষেত্রে আচরণ ও (1) হিসাবে বিবেচনা করা যেতে পারে । এটি, একটি দৈত্য টেবিল থেকে সমাধানটি তত দ্রুত এবং সম্ভবত সম্ভবত বেশ দ্রুত।
আরেকটি জিনিস যদিও। ১৯০ টি পুনরাবৃত্তির পরে , ১৪৪৪০০ এর চেয়ে বড় কিছু এটি প্রথম পাসের বাইরেও তৈরি করতে পারেনি। প্রস্থের প্রথম ট্র্যাভারসালের জন্য যুক্তিটি মূল্যহীন - এটি এমনকি ব্যবহৃত হয় না। উপরের কোডটি বেশ কিছুটা ছোট করা যায়:
#!perl -p
$.*=2,$_/=4until$_&3;
@a=map{$=-=$%*($%=$=**.5-$_);$%*$.}$j++,(0)x3while$=&&=$_;
$_="@a"
যা কেবল অনুসন্ধানের প্রথম পাসটি করে। আমাদের নিশ্চিত করতে হবে যে 144400 এর নীচে এমন কোনও মান নেই যা দ্বিতীয় পাসের প্রয়োজন, যদিও:
for (1..144400) {
$initial = $_;
# reset defaults
$.=1;$j=undef;$==60;
$.*=2,$_/=4until$_&3;
@a=map{$=-=$%*($%=$=**.5-$_);$%*$.}$j++,(0)x3while$=&&=$_;
# make sure the answer is correct
$t=0; $t+=$_*$_ for @a;
$t == $initial or die("answer for $initial invalid: @a");
}
সংক্ষেপে, 1..1000000000 ব্যাপ্তির জন্য , একটি অবিচ্ছিন্ন সময় সমাধান উপস্থিত রয়েছে এবং আপনি এটি সন্ধান করছেন।
আপডেট আপডেট
@ ডেনিস এবং আমি এই অ্যালগরিদমটিতে বেশ কয়েকটি উন্নতি করেছি। আপনি যদি আগ্রহী হন তবে আপনি নীচের মন্তব্যে অগ্রগতি এবং পরবর্তী আলোচনা অনুসরণ করতে পারেন। প্রয়োজনীয় পরিসরের জন্য পুনরাবৃত্তির গড় সংখ্যা মাত্র 4 এর নিচে নেমে 1.229 এ নেমেছে এবং 1..1000000000 এর জন্য সমস্ত মান পরীক্ষা করার জন্য প্রয়োজনীয় সময়টি 18 মি 54 এর থেকে উন্নত করে 2 মি 41 এ পরিণত হয়েছে। সবচেয়ে খারাপ ক্ষেত্রে আগে 190 পুনরাবৃত্তি প্রয়োজন ; সবচেয়ে খারাপ ক্ষেত্রে এখন 854382778 , প্রয়োজন কেবল 21 ।
চূড়ান্ত পাইথন কোডটি নিম্নলিখিত:
from math import sqrt
# the following two tables can, and should be pre-computed
qqr_144 = set([ 0, 1, 2, 4, 5, 8, 9, 10, 13,
16, 17, 18, 20, 25, 26, 29, 32, 34,
36, 37, 40, 41, 45, 49, 50, 52, 53,
56, 58, 61, 64, 65, 68, 72, 73, 74,
77, 80, 81, 82, 85, 88, 89, 90, 97,
98, 100, 101, 104, 106, 109, 112, 113, 116,
117, 121, 122, 125, 128, 130, 133, 136, 137])
# 10kb, should fit entirely in L1 cache
Db = []
for r in range(72):
S = bytearray(144)
for n in range(144):
c = r
while True:
v = n - c * c
if v%144 in qqr_144: break
if r - c >= 12: c = r; break
c -= 1
S[n] = r - c
Db.append(S)
qr_720 = set([ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121,
144, 145, 160, 169, 180, 196, 225, 241, 244, 256, 265, 289,
304, 324, 340, 361, 369, 385, 400, 409, 436, 441, 481, 484,
496, 505, 529, 544, 576, 580, 585, 601, 625, 640, 649, 676])
# 253kb, just barely fits in L2 of most modern processors
Dc = []
for r in range(360):
S = bytearray(720)
for n in range(720):
c = r
while True:
v = n - c * c
if v%720 in qr_720: break
if r - c >= 48: c = r; break
c -= 1
S[n] = r - c
Dc.append(S)
def four_squares(n):
k = 1
while not n&3:
n >>= 2; k <<= 1
odd = n&1
n <<= odd
a = int(sqrt(n))
n -= a * a
while True:
b = int(sqrt(n))
b -= Db[b%72][n%144]
v = n - b * b
c = int(sqrt(v))
c -= Dc[c%360][v%720]
if c >= 0:
v -= c * c
d = int(sqrt(v))
if v == d * d: break
n += (a<<1) - 1
a -= 1
if odd:
if (a^b)&1:
if (a^c)&1:
b, c, d = d, b, c
else:
b, c = c, b
a, b, c, d = (a+b)>>1, (a-b)>>1, (c+d)>>1, (c-d)>>1
a *= k; b *= k; c *= k; d *= k
return a, b, c, d
এটি দুটি প্রাক-গণিত সংশোধন টেবিল ব্যবহার করে, একটি 10kb আকারের, অন্যটি 253kb। উপরের কোডটিতে এই টেবিলগুলির জন্য জেনারেটর ফাংশন অন্তর্ভুক্ত রয়েছে, যদিও এগুলি সম্ভবত সংকলনের সময় গণনা করা উচিত।
আরও পরিমিত আকারের সংশোধন টেবিল সহ একটি সংস্করণ এখানে পাওয়া যাবে: http://codepad.org/1ebJC2OV এই সংস্করণটির গড় প্রতি গড় 1.620 পুনরাবৃত্তি প্রয়োজন, 38 টির সবচেয়ে খারাপ ক্ষেত্রে এবং পুরো পরিসীমাটি প্রায় 3 মি 21 সেকেন্ডে চলে runs মডুলোর পরিবর্তে বি সংশোধনের and
জন্য বিটওয়াইজ ব্যবহার করে কিছুটা সময় তৈরি করা হয় ।
উন্নতি
এমনকি মানগুলি বিজোড় মানগুলির চেয়ে সমাধান তৈরি করার সম্ভাবনা বেশি।
পূর্ববর্তী নোটগুলির সাথে যুক্ত মানসিক গণনা নিবন্ধটি উল্লেখ করেছে যে, চারটি সমস্ত কারণকে অপসারণের পরে, পচে যাওয়া মানটি যদি সমান হয় তবে এই মানটিকে দুটি দ্বারা ভাগ করা যায় এবং সমাধানটি পুনর্গঠিত হয়:
যদিও এটি মানসিক গণনার জন্য অর্থবোধ করতে পারে (ছোট মানগুলি গণনা করা সহজ হয়) তবে এটি অ্যালগোরিদমিকভাবে খুব বেশি বোঝায় না। আপনি যদি 256 এলোমেলো 4- টিপলস নিয়ে যান এবং 8 টি বর্গাকার মডুলোর 8 যোগফলটি পরীক্ষা করেন , আপনি দেখতে পাবেন যে 1 , 3 , 5 এবং 7 এর মান প্রতিটি গড়ে 32 বার পৌঁছেছে । তবে, 2 এবং 6 এর মানগুলি প্রতিটি 48 বার পৌঁছেছে । বিজোড় মানগুলিকে 2 দ্বারা গুণিত করা , 33% কম পুনরাবৃত্তিতে একটি সমাধান খুঁজে পাবে । পুনর্গঠন নিম্নলিখিত:
যত্ন নেওয়া দরকার যে ক এবং খ সমান সমতা রয়েছে তেমনি সি এবং ডিও রয়েছে তবে যদি কোনও সমাধান পাওয়া যায় তবে একটি যথাযথ অর্ডারের অস্তিত্বের নিশ্চয়তা দেওয়া হয়।
অসম্ভব পথগুলি পরীক্ষা করার দরকার নেই।
দ্বিতীয় মান, খ নির্বাচন করার পরে, কোনও প্রদত্ত মডুলোর সম্ভাব্য চতুর্ভুজীয় অবশিষ্টাংশ প্রদত্ত সমাধানের পক্ষে ইতিমধ্যে এটি অসম্ভব হয়ে উঠতে পারে। যাইহোক যাইহোক পরীক্ষা করা, বা পরবর্তী পুনরাবৃত্তির দিকে যাওয়ার পরিবর্তে, খ এর মান সবচেয়ে কম পরিমাণে হ্রাস করে 'সংশোধন' করা যেতে পারে যা সম্ভবত সমাধানের দিকে নিয়ে যেতে পারে। দুটি সংশোধন সারণি এই মানগুলিকে সঞ্চয় করে, একটি খ এর জন্য এবং অন্যটি সি এর জন্য । উচ্চতর মডুলো (আরও সঠিকভাবে, তুলনামূলকভাবে কম চতুষ্কোণীয় অবশিষ্টাংশ সহ একটি মডিউল ব্যবহার করা) ব্যবহারের ফলে আরও উন্নতি হবে। মান একটি কোনো সংশোধনের প্রয়োজন হয় না; সমান হতে n এর সংশোধন করে , সমস্ত মানএকটি বৈধ হয়।