الگوریتمستان

یادداشت‌های یک معلم علاقه‌مند به نوشتن از آنچه آموخته و یاد می‌دهد
 

آموزش الگوریتم

الگوریتم آنلاین

شاید برای شما هم پیش آمده باشد که در یک سفر بین شهری بخواهید از سرویس‌های بهداشتی بین راه استفاده کنید. اما از وضعیت نظافت سرویس‌های طول مسیر اطلاع ندارید و مشخص نیست اگر از یک سرویس بگذرید، آیا امیدی هست محل مناسب‌تر دیگری پیدا شود؟ در واقع سوال این است که چقدر احتمال دارد بهترین و تمیزترین سرویس بهداشتی را از بین گزینه‌های ممکن انتخاب کرده باشیم و حداقل چطور می‌توانیم مطمئن شویم که با احتمال بالایی انتخاب خوبی داشته‌ایم؟ چالش مهم این نوع مسائل انتخاب بهتر از بین گزینه‌هایی هست که به صورت جریانی از اطلاعات یک به یک در اختیار ما قرار می‌گیرند.

کتاب Competetive Programming

ویراست سوم کتاب برنامه‌نویسی رقابتی با نام کامل Competitive Programming 3: The New Lower Bound of Programming Contests با تلاش Steven Halim و Felix Halim از مربیان تیم‌های برنامه‌نویسی ACM-ICPC سنگاپور تالیف و در سال ۲۰۱۳ منتشر شده است که امروزه به عنوان یکی از منابع مناسب برای آمادگی تیم‌های شرکت‌کننده در مسابقات برنامه‌نویسی الگوریتمی بویژه مسابقات برنامه‌نویسی ACM-ICPC توصیه می‌شود.

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

کتاب Competitive Programmer's Handbook (راهنمای برنامه‌نویسان رقابتی) کتابچه‌ای است که در آن عموم مباحث مورد نیاز جهت شرکت در رقابت‌های برنامه‌نویسی همچون المپیاد کامپیوتر دانش‌آموزی یا مسابقات برنامه‌نویسی دانشجویی به صورت مختصر و مفید یک جا جمع شده است.

دکتر Antti Laaksonen از مربیان تیم‌های المپیاد کامپیوتر کشور فنلاند این کتاب را به صورت رایگان جهت استفاده عموم در سه بخش و سی فصل با عناوین زیر منتشر کرده است.

پیچیدگی زمانی اجرای الگوریتم

زمانی که برای حل یک مسئله الگوریتم طراحی می‌کنیم یا قصد استفاده از یک الگوریتم از پیش ابداع شده را داریم، عموما برایمان مهم است بدانیم کارآیی الگوریتم چگونه است و تا چه حد می‌توان روی آن حساب باز کرد. به ویژه اگر برای حل یک مسئله بیش از یک الگوریتم موجود باشد، باید بتوان آنها را به نحوی با هم مقایسه کرد. گاهی چنین مقایسه‌ای بر اساس قابلیت پیاده‌سازی یا میزان سادگی پیاده‌سازی است. اما در بسیاری مواقع سرعت تولید خروجی الگوریتم بسیار مهمتر از پیچیدگی پیاده‌سازی یا مدت زمان مورد نیاز برای پیاده‌سازی است. به همین دلیل طراحی یک الگوریتم کارا بسیار مهم است و در مسابقات برنامه‌نویسی همچون ACM-ICPC نیز این توانایی محک می‌خورد. در کنار این موضوع، ممکن است میزان حافظه مورد نیاز هم مهم باشد.

دوره طراحی و تحلیل الگوریتم دانشگاه استنفورد

ویدئوهای جلسات آموزشی طراحی و تحلیل الگوریتم پروفسور Tim Roughgarden دانشگاه استنفورد از طریق پیوندهای زیر قابل مشاهده است. در این دوره هر الگوریتم یا ساختمان داده بحث شده، همراه با تحلیل، مثال‌های کاربردی یا جایگاه مورد نیاز آنها است.

مستندات دوره آمادگی مسابقات برنامه‌نویسی دانشگاه استنفورد

