برای روشن کردن ترمینال، کلیک کن یا هر کلیدی را بزن

پرش به محتوای اصلی
>_
درس ۲۵پیشرفته

ماتریس‌ها و عملیات ماتریسی

پیمایش، ضرب، یافتن ماکزیمم سطر، ماتریس مثلثی و تولید تصادفی

معمولی
گرافیک و ماتریس•صفحات ۳۹-۴۰•۴۵ دقیقه•۲٬۹۳۹ کلمهزمان مطالعه: ۰ دقیقه
فهرست این درس
// اهداف۵
  • تعریف و پیمایش آرایه دوبعدی (ماتریس) در ⁦C++⁩
  • پیاده‌سازی ضرب دو ماتریس با شرط ابعاد
  • یافتن اندیس ماکزیمم هر سطر
  • ساخت ماتریس مثلثی با الگوهای خاص
  • پر کردن ماتریس با اعداد تصادفی و جستجوی خطی

مقدمه‌ای بر ماتریس‌ها (آرایه دوبعدی)

ماتریس (Matrix) یک آرایه دوبعدی از اعداد است که در آن هر عنصر با دو اندیس (i, j) مشخص می‌شود: i شماره سطر و j شماره ستون.

در ⁦C++⁩ می‌توان ماتریس را به چند شکل تعریف کرد:

main.cppcpp

در این درس از std::vector استفاده می‌کنیم چون استاندارد و امن است و اجازه می‌دهد اندازه‌ها در زمان اجرا تعیین شوند.

تعریف و مقداردهی ماتریس

تعریف یک ماتریس ۲×۳ با مقداردهی اولیهcpp

خروجی برنامه

خروجی
1 2 3 
4 5 6 

پیمایش ماتریس و خواندن از ورودی

برای خواندن یا پیمایش تمام عناصر یک ماتریس n × m از دو حلقه تودرتو استفاده می‌کنیم:

main.cppcpp

نکته: ترتیب پیمایش (سطر-به-سطر یا ستون-به-ستون) روی کارایی حافظه‌ی Cache تأثیر دارد. پیمایش سطر-به-سطر در C متعارف است.

خواندن و چاپ ماتریس

دریافت n×m عدد و چاپ ماتریسcpp

ضرب دو ماتریس

برای ضرب ماتریس A به ابعاد m × n در ماتریس B به ابعاد n × l، حاصل ماتریس C به ابعاد m × l است که هر درایه آن به‌صورت زیر محاسبه می‌شود:

main.cppcpp

شرط امکان‌پذیری ضرب: تعداد ستون‌های A باید برابر تعداد سطر‌های B باشد.

پیچیدگی زمانی: O(m * n * l) که برای ماتریس‌های n×n برابر O(n³) است.

پیاده‌سازی ضرب دو ماتریس

ضرب ماتریس A (m×n) در B (n×l)cpp

فلوچارت ضرب ماتریس

flow.svg

محاسبه دترمینان ماتریس ۲×۲

برای یک ماتریس ۲×۲ [[a, b], [c, d]] دترمینان به فرمول زیر محاسبه می‌شود:

main.cppcpp

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

main.cppcpp

دترمینان در برابر ضرب ماتریس

کد اصلی جزوه عنوان «حاصل‌ضرب دو ماتریس» داشت اما در واقع فرمول a[0][0]*a[1][1] - a[0][1]*a[1][0] که دترمینان ماتریس ۲×۲ است را محاسبه می‌کرد. این یک اشتباه مفهومی است: ضرب ماتریس نیاز به دو ماتریس مجزا و سه حلقه تودرتو دارد، در حالی که دترمینان یک عملیات تک‌ماتریسی است.

نسخه اشتباه جزوه
کد دارای باگcpp
نسخه اصلاح‌شده و استاندارد
کد صحیحcpp

یافتن اندیس ماکزیمم هر سطر

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

نکته: صورت مسئله «اندیس» ماکزیمم را می‌خواهد (نه مقدار آن را). یعنی lmax را چاپ می‌کنیم، نه a[i][lmax] را.

یافتن اندیس ماکزیمم هر سطر

چاپ اندیس بزرگ‌ترین عنصر در هر سطرcpp

اصلاح اشکال حلقه داخلی در جزوه

کد اصلی جزوه دو اشکال داشت: (۱) حلقه داخلی به‌جای j < m اشتباهاً j < n نوشته شده بود که در ماتریس غیرمربعی باعث Out of Bounds می‌شد. (۲) به‌جای چاپ lmax (اندیس)، a[i][lmax] (مقدار) را چاپ می‌کرد که با صورت سؤال مغایر بود.

نسخه اشتباه جزوه
کد دارای باگcpp
نسخه اصلاح‌شده و استاندارد
کد صحیحcpp

ضرب ماتریس با شرط (تساوی ابعاد)

برنامه‌ای که ابعاد دو ماتریس را می‌گیرد، اگر تعداد ستون‌های اول برابر سطر‌های دوم بود، حاصل‌ضرب را محاسبه می‌کند؛ در غیر این صورت پیام خطا می‌دهد.

