ماتریس (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() برای دسترسی به ابعاد در دسترس است.
برنامهای بنویسید که یک ماتریس 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 به آن بروید
// آرایه با اندازه ثابتint a[10][20];// 10 سطر، 20 ستون// با مقداردهی اولیهint b[2][3]={{1,2,3},{4,5,6}};// با std::vector (پیشنهادی، اندازه متغیر)vector<vector<int>>c(n,vector<int>(m));
1#include<iostream>2#include<vector>3usingnamespace std;45intmain(){6// ماتریس 2 سطر و 3 ستون7 vector<vector<int>> a ={8{1,2,3},9{4,5,6}10};1112// چاپ ماتریس13for(int i =0; i <2; i++){14for(int j =0; j <3; j++)15 cout << a[i][j]<<" ";16 cout << endl;17}18return0;19}20
for(int i =0; i < n; i++)// پیمایش سطرهاfor(int j =0; j < m; j++)// پیمایش ستونهای هر سطر cin >> a[i][j];
1#include<iostream>2#include<vector>3usingnamespace std;45intmain(){6int n, m;7 cout <<"n m: ";8 cin >> n >> m;910 vector<vector<int>>a(n,vector<int>(m));1112// دریافت عناصر13for(int i =0; i < n; i++)14for(int j =0; j < m; j++)15 cin >> a[i][j];1617// چاپ ماتریس18 cout <<"Matrix:\n";19for(int i =0; i < n; i++){20for(int j =0; j < m; j++)21 cout << a[i][j]<<"\t";22 cout <<"\n";23}24return0;25}26
C[i][j]= Σ (k=0 to n-1) A[i][k]* B[k][j]
1#include<iostream>2#include<vector>3usingnamespace std;45intmain(){6int m, n, l;7 cout <<"m n l: ";8 cin >> m >> n >> l;910 vector<vector<int>>a(m,vector<int>(n));11 vector<vector<int>>b(n,vector<int>(l));12 vector<vector<int>>c(m,vector<int>(l,0));// با صفر مقداردهی اولیه1314 cout <<"Matrix A ("<< m <<"x"<< n <<"):\n";15for(int i =0; i < m; i++)16for(int j =0; j < n; j++)17 cin >> a[i][j];1819 cout <<"Matrix B ("<< n <<"x"<< l <<"):\n";20for(int i =0; i < n; i++)21for(int j =0; j < l; j++)22 cin >> b[i][j];2324// محاسبه ضرب25for(int i =0; i < m; i++)26for(int j =0; j < l; j++){27 c[i][j]=0;28for(int k =0; k < n; k++)29 c[i][j]+= a[i][k]* b[k][j];30}3132// چاپ نتیجه33 cout <<"A * B:\n";34for(int i =0; i < m; i++){35for(int j =0; j < l; j++)36 cout << c[i][j]<<" ";37 cout <<"\n";38}39return0;40}41
flow.svg100%
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
شروع/پایانپردازشتصمیمورودی/خروجی
det = a*d - b*c
1#include<iostream>2#include<vector>3usingnamespace std;45intmain(){6 vector<vector<int>>a(2,vector<int>(2));78for(int i =0; i <2; i++)9for(int j =0; j <2; j++)10 cin >> a[i][j];1112int det = a[0][0]* a[1][1]- a[0][1]* a[1][0];13 cout <<"Determinant: "<< det << endl;14return0;15}16
1// عنوان در جزوه: "حاصلضرب دو ماتریس"2// اما در واقع دترمینان را محاسبه میکند!3main(){4int a[2][2];5for(int i =0; i <2; i++)6for(int j =0; j <2; j++)7 cin >> a[i][j];8 cout << a[0][0]* a[1][1]- a[0][1]* a[1][0];9}10// مشکل: عنوان و محتوا مطابقت ندارند
1// نسخه اصلاحشده: محاسبه دترمینان با عنوان صحیح2#include<iostream>3#include<vector>4usingnamespace std;5intmain(){6 vector<vector<int>>a(2,vector<int>(2));7for(int i =0; i <2; i++)8for(int j =0; j <2; j++)9 cin >> a[i][j];10int det = a[0][0]* a[1][1]- a[0][1]* a[1][0];11 cout <<"Determinant = "<< det << endl;12return0;13}14// برای ضرب واقعی، به کد «پیادهسازی ضرب دو ماتریس» در بالا مراجعه کنید.
1#include<iostream>2#include<vector>3usingnamespace std;45intmain(){6int n, m;7 cin >> n >> m;89 vector<vector<int>>a(n,vector<int>(m));10for(int i =0; i < n; i++)11for(int j =0; j < m; j++)12 cin >> a[i][j];1314for(int i =0; i < n; i++){15int lmax =0;// فرض: اندیس 0 بزرگترین است16for(int j =0; j < m; j++){17if(a[i][j]> a[i][lmax])18 lmax = j;19}20 cout <<"Row "<< i <<": max at index "<< lmax
21<<" (value = "<< a[i][lmax]<<")\n";22}23return0;24}25
1// نسخه دارای اشکال در جزوه2for(int i =0; i < n; i++){3int lmax =0;4for(int j =0; j < n; j++){// باید j < m باشد5if(a[i][j]> a[i][lmax])6 lmax = j;7}8 cout << a[i][lmax]<<"\t";// باید lmax چاپ شود9}
1// نسخه اصلاحشده2for(int i =0; i < n; i++){3int lmax =0;4for(int j =0; j < m; j++){// صحیح: m ستون داریم5if(a[i][j]> a[i][lmax])6 lmax = j;7}8 cout <<"Row "<< i <<": max index = "<< lmax <<"\n";9}
1#include<iostream>2#include<vector>3usingnamespace std;45intmain(){6int m, n, k, l;7 cout <<"A: m n, B: k l: ";8 cin >> m >> n >> k >> l;910if(n != k){11 cout <<"ضرب ممکن نیست: ستونهای A ("<< n
12<<") برابر سطرهای B ("<< k <<") نیست.\n";13return0;14}1516 vector<vector<int>>a(m,vector<int>(n));17 vector<vector<int>>b(k,vector<int>(l));18 vector<vector<int>>c(m,vector<int>(l,0));1920 cout <<"A:\n";21for(int i =0; i < m; i++)22for(int j =0; j < n; j++)23 cin >> a[i][j];2425 cout <<"B:\n";26for(int i =0; i < k; i++)27for(int j =0; j < l; j++)28 cin >> b[i][j];2930for(int i =0; i < m; i++)31for(int j =0; j < l; j++){32 c[i][j]=0;33for(int p =0; p < n; p++)34 c[i][j]+= a[i][p]* b[p][j];35}3637 cout <<"C = A * B:\n";38for(int i =0; i < m; i++){39for(int j =0; j < l; j++)40 cout << c[i][j]<<" ";41 cout <<"\n";42}43return0;44}45
1// نسخه دارای اشکال در جزوه2int m, n, k, l;3cin >> m >> n >> k >> l;4if(n == k){5int a[m][n];6int b[k][l];7int c[m][l]={0};// فقط سطر اول صفر میشود!89for(int i =0; i < m; i++)10for(int j =0; j < k; j++)// باید j < n باشد11 cin >> a[i][j];12// مقادیر b هرگز از ورودی گرفته نمیشوند!1314for(int i =0; i < m; i++)15for(int j =0; j < l; j++)16for(int k =0; k < n; k++)// k ورودی سایه میافتد!17 c[i][j]+= a[i][k]* b[k][j];18}
1// نسخه اصلاحشده2vector<vector<int>>a(m,vector<int>(n));3vector<vector<int>>b(k,vector<int>(l));4vector<vector<int>>c(m,vector<int>(l,0));// همه صفر56for(int i =0; i < m; i++)7for(int j =0; j < n; j++)// اصلاح: n نه k8 cin >> a[i][j];9for(int i =0; i < k; i++)10for(int j =0; j < l; j++)11 cin >> b[i][j];// دریافت b که جا افتاده بود1213for(int i =0; i < m; i++)14for(int j =0; j < l; j++){15 c[i][j]=0;16for(int p =0; p < n; p++)// تغییر نام شمارنده به p17 c[i][j]+= a[i][p]* b[p][j];18}
0001002103214321
1#include<iostream>2#include<vector>3usingnamespace std;45intmain(){6int n;7 cin >> n;8 vector<vector<int>>a(n,vector<int>(n,0));910for(int i =0; i < n; i++)11for(int j =0; j < n; j++){12if(i + j >= n -1)13 a[i][j]= n - j;14else15 a[i][j]=0;16}1718// چاپ ماتریس19for(int i =0; i < n; i++){20for(int j =0; j < n; j++)21 cout << a[i][j]<<" ";22 cout <<"\n";23}24return0;25}26
flow.svg100%
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
شروع/پایانپردازشتصمیمورودی/خروجی
x = a +rand()%(b - a +1);
srand((unsigned)time(NULL));
1#include<iostream>2#include<vector>3#include<cstdlib>4#include<ctime>5usingnamespace std;67intmain(){8srand((unsigned)time(NULL));910constint N =8;11 vector<vector<int>>a(N,vector<int>(N));1213for(int i =0; i < N; i++)14for(int j =0; j < N; j++)15 a[i][j]=1+rand()%99;// 1 تا 991617// چاپ ماتریس18for(int i =0; i < N; i++){19for(int j =0; j < N; j++)20 cout << a[i][j]<<"\t";21 cout <<"\n";22}23return0;24}25
1#include<iostream>2#include<vector>3#include<cstdlib>4#include<ctime>5usingnamespace std;67intmain(){8srand((unsigned)time(NULL));910constint N =8;11 vector<vector<int>>a(N,vector<int>(N,0));1213// قرار دادن 4 عدد 1 در موقعیت تصادفی14for(int i =0; i <4; i++)15 a[rand()% N][rand()% N]=1;1617// چاپ ماتریس18 cout <<"Matrix:\n";19for(int i =0; i < N; i++){20for(int j =0; j < N; j++)21 cout << a[i][j]<<" ";22 cout <<"\n";23}2425// جستجوی خطی از (x, y) به راست26int x, y;27 cout <<"x y: ";28 cin >> x >> y;2930int j = y;31int k =0;// شمارنده گامها32while(j < N && a[x][j]!=1){33 j++;34 k++;35}3637if(j < N && a[x][j]==1)38 cout <<"پیدا شد در ستون "<< j
39<<" بعد از "<< k <<" گام\n";40else41 cout <<"تا انتهای سطر (گام: "<< k <<") پیدا نشد\n";42return0;43}44
1// نسخه دارای اشکال در جزوه2int x, y, k =0;3cin >> x >> y;4j = y;// خطا: j تعریف نشده است5while(a[x][j]!=1&& j <=7){6 j++;7if(j !=8)8 k++;9}10// مشکل: j تعریف نشده و منطق افزایش k پیچیده است
1// نسخه اصلاحشده2int x, y;3cin >> x >> y;4int j = y;// تعریف صحیح5int k =0;6while(j < N && a[x][j]!=1){7 j++;8 k++;// هر گام به جلو را میشمارد9}10if(j < N && a[x][j]==1)11 cout <<"پیدا شد\n";12else13 cout <<"پیدا نشد\n";
vector<vector<int>>a(n,vector<int>(m,0));// n سطر، m ستون، همه با صفر مقداردهی شدهاند