مستندات دوره «Introduction to Programming Contests» دانشگاه استنفورد با تدریس Jaehyun Park (مربی تیم‌های ACM-ICPC این دانشگاه) شامل اسلایدها، سوالات برگزیده برای تمرین در موضوعات مختلف ریاضیات، ساختمان داده‌ها و الگوریتم‌ها به همراه نکات برنامه‌نویسی از پیوند زیر قابل مشاهده و دریافت هستند:

دنباله اعداد فیبوناچی

بسیاری از فرآیندهای طبیعی از جمله ترکیب ساختار بدن موجودات زنده نظم مشخصی دارند و از دنباله اعدادی تبعیت می‌کنند که امروزه با نام دنباله اعداد فیبوناچی (فیبوناتچی - Fibonacci) شناخته می‌شود. مشهورترین خاصیت این اعداد نسبت دو جمله متوالی آنها به ازای جملات بزرگ دنباله است که به عدد طلایی مشهور است.

کتاب طراحی الگوریتم با رویکردی خلاقانه

کتاب Introduction to Algorithms: A Creative Approach را می‌توان مکملی بر استفاده از کتاب Introduction to Algorithms (مشهور به کتاب CLRS) دانست. در این کتاب علاوه بر معرفی تکنیک‌های مختلف طراحی الگوریتم‌ها و روش‌های حل برخی مسائل الگوریتمی، روش‌های تحلیل و حل آنها با جزئیات بیشتر و به صورت گام به گام بررسی شده است. به همین دلیل نیز از جمله منابع اصلی پیشنهادی به متقاضیان شرکت در المپیادهای کامپیوتر و مسابقات برنامه‌نویسی برای یادگیری طراحی و تحلیل الگوریتم‌ها است.

الگوریتم جستجوی اول عمق (DFS)

الگوریتم جستجوی اول عمق (Depth First Search - DFS) یا نام‌های دیگری همچون جستجو در عمق، پیمایش اول عمق، پیمایش عمق اول الگوریتمی مشابه الگوریتم جستجوی اول سطح (BFS) برای پیمایش گراف است. این دو الگوریتم خواص و کاربردهای مشترک بسیاری دارند و تفاوت اصلی در این است که در هر تکرار الگوریتم DFS تنها یکی از گره‌های مجاور گره پردازش شده برای مرحله بعد انتخاب می‌شود. به این ترتیب، الگوریتم DFS به جای صف از یک پشته برای مشخص کردن مسیر پیمایش استفاده می‌کند.

کتاب مقدمه‌ای بر الگوریتم‌ها

کتاب Introduction to Algorithms (مشهور به کتاب CLRS) از انتشارات MIT اثر Thomas H. Cormen، Charles E. Leiserson، Ronald L. Rivest و Clifford Stein کتاب جامع مباحث الگوریتم‌ها و ساختمان داده‌ها است که منبع درسی بسیاری از دانشگاه‌های معتبر بوده و تا کنون بیش از سی هزار مقاله و کتاب با ارجاع به آن نگارش یافته است. مطالب این کتاب از مباحث اولیه مانند مفهوم تحلیل و طراحی الگوریتم آغاز شده و مباحث پیشرفته طراحی الگوریتم‌ها و ساختمان داده‌ها را نیز پوشش می‌دهد. به همین دلیل مطالعه و استفاده از آن به عنوان مرجع برای کلیه علاقمندان مباحث طراحی الگوریتم‌ها، ساختمان داده‌ها و همینطور شرکت‌کنندگان المپیادهای کامپیوتری و مسابقات برنامه‌نویسی توصیه می‌شود.

الگوریتم فلوید-وارشال

الگوریتم فلوید-وارشال (Floyd-Warshall) یک الگوریتم مبتنی بر روش برنامه‌نویسی پویا برای محاسبه کوتاهترین مسیر بین هر دو جفت گره گراف‌های وزن‌دار است. دو الگوریتم رایج دایکسترا و بلمن-فورد روش‌های محاسبه کوتاهترین مسیر از مبدأ ثابت هستند که در صورت تکرار آنها به ازای هر گره عملکردی همانند الگوریتم فلوید-وارشال دارند. اما این الگوریتم ویژگی‌هایی دارد که آن را برجسته می‌کند:

