برنامه محاسبه معکوس ماتریس n * n ( زبان C# )


در پست های قبل نحوه به دست آوردن معکوس یا واران یک ماتریس 3 در 3 یا بیشتر و همچنین شروط آن را یادآوری کردیم. در این پست هم برای کسانی که مانند من علاقه دارند برنامه محاسبه را خودشان به صورت دلخواه بنویسند، نکاتی را توضیح خواهیم داد. هدف اصلی من از این چند پست، افزایش مهارت در برنامه نویسی با بهانه تعیین معکوس یا واران یک ماتریس بود که سوژه خوبی برای برنامه نویسی در سطوح مقدماتی است؛ در این میان یک یادآوری هم از دروس دوران دبیرستان شد. در انتهای پست نیز میتوانید نسخه ای از برنامه ای که من برای این چند پست نوشته ام را داشته باشید. که به احتمال زیاد بدون مشکل نخواهد بود.

اولین کار برای محاسبه معکوس ماتریس به دست آوردن ماتریس الحاقی است. ماتریس الحاقی ماتریسی هم مرتبه ماتریس ورودی است که درایه های آن از انجام 3 عملیات زیر به دست می آید :

1 : هر درایه برابر است با کهاد ماتریس نسبت به سطر و ستون آن درایه.
2 : درایه های ماتریس با شرط اینکه، "اگر سطر + ستون درایه یا i+ j فرد بود" تغییر علامت می یابند.
3 : درایه های نسبت به قطر اصلی جابه جا می شوند. مانند اینکه قطر اصلی را محور تقارن ماتریس در نظر بگیریم.
برای دو تغییر اول فرمول زیر استفاده می شود و ماتریسی که از این اعمال این دو تغییر به دست می یابد را ماتریس همسازه گفته می شود.


A i, j = (-1) ᴵ + ᴶ x | M i ,j |


من تابع محاسبه ماتریس همسازه را اینگونه نوشته ام :


private double[,] Hamsaze(double[,] matrix)
{
int Size = (int)Math.Sqrt(matrix.Length);
double[,] result = new double[Size, Size];
for (int i = 0; i < Size; i++)
{
   for (int j = 0; j < Size; j++)
   {
      result[i, j] = Math.Pow(-1, (i+1 + j+1)) * Determinant(Kehad(matrix, i, j));
   }
}
return result;
}


در این تابع دو تابع دیگر به نام Determinant و Kehad را مشاهده می کنید. که از نامشان پیداست خروجی انها چیست.
تابع دترمیان :

private double Determinant(double[,] matrix)
{
double determinant = 0;
int Size = (int)Math.Sqrt(matrix.Length);
if (Size == 1)
{
   return matrix[0, 0];
}
else if (Size == 2)
{
   return (matrix[0, 0] * matrix[1, 1] - matrix[0, 1] * matrix[1, 0]);
}
else
{
  for (int i = 0; i < Size; i++)
     determinant += (int)Math.Pow(-1, (i + 1) + 1) * matrix[0, i] * Determinant(Kehad(matrix, 0, i));
}
return determinant;
}


تابع کهاد :


private double[,] Kehad(double[,] matrix, int ROW, int COL)
{
int Size = (int) Math.Sqrt(matrix.Length);
double[,] result = new double[Size - 1, Size - 1];
int x = -1, y = 0;
for (int i = 0; i < Size; i++)
{
   if (i == ROW)
   continue;
   x++;
   y = 0;
    for (int j = 0; j < Size; j++)
    {
      if (j == COL)
      continue;
      result[x, y++] = matrix[i, j];
    }
}
return result;
}

تابعی که برای اعمال تغییر سوم (به همراه دو تغییر اول) به کار برده ام Adjugate نام دارد و کاری که می کند، جابه جا کردن درایه های ماتریس همسازه نسبت به قطر اصلی آن است.

private double[,] Adjugate(double[,] matrix, int row, int col)
{
// first step
matrix = Hamsaze(matrix);

//second step
double temp = 0;
for (int i = 0; i < row; i++)
   {
    for (int j = i - 1; j >= 0; j--)
    {
      temp = matrix[j, i];
      matrix[j, i] = matrix[i, j];
      matrix[i, j] = temp;
    } 
   }  
return matrix;
}

با به دست آوردن ماتریس الحاقی تنها کاری که باید بکنیم این است که معکوس دترمینان ماتریس ورودی را در ماتریس الحاقی ضرب کنیم. این کار ساده است چون تنها باید این عدد را در تک تک درایه های ماتریس الحاقی ضرب کنیم. برای مثال ضرب یک عدد در ماتریس می تواند با کد زیر انجام شود. (البته واضح است که در تمام توابع بالا و تابع زیر سطر و ستون ماتریس یا row و col با یکدیگر برابر خواهد بود چون مربعی بودن ماتریس، پیش شرط وجود این مشتقات است.)  

فرمول محاسبه ماتریس معکوس : 

Inv(matrix) = 1 / Determinant(matrix)  x  Adjugate-matrix

ضرب یک عدد در ماتریس :

number : عدد مورد نظر

for (int i = 0; i < row; i++)
   for (int j = 0; j < col; j++)
   {
      matrix[i, j] *= number;
   }

در پایان می توانید از لینک زیر برنامه ی ساده ای که برای محاسبه معکوس ماتریس در محیط دات نت نوشته ام را دانلود و تست کنید. البته باید آخرین نسخه دات نت را برای اجرای این برنامه داشته باشید.

معکوس ماتریس 3 * 3 و بالاتر ( n * n )


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

پیش شرط :
همه می دانید که اولا ماتریس شما باید یک ماتریس مربعی باشد و دوما دترمینان آن مخالف صفر 0 باشد. اگر این دو شرط را رعایت کنید ماتریس شما واران پذیر خواهد بود.

محاسبه :
اگر اطلاعاتی در مورد دترمینان و ماتریس الحاقی ندارید می توانید از لینک های ابتدای پست استفاده کنید یا در اینترنت در مورد آنها جست و جو کنید چون دیگر در این پست در مورد انها صحبت نمی کنیم. فرمول زیر برای محاسبه دترمینان به کار میرود.


Inv (matrix) = 1 / Det(matrix) x Adjugate-matrix

Inv (matrix) : ماتریس معکوس
Det (matrix) : دترمینان ماتریس
Adjugate-matrix : ماتریس الحاقی


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

Effective Memory Clock چیست ؟


Effective Memory Clock یا "فرکانس موثر حافظه"، نقطه ی مقابل Real Memory Clock یا "فرکانس واقعی حافظه"، به فرکانس کلی یک حافظه تصادفی (RAM) گفته می شود که از ضرب فرکانس واقعی، در تعداد واحد های انتقال یافته اطلاعات در هر چرخه (کلاک) به دست می آید. برای مثال در رم های DDR - Double Data Rate فرکانس موثر از ضرب فرکانس واقعی در 2 به دست می آید چون این حافظه ها در هر چرخه می توانند دو واحد اطلاعات را انتقال دهند و یا در حافظه های QDR - Quad Data Rate فرکانس موثر از ضرب فرکانس واقعی در 4 به دست می آید چون این حافظه ها در هر چرخه 4 واحد اطلاعات را منتقل می کنند.    



ماتریس الحاقی


ماتریس الحاقی یا Adjugate Matrix یا Adjoint Matrix از معکوس کردن درایه های ماتریس همسازه نسبت به قطر اصلی به دست می آید.

یاد آوری ماتریس همسازه :
در پست های قبل در مورد ماتریس همسازه یا همسازه ماتریس صحبت کرده بودیم و گفته بودیم که ماتریس همسازه ماتریسی است که درایه های آن به وسیله ی فرمول زیر به دست می آید.


A i, j = (-1) ᴵ + ᴶ x | M i ,j |

که در آن M i ,j به معنی i و j اومین کهاد ماتریس مورد نظر می باشد و از حذف سطر i ام و ستون j ام ماتریس اولیه به دست می آید. پس از محاسبه تک تک درایه ها با استفاده از فرمول بالا، ماتریس همسازه ماتریس موردنظر به دست می آید.

معکوس کردن درایه ها نسبت به قطر اصلی : 

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



ماتریس به دست آمده از اعمال این تغییر را ماتریس الحاقی می گویند که در محاسبه ماتریس واران، یک ماتریس کاربرد دارد.  

فناوری GPU Boost


در سال های قبل GPU ها با فرکانس ثابتی فعالیت می کردند و آن فرکانسی بود که شرکت سازنده برای انها در نظر گرفته بود. البته می توانستیم این فرکانس را با اورکلاک افزایش دهیم ولی این نیز به این معنی بود که باز GPU در هنگام کار، با فرکانس معین و ثابتی فعالیت می کند، و صد البته که اورکلاک دستی GPU برای کاربر های مبتدی می تواند با خطراتی مانند عبور از نقطه تحمل حرارتی همراه باشد. 


فناوری GPU Boost از شرکت Nvidia که مشابه فناوری Turbo Boost از شرکت اینتل است و احتمالا با آن بیشتر آشنا هستید، و با معماری کپلر (Kepler) این شرکت معرفی شد، یک فناوری تنظیم فرکانس یا بهتر است بگوییم اورکلاکینگ به صورت هوشمند و دینامیک است که فرکانس GPU و فرکانس رم کارت گرافیک را در بر می گیرد. این فناوری چندین پارمتر مانند دمای کنونی و آستانه تحمل تراشه، حجم کار تراشه، حجم رم استفاده شده و ... را برای تنظیم فرکانس GPU در نظر می گیرد و فرکانس را متناسب با آنها و نیاز پردازش تغییر می دهد. اورکلاکینگ دینامیک به شما اجازه می دهد تا از یک طرف از حداکثر توان کارت گرافیک در لحضاتی که نیازمند پردازش بالا هستید استفاده کنید و از طرف دیگر در مواقعی که نیاز به پردازش بالا نیست، در مصرف انرژی صرفه جویی کنید.  
src=

کاربرد دوگانه ی عملگر های & و * در زبان C++


شاید قبلا در مورد کاربرد دوگانه عملگر های & و * در زبان برنامه نویسی C++ صحبت کرده باشم ولی این پست یک یاداوری در مورد این دو عملگر است. این دو عملگر می توانند به صورت پیشوند یا پسوند به کار روند و در این صورت عملکرد های متفاوتی خواند داشت.


عملگر & به صورت پسوند نوع: (یا پیشوند متغیر در هنگام اعلان)
در این صورت، & برای تعریف یک نام مستعار یا تعریف یک پارمتر از نوع ارجاع به کار می رود. در زیر مثالی از موضوع را مشاهده می کنید.


تعریف یک نام مستعار برای یک متغیر :
int num = 100;
int& mirror = num;  // or :  int  &mirror = num; 

تعریف یک پارمتر به صورت ارجاع :
void Function (int& Parameter1);



عملگر & به صورت پشوند متغیر :
در این صورت این عملگر آدرس متغیر موردنظر را بر می گرداند. در زیر مثالی از این موضوع را مشاهد می کنید.



بازگرداندن آدرس یک متغیر :
int num = 100;
cout << & num;


عملگر * به صورت پسوند نوع : (یا پیشوند متغیر در هنگام اعلان) 

در این صورت این عملگر برای تعریف یک اشاره گر به کار می رود. در زیر مثالی از این موضوع را مشاهد می کنید.


تعریف یک اشاره گر :
int num = 100;
int* ptr = & num;  // or : int *ptr = & num;


عملگر * به صورت پشوند متغیر :
در این صورت این عملگر مقدار متغری که اشاره گر به آن اشاره می کند. برای مثال اشاره گری که در بالا تعریف کردیم را در نظر بگیرید.


بازگرداندن مقدار متغیری که اشاره گر به آن اشاره دارد :
cout << * ptr;


 

ایجاد و راه اندازی Event

همانطور که قبلا گفتیم، عملیات اطلاع رسانی یا سیگنالیگ یک Event (رخداد) شامل یک دلیگیت (Delegate) میشود و دلیگیت، در بردارنده ی اشاره گر هایی به توابعی است که بایستی در هنگام رخ دادن Event فراخوانی شوند. اکثر Event هایی که ما به آنها سرو کار داریم مربوط به کنترل های برنامه مانند دکمه ها، منو ها و تکست باکس ها و ... مربوط می شوند و هنگامی رخ می دهند که کاربر با انها تعاملی داشته باشد؛ اما  Event ها را  همچنین میتوان در کد های برنامه ایجاد و راه اندازی کرد.
یک Event عضوی از یک کلاس ارجاع (Reference) است که به وسلیه ی کلمه Event و نام کلاس دلیگیت، تعریف می شود. در زیر نمونه ی ساده ای از تعریف Event را مشاهده می کنید.

public delegate void HANDLER();


// Class with an event member
public ref class EventClass
{
public:
// declaring the event
event HANDLER^ EVENT;

// Function to trigger event
void TriggerEvent()
{
   EVENT();
}
};

   

کلاس EventClass دارای یک Event وهله ای (instance) به نام EVENT است که با استفاده از دلیگیت HANDLER تعریف شده است و با آن در ارتباط است. و تابع عمومی TriggerEnevt مسئول راه اندازی این Event و در این مثال، بدون پارامتر است. هنگامی که EVENT راه اندازی می شود، میتواند توابعی که در لیست دلیگیت هستند را فراخوانی کند و همچنین نوع خروجی تعریف شده آن را داشته باشد. همینطور که می بینید راه اندازی Event مشابه فراخوانی دلیگیت است. برای جواب دادن به این Event میتوان کلاسی مشابه زیر ایجاد کرد :


public ref class AnswerClass

{
public:
void answer()
{
   Console::WriteLine("Event triggered");
}
};

کلاس AnswerClass دارای یک تابع عمومی به نام answer است که می تواند برای جواب دادن به Event به کار رود به این خاطر که دارای پارمتر ها و نوع بازگشتی یکسان با دلیگیت تعریف شده ما است.(در این مثال برای سهولت، هر دو بدون پارامتر و نوع بازگشتی هستند). 

به کار گیری Event :
قبل از اینکه بتوانیم از این تابع استافده کنیم باید یک شی از کلاس EventClass ایجاد کنیم. خط اول کد زیر این کار را انجام می دهد. بعد از اینکار میتوانیم این تابع را برای مدیریت Event خود ثبت کنیم.


EventClass^ obj = gcnew EventClass;

AnswerClass^ answer = gcnew AnswerClass;
obj -> EVENT += gcnew HANDLER(answer, & AnswerClass::answer);
obj ->TriggerEvent();

خط دوم کد بالا یک شی از کلاس AnswerClass ایجاد می کند، چون تابع answer یک تابع وهله ای (غیر استاتیک) است و سپس در خط بعد نمونه ای (وهله ای) از دلیگیت HANDLER را به همراه تابع مدیریت کننده خود را به Event تعریف شده در کلاس EventClass اضافه می کنیم؛ به عبارت دیگر تابع answer را برای Event خود ثبت می کنیم. در خط بعد با استفاده از تابع TriggerEvent آن را راه اندازی می کنیم. با فراخوانی این تابع، EVENT راه اندازی می شود و نتیجه ی آن، فراخوانی تابع answer است و در نتیجه عبارت "Event triggerd" در خروجی چاپ می شود.

منبع : Ivor Horton's beginnig visual C++ 2010

دلیگیت های آزاد - Unbound Delegates


در پست های قبل دلیگیت هایی که تعریف کردیم از نوع "دلیگیت های محدود" یا Bound Delegates بودند، زیرا آنها مجموعه ی ثابتی از توابع را در لیست فراخوانی خود داشتند. یک دلیگیت "نامحدود" یا "آزاد" به تابعی وهله ای (Instance) اشاره می کند که دارای لیست پارامتر مشخص شده و نوع بازگشتی برای یک نوع شی مشخص شده است. بنابراین یک دلیگیت یکسان میتواند برای فراخوانی توابع وهله ای برای هر شی مشخص شده به کار رود. در زیر نمونه ای از تعریف یک دلیگیت آزاد را مشاهده می کنید.

public delegate void HANDLER (HandlerClass^ , int value);

اولین آرگومان نوع اشاره گر this را مشخص می کند، برای اینکه یک دلیگیت از نوع HANDLER بتواند یک تابع وهله ای (غیر استاتیک) را فراخوانی کند. این تابع باید یک آرگومان از نوع int و نوع بازگشتی void داشته باشد. بنابراین یک دلیگیت از نوع HANDLER تنها میتواند یک تابع برای یک شی از کلاس HandlerClass را فراخوانی کند ولی برای هر شی از این نوع. این ممکن است کمی محدود کننده به نظر برسد ولی در بسیار مفید خواهد بود. فرض کنید HandlerClass ساختاری مشابه زیر دارد ...

ref class HandlerClass
{public:
// constructors ...
....
// functions
void Function1 (...)
{// do something}
void Function2 (...)
{// do something}
...
}

میتوان یک دلیگیت از نوع HANDLER را به صورت زیر ایجاد کرد:

HANDLER^ handler = gcnew HANDLER (&HandlerClass::Function1);

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

HandlerClass^ obj = gcnew HandlerClass ("parameters");
handler (obj, "value");

اولین آرگومان یک هندل به شی کلاس HandlerCalss است که شما با ارسال پارامتر ها به سازنده کلاس، ایجاد کردید که تعداد آنها بسته به تعریف سازنده است. دومین ارگومان مقداری است که به تابع موردنظر ارسال می کنید.
دلیگیت های آزاد را نیز میتوان با استفاده از عملگر های + و - در یکدیگر ادغام یا از یکدیگر تفکیک کرد. برای مثال بالا فرض کنید که بخواهید تابع دیگری از کلاس را به لیست دلیگیت اضافه کنید. با استفاده از عملگر + این کار به صورت زیر امکان پذیر است.

handler += gcnew HANDLER (&HandlerClass::Function2);

رزولوشن (تفکیک پذیری) 4K و 8K


شاید چند سال پیش خیلی ها از جمله خود من با اصطلاحات HD و FullHD آشنا نبودند ولی در حال حاضر به حدی این فرمت های گشترش پیدا کرده و معمول شده اند که وقتی که میخواهیم یک تلویزیون جدید خریداری کنیم یا ویدئویی را دانلود کنیم ابتدا به سراغ وضوح تصویر آن می رویم و این کاملا هم طبیعی است. فرمت های HD و FullHD ، به مرحله ی اشباه و تکامل خود رسیده اند و شرکت های سازنده ی تلویزیون و نمایشگر مدتی است که به سراغ فرمت های جدید رفته اند و برخی هم محصولاتی را با این فرمت ها وارد بازار کرده اند. البته در کنار آنها، دوربین های حرفه ای جدید نیز از این فرمت ها پشتیبانی می کنند، تا دچار کمبود محتوا برای این تلویزیون ها و نمایشگر ها نشویم!  ( 2K : تفکیک پذیری 1080*2048 برای سینما)  


 اصطلاحات 4K و 8K که این روز ها بیشتر و بیشتر شنیده می شوند، و یا با نام Ultra HD یا UHD بیان می شوند، به رزولوشن یا تفکیک پذیری یا وضوح تصویر جدیدی اطلاق می شود که پس از وضوح تصویر Full HD درحال معمول شدن هستند. اصطلاحا 4K به معنی وجود تقریبا 4000 هزار پیکسل، و 8K وجود 8000 هزار پیکسل فیزیکی در یک ردیف افقی در طول صحفه نمایش؛ و اگر آن را در مورد ویدئو ها به کار ببریم به همین میزان پیکسل افقی، در ویدئو است. تفکیک پذیری دقیق 4K و 8K و در نتیجه تعداد کل پیکسل ها در این دو فرمت به "نسبت تصویر" صفحه یا ویدئو بستگی دارد ولی برای مثال 4K در نسبت تصویر 16:9 برای تلویزیون ها برابر با 3840 x 2160 است که تقریبا دو برابر تفکیک پذیری FullHD برای همین نسبت تصویر است. تصویر بالا فرمت 4K را از نظر اندازه با فرمت های معمول مقایسه کرده است و طبق آن میتوانید به حد و حدود فرمت 8K با 33 میلیون پیکسل پی ببرید!

اگر بخواهیم این فرمت ها را با تعداد پیکسل های عمودی بیان کنیم این چنین خواهد بود :


HD : 720 pixels
FullHD : 1080 pixels
UltraHD-4K : 2160 pixels
UltraHD-8K : 4320 pixels


عملگر های + و - در delegate


عملگر + به نحوی برای delegate سربارگذاری شده است تا لیست توابع دو delegate را در یک شی delegate ترکیب کند. برای مثال کلاس زیر را در نظر بگیرید که دارای دو تابع عضو است.


class HandlerClass
{public:
 static void Func1()
 {
   Console::WriteLine("First function called\n");
 }
 static void Func2()
 {
   Console::WriteLine("Second function called\n");
 }
};


همچنین delegate را به صورت زیر تعریف کرده ایم :

public delegate void HANDLER ();


حالا یک بار delegate را با تابع اول (Func1) تعریف می کنیم و آن را فراخوانی می کنیم.


HANDLER^ handler = gcnew HANDLER (HandlerClass::Func1);
handler();

چون تابع اول تنها تابع در لیست فراخووانی delegate است، خروجی به صورت زیر خواهد بود :

"First function called"

حالا با استفاده از عملگر + تابع دوم را به لیست توابع delegate اضافه می کنیم.

handler += gcnew HANDLER(HandlerClass::Func2);

الان متغیر handler به دلیگیتی اشاره دارد که دو تابع را در لیست توابع خود دارد. البته این دستور یک delegate جدید ایجاد می کند. حالا دوباره آن را فراخوانی می کنیم. 

handler();

حالا این خروجی را خواهید گرفت :

"First function called"
"Second function called"

هر دو تابعی که که در لیست دلیگیت وجود دارند به ترتیبی که در آن قرار گرفته اند فراخوانی می شوند.
عملگر - نیز عملکردی مشابه عملکرد + دارد، البته واضح است که اینبار توابع مشخص شده به صورت ارگومان، از لیست توابع delegate خذف می شوند. برای مثال دستور زیر تابع Func2 را از لیست handler حذف می کند.

handler -= gcnew HANDLER(HandlerClass::Func2);

در برنامه نویسی با فناوری Windows Forms بسیار از این دو عملگر برای ایجاد و به کار گیری Event های مختلف استفاده می شود.