مساله: برنامهاي بنويسيد كه بگويد يك عدد كامل است يا خير.
اما عدد كامل چيست؟ به رياضي بازميگرديم، عدد كامل عددي است كه مجموع مقسوم عليههاي سره آن برابر خود عدد باشد.
(مجموع مقسوم عليههاي سره يعني جمع مقسوم عليههاي يك عدد به جز خود عدد مثلا براي عدد 15 داريم 8=5+3+1 مقسوم عليههاي عدد 15 برابر 1 و 3 و5 و 15 هستند)
حال چگونه مقسوم عليههاي يك عدد را به دست بياوريم؟ ميدانيم كه مقسوم عليه عددي است كه وقتي عدد را بر آن تقسيم كنيم خارج قسمت برابر صفر ميشود و اين را هم ميدانيم كه بزرگترين مقسوم عليه يك عدد خود عدد است.
حال بايد يك برنامه بنويسيم كه مقسوم عليههاي يك عدد را پيدا كند، راه ساده اين است كه در يك حلقه كه از 1 شروع ميشود و تا خود عدد ادامه پيدا ميكند، خود عدد را بر انديس حلقه تقسيم كنيم و اگر خارج قسمت برابر صفر شد يعني انديس حلقه برابر مقسوم عليه يك عدد است. كد آن به صورت زير است:
for (int i = 1; i «= number; i++) {
if(number%i == 0)
print i; }
Print i تمام مقسوم عليههاي يك عدد را چاپ ميكند. حالا بياييم قطعه كد بالا را تبديل به قطعه كدي براي حل مساله خودمان بكنيم.
صورت مساله مجموع تمام مقسوم عليههاي سره يك عدد را ميخواهد پس ما بايد مقدار i را با يك متغير ديگري مثلا به اسم sum جمع كنيم، يعني:
for (int i = 1; i «= number; i++) {
if(number%i == 0)
sum+=i; }
اما آيا اين جواب ماست؟ خير، چون خود عدد را نيز جمع ميكند پس شرط پايانپذيري حلقه را به صورتI«number تغيير ميدهيم، يعني براي تمام iهاي كوچكتر از خود عدد. در نهايت مقدار sum را با خود عدد مقايسه ميكنيم اگر برابر بودند يعني عدد كامل است و خروجي مناسب را چاپ ميكنيم.
حال بياييد كد بالا را قدري بهتر كنيم.
مي دانيم هر عدد بر 1 تقسيم پذير است پس مقدار sum را برابر 1 قرار ميدهيم و حلقه را از 2 شروع ميكنيم، اين طوري حلقه ما يك مرحله كمتر اجرا ميشود.
از رياضي به ياد داريم كه مقسومعليهها حالت قرينه دارند يعني مقسوم عليههاي يك عدد تا نصف آن عدد را در نظر بگيريد، مقسوم عليههاي بزرگتر از نصف عدد حاصل تقسيم عدد بر مقسوم عليههاي نصفه اول است. پس ما نيازي نداريم كه هميشه يك حلقه را تا خود عدد ادامه دهيم كافيست كه تا نصف عدد برويم و سپس مقسوم عليههاي عدد را در يك ارائه ذخيره كنيم سپس در بيرون حلقه عدد را بر تكتك آنها تقسيم كنيم تا باقي مقسوم عليهها پيدا شوند. چرا اين كار را ميكنيم؟ بخاطر اينكه تعداد تكرارهاي حلقه خود را كم كنيم، هيچ عددي وجود ندارد كه تعداد مقسوم عليههاي كوچكتر از نصف خودش برابر نصف عدد باشد، طبق حل اول مساله گفتيم كه تعداد تكرار حلقه برابر خود عدد 1- است، حالا ما اين مقدار را به نصف كاهش داديم و سپس براي نصفه باقي يك حلقه ديگر نوشتيم پس هميشه تكرار حلقههاي ما كمتر از خود عدد1- است. كد آن را به صورت زير مينويسيم:
int number = 6;
int sum = 1;
List«int» items = new List«int»();
for (int i = 2; i « number / 2; i++) {
if (number % i == 0) {
sum += i;
items.Add(i); }}
مقاديري كه در itemsقرار ميگيرند عبارتند از تمامي مقسوم عليههايي كه از نصف عدد كوچكتر هستند. پس خارج از حلقه بايد مقسوم عليههاي بزرگتر از نصف را با مقدار sum جمع كنيم كه اين كار را به صورت زير انجام ميدهيم:
for (int i = 0; i « items.Count; i++) {
int x = number / items[i];
sum += x;}
اين قطعه كد براي اعدادي كه بزرگ هستند به مراتب بهتر از قطعه كدهاي بالايي است. اما آيا اين كد بهينه است؟
جواب، خير است، پس كد را عوض ميكنيم. ميدانيم كه اگر عددي اول باشد مجموع مقسوم عليههاي سره آن برابر 1 است!
پس قبل از اجرا شدن 2 حلقه بالا كافي است كه بررسي كنيد يك عدد اول هست يا خير اگر اول بود ديگر كامل نيست.
اين كار يك مزيت دارد آن هم اين است كه اگر عدد اول بزرگي را خواستيد بررسي كنيد، ديگر همه مراحل را تكرار نميكنيد و همانجا صراحتا «جواب كامل نيست» را در خروجي چاپ ميكنيد، عيب اين روش در اين است كه اگر عدد بزرگ بوده و اول هم نباشد ما زماني را صرف چككردن اول بودن عدد كردهايم، اما در مقالههاي پيش ثابت كرديم كه چككردن اولبودن يك عدد را ميتوان در هزارم ثانيه انجام داد پس ميتوان از مدت زمان اجراي اين عمليات صرف نظر كرد.
امير بهاالدين سبطالشيخ