کتاب Concrete Mathematics

کتاب Concrete Mathematics: A Foundation for Computer Science نوشته‌ای با موضوع مفاهیم اولیه ریاضیات پیوسته (CONtinuous mathematics) و ریاضیات گسسته (disCRETE mathematics) به قلم رونالد گراهام، دونالد کنوت و اُرِن پاتاشنیک - از دانشمندان بزرگ علوم ریاضیات و کامپیوتر - است . در این کتاب از بیان متفاوتی نسبت به نوشتار عموم کتاب‌های آموزش ریاضی استفاده شده و مفاهیم پایه‌ای محاسباتی علم کامپیوتر به زبان ساده و گیرا توضیح داده شده است. این مفاهیم پیش‌نیاز حل بسیاری از مسائل کامپیوتری، ریاضی و محاسبات علمی هستند. به همین دلیل، مطالعه آن به علاقه‌مندان برنامه‌نویسی، بویژه شرکت‌کنندگان المپیادهای کامپیوتری و مسابقات برنامه‌نویسی توصیه می‌شود.

الگوریتم جستجوی اول سطح (BFS)

الگوریتم پیمایش اول سطح یا جستجوی اول سطح (Breadth First Search - BFS) از جمله الگوریتم‌های مشهور پیمایش و جستجوی گراف است که در حل مسائل الگوریتمی و هوش مصنوعی کاربرد دارد. این الگوریتم برای پیمایش و جستجوی گراف از یک صف برای نگهداری ترتیب جستجو استفاده می‌کند.

  

الگوریتم دایجسترا

الگوریتم دایکسترا (دیکسترا، دایجسترا - Dijkstra) یک راهکار حریصانه برای یافتن کوتاهترین مسیر از مقصد ثابت (تک منبع) به سایر گره‌های گراف وزن‌دار است. این گراف می‌تواند معرف مسیرهای یک شهر و تقاطع‌های آن باشد که انبار شرکت در یک گره آن قرار داشته و هدف یافتن کوتاهترین مسیر به هر محل دیگر از این انبار است. طبیعتا این الگوریتم در یافتن کوتاهترین مسیر بین دو گره مشخص نیز کاربرد دارد. تنها شرط لازم برای استفاده از این الگوریتم نامنفی بودن وزن یال‌های گراف است.

کتاب چالش‌های برنامه‌نویسی

کتاب Programming Challenges: The Programming Contest Training Manual از انتشارات معتبر Springer کتاب مفیدی برای آمادگی شرکت در مسابقات برنامه‌نویسی است که نویسندگان آن به صورت گام به گام، خلاصه و مفید، به مفاهیم و نکات مهم برنامه‌نویسی، ساختمان داده‌ها، محاسبات ریاضی و طراحی الگوریتم‌ها اشاره داشته و با طرح مسائل متفاوت از هر موضوع، خواننده را به چالش حل مسئله کشیده‌اند.

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

روش حریصانه (Greedy) یکی از روش‌های مشهور و پرکاربرد طراحی الگوریتم‌ها است که با ساختاری ساده در حل بسیاری از مسائل استفاده می‌شود. این روش اغلب در حل مسائل بهینه‌سازی استفاده شده و در پاره‌ای مواقع جایگزین مناسبی برای روش‌هایی مانند برنامه‌ریزی پویا است. در حالت کلی این روش سرعت و مرتبه اجرایی بهتری نسبت به روش‌های مشابه خود دارد؛ اما متناسب با مسئله ممکن است به یک جواب بهینه سراسری ختم نشود.

معمای هشت وزیر

معمای هشت وزیر از جمله مسائل کلاسیک مباحث طراحی الگوریتم است که در حالت کلی‌تر با عنوان معمای n وزیر یا معمای چند وزیر مطرح می‌شود.

  

برای افرادی که با بازی شطرنج آشنایی ندارند

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

الگوریتم مرتب‌سازی هرمی

مرتب‌سازی هرمی (Heap Sort) یکی از روش‌های مشهور مرتب‌سازی داده‌ها است که بر اساس خصوصیات درخت heap (هیپ، هرم یا کپه)  و عملکرد آن پیاده‌سازی شده است.

