contest

مسابقه برنامه‌نویسی پیتزا رباتیک: نتایج

پیتزا رباتیک یک مسابقه برنامه‌نویسی و ریاضی دانش‌آموزی آنلاین هست که بهمن‌ماه ۹۷ در پایلی و با همراهی خانه ریاضیات اصفهان برگزار شد.

در این مسابقه برنامه‌نویسی، دانش‌آموزان باید برنامه‌ای می‌نوشتند که به یک شرکت نوپای پیتزا، به نام پیتزا رباتیک، کمک کند که بهترین سود و سرویس به مشتریان را داشته باشد. پیتزا رباتیک از ربات‌ها برای تهیه و تحویل پیتزا استفاده میکنه.

مشتری‌های پیتزا رباتیک پیتزای دلخواهشون را به هر قیمتی که می‌خواهند سفارش می‌دهند. پیتزا رباتیک میتونه هر کدام را که خواست قبول یا رد کنه. اما آنهایی که پذیرفت را باید تا یک مدت زمان مشخص تحویل دهد.

پیتزا رباتیک اگر هیچ سفارشی را قبول نکنه، هیچ سودی نمیکنه. از طرف دیگر، اگر همه سفارش‌ها را قبول کنه به احتمال زیاد ضرر کلانی میکنه. پیتزا رباتیک به برنامه‌ای نیاز داشت که بهشون بگه کدام یک از سفارش‌ها را قبول کنه. این برنامه‌ای هست که دانش‌آموزان براشون نوشتن.

مسابقه برنامه‌نویسی

جزییات مسأله مسابقه را میتونی اینجا ببینی.

تعریف مسأله از دیدگاه علوم کامپیوتر

مسأله مسابقه یکی از حالت‌های مسأله معروف کوله‌پشتی (Knapsack Problem) در علوم کامپیوتر هست. در تخصیص منابع (که برای ما می‌شود یک ربات پرنده) با محدودیت‌های عملیاتی (که برای ما می‌شود محدودیت زمانی تحویل پیتزا به مشتریان)، با این مسأله روبرو هستیم.

مسأله کوله‌پشتی کاربرد بسیار گسترده‌ای در بسیاری از صنایع دارد. رمزنگاری، انتخاب سرمایه‌گذاری، و بهره‌بردای شبکه توزیع برق تنها چند نمونه از این صنایع هستند.

در حل مسأله، دو معیار دارای اهمیت ویژه‌ای هستند و برای رتبه‌بندی شرکت‌کنندگان مسابقه در نظر گرفته شدند. به ترتیب اهمیت:

دقت: اینکه آیا برنامه جواب بهینه برای هر مورد به دست می‌آورد یا نه.

سرعت: اینکه برنامه برای پیدا کردن جواب چقدر زمان می‌برد.

رتبه‌های اول تا سوم بر اساس این معیارها تعیین شدند. جزییات برنامه‌ها و عملکرد هر یک بر روی یک مجموعه از ۶ مورد گوناگون در انتهای خواندنی آورده شده است. اینجا میتونی راه‌حل گوگل (برنامه‌نویسی پویا) برای حل مسأله کوله‌پشتی را هم ببینی.

مسابقه برنامه‌نویسی

رتبه‌بندی

رتبه اول: نرگس منتظری با یک الگوریتم بازگشتی (recursive algorithm)

نرگس منتظری از مدرسه فرزانگان امین ۱ اصفهان موفق به نوشتن برنامه‌ای بازگشتی برای حل مسأله شد (فایل برنامه). رهیافت برنامه‌نویسی پویا در واقع حالت بهبودیافته این الگوریتم است.

این برنامه همیشه جواب بهینه را به دست می‌آورد. سرعت برنامه برای موارد با تعداد سفارش‌های کم تا متوسط (تا تقریبا ۴۰ سفارش) بالاست. اما بعد از آن سرعت برنامه به طور تصاعدی کاهش می‌یابد.

رتبه دوم: مهدی کریمی با یک الگوریتم حریصانه (greedy algorithm)

مهدی کریمی از مدرسه ابوذر اصفهان با رهیافت الگوریتم حریصانه در مسابقه شرکت کرد (فایل برنامه).

این برنامه معمولا جواب بهینه را به دست می‌آورد اما این تضمین‌شده نیست. سرعت برنامه همیشه و برای هر تعداد سفارشی بالا است. این یک مزیت بزرگ برای حالت‌هایی از مسأله کوله‌پشتی با تعداد انتخاب‌های زیاد هست.

رتبه سوم: بهفر باقری با یک الگوریتم بر پایه ارزیابی همه زیرمجموعه‌ها (enumeration)

بهفر باقری از مدرسه امام خمینی شاهین‌شهر برنامه‌ای نوشت که همه زیرمجموعه‌های ممکن را با استفاده از ماژول itertools تشکیل داده و ارزیابی می‌کند (فایل برنامه).