اشکالات کد اصلی جزوه:

  • متغیر k هم به‌عنوان ورودی (ابعاد) و هم به‌عنوان شمارنده حلقه استفاده شده بود (Shadowing).
  • مقادیر ماتریس B از ورودی گرفته نمی‌شد.
  • شرط حلقه دریافت A اشتباهاً j < k بود به‌جای j < n.

ضرب ماتریس با شرط (کامل)

دریافت ابعاد، بررسی شرط و محاسبه ضربcpp

اصلاح ضرب ماتریس با شرط در جزوه

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

نسخه اشتباه جزوه
کد دارای باگcpp
نسخه اصلاح‌شده و استاندارد
کد صحیحcpp

ماتریس مثلثی

ماتریس مثلثی ماتریسی است که عناصر آن بالای یا پایین قطر اصلی (یا هر دو) صفر باشند.

  • مثلثی پایینی (Lower Triangular): عناصر بالای قطر اصلی صفر هستند (یعنی i < j).
  • مثلثی بالایی (Upper Triangular): عناصر زیر قطر اصلی صفر هستند (یعنی i > j).

نکته: مثال زیر یک الگوی متفاوت است که ناحیه زیر قطر فرعی (anti-diagonal) را با اعداد نزولی پر می‌کند. این الگو با «مثلثی پایینی استاندارد» متفاوت است اما حرف زدن از قطر فرعی هم در برنامه‌نویسی رایج است.

main.cppcpp

شرط پر بودن: i + j >= n - 1. مقدار عنصر: n - j.

ماتریس زیر قطر فرعی (Anti-diagonal)

ساخت ماتریس n×n با الگوی زیر قطر فرعیcpp

نمونه خروجی برای n=4

خروجی
0 0 0 1 
0 0 2 1 
0 3 2 1 
4 3 2 1 

فلوچارت ساخت ماتریس مثلثی

flow.svg

پر کردن ماتریس با اعداد تصادفی

با ترکیب توابع rand() و ماتریس می‌توانیم ماتریس‌هایی با مقادیر تصادفی تولید کنیم. فرمول کلی برای عدد در بازه [a, b]:

main.cppcpp

مثال‌ها:

  • عدد بین ۱ تا ۱۰۰: 1 + rand() % 100
  • عدد بین ۰ تا ۹: rand() % 10
  • اندیس تصادفی در ماتریس ۸×۸: rand() % 8

Seed کردن با زمان: برای تولید دنباله متفاوت در هر اجرا، باید در ابتدای main بنویسیم:

main.cppcpp

این کار نیاز به هدر‌های <cstdlib> (برای rand و srand) و <ctime> (برای time) دارد.

پر کردن ماتریس با اعداد تصادفی

ماتریس ۸×۸ با اعداد تصادفی بین ۱ تا ۹۹cpp

جستجوی خطی در ماتریس

برنامه‌ای که یک ماتریس ۸×۸ می‌سازد، ۴ خانه تصادفی را با 1 و بقیه را با 0 پر می‌کند. سپس مختصات (x, y) را می‌گیرد و از آن نقطه به سمت راست حرکت می‌کند تا به اولین 1 برسد یا به انتهای سطر برسد. تعداد گام‌های برداشته‌شده را چاپ می‌کند.

main.cppcpp

اصلاح متغیر تعریف‌نشده j در جزوه

در کد اصلی جزوه، خط j = y; بدون تعریف قبلی متغیر j نوشته شده بود که باعث خطای کامپایل می‌شود. در نسخه اصلاح‌شده از همان ابتدا int j = y; تعریف شده و ضمناً منطق جستجو برای رسیدن به انتهای سطر نیز ایمن‌سازی شده است.

نسخه اشتباه جزوه
کد دارای باگcpp
نسخه اصلاح‌شده و استاندارد
کد صحیحcpp

استفاده از vector به جای VLA

در تمام کدهای این درس به‌جای آرایه پویای int a[n][n]; (که VLA و غیراستاندارد است) از vector<vector<int>> استفاده کرده‌ایم. این روش مزایای زیر را دارد:

  • استاندارد ISO ⁦C++⁩ است و روی همه کامپایلر‌ها کار می‌کند.
  • حافظه به‌صورت خودکار آزاد می‌شود (نه نشت حافظه).
  • می‌توان اندازه را در زمان اجرا تغییر داد.
  • توابعی مانند .size() برای دسترسی به ابعاد در دسترس است.
main.cppcpp

فرمول تولید عدد تصادفی در بازه دلخواه

یادآوری فرمول مهم:

main.cppcpp

برای تولید عدد صحیح در بازه [a, b].

مثال‌های پرکاربرد:

  • تاس (۱ تا ۶): 1 + rand() % 6
  • ماه (۱ تا ۱۲): 1 + rand() % 12
  • حرف بزرگ انگلیسی (A تا Z): 'A' + rand() % 26
  • اندیس ماتریس N×N: rand() % N
  • درصد (۰ تا ۱۰۰): rand() % 101