بر اساس تعریف درخت heap، در یک max-heap (یا min-heap) بزرگترین (یا کوچکترین) مقدار بین داده‌ها همواره در ریشه درخت قرار دارد. یافتن بزرگترین (یا کوچکترین) عنصر بین عناصر، هزینه ثابت ( Ө( 1 دارد. با حذف این عنصر از درخت، بزرگترین (یا کوچکترین) عنصر بعدی مجددا در ریشه قرار می‌گیرد. به این ترتیب با حذف متوالی عناصر درخت heap و درج آنها در محل جدید، یک آرایه مرتب‌شده نزولی (یا صعودی) به دست خواهد آمد.

الگوریتم مرتب‌سازی ادغامی

روش مرتب‌سازی ادغامی (Merge Sort) یک روش مرتب‌سازی مبتنی بر مقایسه عناصر با استفاده از روش تقسیم و غلبه است. این روش از مراحل بازگشتی زیر تشکیل یافته است:

1- آرایه را به دو زیرآرایه با اندازه تقریبا یکسان تقسیم کن.

الگوریتم مرتب‌سازی سریع

روش مرتب‌سازی سریع (Quick Sort) یکی از الگوریتم‌های مشهور مرتب‌سازی داده‌ها است. این الگوریتم طی مراحل بازگشتی زیر یک روش تقسیم و غلبه برای مرتب کردن داده‌ها ارائه می‌نماید:

1- انتخاب عنصر محوری: یکی از عناصر آرایه به عنوان عنصر محوری (pivot) - به عنوان مثال عنصر اول - انتخاب می‌شود.

دنباله اعداد کاتالان و محاسبه آن

دنباله اعداد کاتالان (Catalan Numbers) یکی از دنباله‌های عددی مشهور ریاضیات است که برای عدد نامنفی n به صورت $C_n$ نمایش داده می‌شود.

  

$C_n:\qquad 1,\;1,\;2,\;5,\;14,\;42,\;132,\;429,\;1430,\;4862,\;16796,\;\cdots$

الگوریتم مرتب‌سازی درجی

روش مرتب‌سازی درجی (Insertion Sort) یکی از روش‌های مقدماتی مرتب‌سازی مبتنی بر مقایسه عناصر است که در مقایسه با روش‌های دیگر بیشتر مورد توجه قرار دارد.

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

محاسبه ضرایب دوجمله‌ای

تعریف ترکیب (Combination)

تعداد حالت‌های انتخاب r (عدد صحیح و نامنفی) شیء از n (عدد صحیح و بزرگتر یا مساوی r) شیء را که ترتیب انتخاب اهمیت نداشته باشد، انتخاب r از n یا ترکیب r روی n گویند و به یکی از صورت‌های زیر نمایش می‌دهند:

  

الگوریتم مرتب‌سازی انتخابی

روش مرتب‌سازی انتخابی (Selection Sort) یکی از روش‌های اولیه مرتب‌سازی بر اساس مقایسه عناصر است. این الگوریتم طی چند مرحله عناصر لیست را به صورت صعودی یا نزولی مرتب می‌کند. به این ترتیب که در هر مرحله با بررسی عناصر نامرتب، بزرگترین (یا کوچکترین) عنصر را پیدا کرده و به انتهای لیست منتقل می‌کند.

الگوریتم مرتب‌سازی حبابی

یکی از روش‌های مرتب‌سازی، روش مرتب‌سازی حبابی (Bubble Sort) است که به آن روش تعویض استاندارد (Standard Exchange) نیز می‌گویند. این روش شامل چند مرحله است که در هر مرحله یک عنصر از لیست به طور قطع در محل مناسب خود قرار می‌گیرد.

لیست زیر را در نظر بگیرید که می‌خواهیم به صورت صعودی (از کوچک به بزرگ) مرتب کنیم:

برج هانوی

علاقه‌مندان به مباحث مختلف طراحی الگوریتم و همینطور شرکت‌کنندگان مسابقات برنامه‌نویسی به خوبی می‌دانند که یکی از مهمترین پارامترهای طراحی موفقیت‌آمیز یک الگوریتم، شیوه صحیح فکر کردن روی حل مسئله است. حل انواع سوالات الگوریتمی به ما کمک می‌کند ذهن خودمان را برای حل مسائل پیچیده‌تر آماده کنیم. مسئله برج هانوی (Tower of Hanoi) یکی از مسائل تاریخی مشهور است که در مباحث طراحی الگوریتم نیز به آن پرداخته می‌شود.

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

وب‌سایت UVa یکی از وب‌سایت‌هایی است که امکانات مفیدی را برای علاقه‌مندان حل سوالات الگوریتمی و شرکت‌کنندگان مسابقات برنامه‌نویسی (بالاخص ACM) مهیا کرده است. در این وب‌سایت مجموعه سوالاتی در سطوح مختلف و مشابه سوالات مسابقات برنامه‌نویسی بین‌المللی در دسترس عموم قرار گرفته است که علاقه‌مندان می‌توانند پس از عضویت در سایت، پاسخ هر سوالی را که حل کرده‌اند، ارسال کنند. این جواب‌ها بررسی شده و درست یا نادرست بودن آن به کاربر ابلاغ می‌شود. می‌توان گفت نوعی شبیه‌سازی مسابقات برنامه‌نویسی رسمی انجام می‌گیرد.

مسئله کاشیکاری

یکی از مسائل جالب طراحی الگوریتم مسئله کاشیکاری یا فرش کردن زمین با موزاییک‌ است. فرض کنید قطعه زمین مربعی شکل با ابعادی از توان عدد دو داریم. هدف فرش کردن این قطعه زمین با استفاده از موزاییک‌هایی با شکل L است به قسمی که یکی از خانه‌های زمین شطرنجی شده فوق پوشیده نشود. می‌توان فرض کرد از این خانه برای احداث باغچه یا حوضچه کوچکی استفاده خواهد شد. در ضمن حق شکستن این موزاییک‌ها به تکه‌های کوچکتر را نداریم.

الگوریتم ضرب استراسن

همه ما با تعریف ضرب ماتریس‌های مربعی آشنایی داریم. حاصلضرب ماتریس‌های مربعی A و B به صورت زیر تعریف می‌شود:

  

\[ A=(a_{ij})_{n \times n} \qquad , \qquad B=(b_{ij})_{n \times n} \] \[ C = A \times B = (c_{ij})_{n \times n} \qquad ; \qquad c_{ij}= \sum_{k=1}^{n} a_{ik} \; b{kj} \]

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

مسئله ضرب زنجیره‌ای ماتریس‌ها و پرانتزبندی بهینه آن یکی از مثال‌های مشهور کاربرد برنامه‌نویسی پویا در حل مسائل بهینه‌سازی است.

فرض کنید قصد داریم حاصلضرب عبارت ماتریسی $ A_{3 \times 7} \times B_{7 \times 8 } \times C_{8 \times 4} $ را محاسبه کنیم. می‌دانیم که ضرب ماتریس‌ها خاصیت شرکت‌پذیری داشته، اما خاصیت جابجایی ندارد. بنابراین رعایت ترتیب ضرب آنها مهم است. پرانتزبندی‌های مختلف ضرب ماتریس‌ها حالت‌های مختلف محاسبه آن را به ما می‌دهند:

الگوریتم‌های برنامه‌نویسی پویا

یکی از روش‌های پرکاربرد و مشهور طراحی الگوریتم روش برنامه‌نویسی پویا (یا برنامه‌ریزی پویا، برنامه‌سازی پویا - Dynamic Programming) است. این روش همچون روش تقسیم و حل (Divide and Conquer) بر پایه تقسیم مسئله بر زیرمسئله‌ها کار می‌کند. اما تفاوت‌های چشم‌گیری با آن دارد.

الگوریتم‌های تقسیم و حل

یکی از روش‌های پرکاربرد و محبوب برای طراحی الگوریتم‌ها روش Divide and Conquer است که در زبان فارسی به صورت الگوریتم‌های تقسیم و حل یا تقسیم و غلبه ترجمه شده است.

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