این برنامه همیشه جواب بهینه را به دست می‌آورد. اما به دلیل سرعت کم (هزینه محاسباتی بسیار بالا)، تنها برای موردهای با تعداد سفارش کم قابل استفاده است.

تقدیر و جمع‌بندی

ما به همه دانش‌‌آموزانی که برای حل مسأله این مسابقه تلاش کردند تبریک می‌گوییم. امیدوار هستیم که این مسابقه تجربه‌ای هیجان‌انگیز و آموزنده برای همه بوده باشد. این هدف اصلی ما بوده است.

از آنجایی که همه نفرات برتر از اصفهان هستند، تقدیرنامه و یک هدیه به رسم یادگار به صورت حضوری در خانه ریاضیات اصفهان به آنها تقدیم شد.

شرکت فرادرس هم هدیه‌ای در قالب اعتبار خرید برای دریافت رایگان هر کدام از صدها دوره آموزشی آنها به نفرات برتر اهدا نمود. 

awards


پیوست‌ها

عملکرد برنامه‌‌ها روی ۶ مورد گوناگون

جزییات برنامه‌ها

حل با الگوریتم بازگشتی

این برنامه در ابتدا با گرفتن اطلاعات از کاربر برای هر سفارش، فاصله تا رستوران (رفت و برگشت) را محاسبه می‌کند (masafat) و به کمک آن، زمان (time=masafat/speed) و سود خالص (net_profit=profit-masafat*cast_meter) هر سفارش را جداگانه محاسبه و در لیستی ذخیره می‌کند.

سپس به کمک تابع بازگشتی دو مقدارِ تعداد سفارش‌ها و حداکثر زمان را به عنوان ورودی دریافت و مقدار سود، زمان تحویل و لیست کالاهای پذیرفته شده را به عنوان خروجی نشان می‌دهد.

اگر یکی از دو مقدار ورودی صفر باشد، حداکثر سود خالص و زمان تحویل را برابر صفر و لیست سفارش‌های پذیرفته شده را خالی می‌گذارد. در غیر این صورت اگر زمان سفارش آخر از حداکثر زمان (max_total_time) بیشتر بود، به صورت تابع بازگشتی حداکثر سود خالص و زمان این حالت را برابر حالتی که تعداد n-1 کالا را باید حداکثر در همین زمان برساند قرار می‌دهد. و در غیر این صورت حداکثر سود خالص را برابر بیشترین مقدار بین وقتی که کالای آخر را بردارد (تعداد n-1 کالا را در حداکثر زمان منهای زمان کالا آخر بردارد) یا بر ندارد (تعداد n-1 کالا را حداکثر در همین زمان بردارد) قرار می‌دهد. اگر حالت اینکه کالای آخر را بردارد بزرگتر بود، آن را به لیست پذیرفته شده اضافه می‌کند و زمان و سود خالص آن را نیز به زمان تحویل و حداکثر سود خالص اضافه می‌کند.

حل با الگوریتم حریصانه

جزئیات کامل این برنامه را میتونی در این اسلایدهای تصویری ببینی. به طور خلاصه:

  1. با قانون فیثاغورث فاصله مبدأ تا مقصد را بدست آورده و آن را در دو ضرب می‌کنیم زیرا ربات باید هم فاصله را برود و هم باز گردد.
  2. فاصله بدست آمده را در 0.001 ضرب می‌کنیم زیرا هر 1 متر که ربات می‌رود 0.001 هزینه دارد.
  3. فاصله را بر 500 تقسیم می‌کنیم تا زمان رفتن ربات بر ثانیه بدست آید.
  4. هزینه – پولی که کاربر می‌دهد = سود
  5. سود را بر زمان تقسیم کرده تا بفهمیم ربات در هر ثانیه چقدر سود می‌کند .
  6. برای همه مشتریان این کار را کرده و آن ها را ذخیره می‌کنیم. (می‌توان برای ذخیره هر یک از  dict استفاده کرد.)
  7. آنها را بر حسب مرحله ۵ مرتب میکنیم. ( sorted(orders1,key=lambda k: k[‘p/t’],reverse=True) )
  8. داده‌های قبلی را از اول پیمایش کرده اگر سود آن ها منفی نبود و همچنین زمان آن مناسب بود (یعنی هنوز انرژی برای رفتن آن مسافت داشته باشیم)  سود و زمان و id آن را در متغیرهایی ذخیره کرده و همینطور تا آخر پیش می‌رویم (هر کدام را ذخیره می‌کنیم) .
  9. حال یکبار دیگر از اول پیمایش کرده و هر بار یکی از آن ها را حساب نمی‌کنیم و آنها را در متغیر هایی ذخیره می‌کنیم (مثلا خانه یکم را حساب نمی‌کنیم و با توجه به شروط مرحله ی ۸ سود آنها را جمع می‌کنیم بعد که تا آخر حساب کردیم خانه ی ۲ را حساب نمی‌کنیم).
  10. بیشترین سود را محاسبه کرده و آن را نمایش می‌دهیم.