مرور سریع مطالب

۲۶ بخش در این درس

تمرین‌ها

01
تمرین•حل کامل دارد

برنامه‌ای بنویسید که یک ماتریس n×m را بگیرد و مجموع هر سطر و مجموع هر ستون را چاپ کند.

نمایش راهنما
برای مجموع سطر: حلقه داخلی روی j. برای مجموع ستون: حلقه داخلی روی i.
02
تمرین•حل کامل دارد

برنامه‌ای بنویسید که یک ماتریس مربعی n×n را بگیرد و قطر اصلی و قطر فرعی آن را چاپ کند.

نمایش راهنما
قطر اصلی: عناصری که i == j. قطر فرعی: عناصری که i + j == n - 1.
03
تمرین•حل کامل دارد

برنامه‌ای بنویسید که دو ماتریس ۳×۳ را بگیرد و حاصل‌ضرب آن‌ها را محاسبه و چاپ کند.

نمایش راهنما
از سه حلقه تودرتو استفاده کنید: i برای سطر‌های C، j برای ستون‌های C، k برای جمع‌ضرب.
04
تمرین•حل کامل دارد

برنامه‌ای بنویسید که یک ماتریس n×n را با اعداد تصادفی بین ۱ تا ۹ پر کند. سپس ماتریس را چاپ کرده، بزرگ‌ترین و کوچک‌ترین عنصر کل ماتریس را به‌همراه مختصاتشان نمایش دهد.

نمایش راهنما
با فرض اولین عنصر به‌عنوان max و min شروع کنید، سپس با پیمایش مقایسه کنید.
05
تمرین•حل کامل دارد

برنامه‌ای بنویسید که ماتریس مثلثی بالایی n×n بسازد: یعنی عناصر زیر قطر اصلی (i > j) صفر باشند و بالای قطر اصلی اعداد ۱ تا بالا به‌صورت ردیفی قرار گیرند.

نمایش راهنما
شرط: اگر i <= j مقدار بنویسید، در غیر این صورت 0. الگوی اعداد می‌تواند a[i][j] = j - i + 1 باشد.
// نکات-کلیدی۶
  • 01ماتریس در ⁦C++⁩ به‌صورت آرایه دوبعدی تعریف می‌شود؛ ترجیحاً از `vector<vector<int>>` استفاده کنید تا از خطای VLA جلوگیری شود.
  • 02برای پیمایش ماتریس n×m از دو حلقه تودرتو استفاده می‌شود: حلقه بیرونی روی سطر‌ها (i از 0 تا n-1) و حلقه درونی روی ستون‌ها (j از 0 تا m-1).
  • 03شرط امکان‌پذیری ضرب دو ماتریس A (m×n) و B (n×l): تعداد ستون‌های A باید برابر سطر‌های B باشد، و حاصل ماتریس C (m×l) است.
  • 04دترمینان ماتریس ۲×۲ به‌صورت `a*d - b*c` محاسبه می‌شود؛ توجه کنید که این عملیات با «ضرب دو ماتریس» متفاوت است.
  • 05برای یافتن اندیس ماکزیمم هر سطر، متغیر `lmax` را روی ۰ مقداردهی اولیه کنید و آن را به‌روزرسانی کنید؛ دقت کنید که حلقه داخلی روی `m` ستون (نه `n` سطر) پیمایش کند.
  • 06برای تولید عدد تصادفی در بازه `[a, b]` از فرمول `a + rand() % (b-a+1)` و برای تنوع در هر اجرا از `srand(time(NULL))` استفاده کنید.
./project-graphicsپروژهٔ پایانی این دسته

آزمایشگاه ماتریس و شانس

اعمال ماتریسی و اعداد تصادفی در یک برنامهٔ ترکیبی

آخرین درس این دسته را تمام کردی — وقت آن است که همه‌چیز را یک‌جا به محک بزنی. برنامه‌ای بنویس که دو ماتریس مربعی n×n را بخواند، حاصل جمع و ضرب آن‌ها را چاپ کند، بررسی کند آیا حاصل ضرب ماتریس مثلثی پایین است یا نه، و در پایان یک «ماتریس شانس» تصادفی ۰/۱ با احتمال ۵۰٪ تولید کند و تعداد یک‌هایش را به‌عنوان «امتیاز» بشمارد.

⏱ ۷۵ تا ۹۰ دقیقه۶ نیازمندی — هر کدام به یک درس لینک است
شرح کامل پروژه
گام بعدی پیشنهادی

توابع: تعریف، فراخوانی و مثال‌ها

از مفهوم تابع تا پیاده‌سازی فاکتوریل، عدد اول و ترکیب

۴۵ دقیقهمتوسط۱۸ بخش

شروع درس

جستجوی سریع در درس‌ها

نام درس، موضوع یا کلمه کلیدی را بنویسید و با Enter به آن بروید