تصمیمگیری به طور کلی چیزی است که هر انسانی با آن سر و کار دارد، از وقتی که مثلا میخواهیم از بقالی چیپس بخریم گرفته تا انتخاب زمینی که قرار است یک طرح عمرانی بزرگ در آن اجرا شود. در این پست میخواهیم به سراغ علمی به نام تصمیمگیری و به طور مشخص، یکی از زیرشاخههای آن به نام تصمیمگیری چندمعیاره(mcdm) برویم.
mcdm همانطور که احتمالا از اسمش مشخص است هنگامی به کار میرود که تصمیمگیرنده میخواهد چندین معیار را با هم در تصمیمگیری خود لحاظ کند. چندین روش و الگوریتم برای mcdm پیشنهاد شده و در این پست قرار است با یکی از الگوریتمهای نسبتا سادهی mcdm به نام TOPSIS آشنا شویم.
الگوریتم TOPSIS
TOPSIS مخفف این عبارت است:
Technique for Order of Preference by Similarity to Ideal Solution
که اگر بخواهید ترجمهاش کنید میشود: فنی برای مرتبکردن ترجیحات با توجه به شباهتشان به راهحل ایدهآل. این الگوریتم را آقایان یون و هوانگ در سال ۱۹۸۱ معرفی کردهاند و به طور خلاصه از بین چندگزینه، گزینهای که بیشترین شباهت به ایدهآل ذهنی شما و کمترین شباهت به چیزی که در ذهن شما به عنوان بدترین گزینه وجود دارد را انتخاب میکند. به طور خلاصه:
- در این روش m گزینه داریم.
- گزینهای را میخواهیم که نزدیکترین فاصله به ایدهآل مثبت و بیشترین فاصله از ایدهآل منفی را دارد.
پیادهسازی این الگوریتم در R نسبتا ساده است و فقط نیاز به کمی محاسبات ماتریسی دارد(به همین دلیل پیادهسازی آن در متلب و پایتون و زبانهای مشابه هم احتمالا آسان خواهد بود.). در صفحه ویکیپدیای این الگوریتم میتوانید مراحل آن را مشاهده کنید. در قالب یک مثال این الگوریتم را مرحله به مرحله اجرا میکنیم:
فرض کنید شخصی میخواهد از بین سه گوشی LG G5، LG v10 و گالاکسی S7 یک مورد را انتخاب کند.(پینوشت ۱) او معیارهای زیر را برای تصمیمگیری انتخاب نموده است:
معیارهای مثبت | معیارهای منفی |
اندازهی صفحه | وزن |
مگاپیکسل دوربین | قیمت |
شارژدهی هنگام اینترنت | |
قدرت نسبی پردازنده | |
طراحی |
یکی از خوبیهای روش تاپسیس این است که فرقی نمیکند معیارهای شما کمی باشند یا کیفی، در مثال ما قدرت نسبی پردازنده و طراحی مثالهایی کیفی هستند.
مرحله اول: جمعآوری اطلاعات
با کمک سایت gsmArena اطلاعات مورد نیاز را جمع میکنیم و در جدول زیر مینویسیم:
طراحی | قیمت | وزن | cpu | شارژدهی اینترنت | دوربین | اندازه صفحه | معیار|گزینه |
خیلیخوب | ۵۹۹ | ۱۵۹ | خیلیخوب | ۴۵۵ | ۱۶ | ۵.۳ | G5 |
خوب | ۶۹۹ | ۱۹۲ | خوب | ۵۷۴ | ۱۶ | ۵.۷ | V10 |
خیلیخوب | ۶۴۹ | ۱۵۲ | خیلیخوب | ۵۰۰ | ۱۲ | ۵.۱ | s7 |
قدم بعدی تبدیل جدول بالا به یک ماتریس است که بتوان روی آن عملیات ریاضی انجام داد، برای این کار باید به نحوی معیارهای نسبی را به معیارهای کمی تبدیل کنیم، راهحل رایج در mcdm نسبتدادن یک عدد به صفاتی است که استفاده کردهایم، مثلا خیلیخوب برابر ۹ و خوب برابر ۷ و به همین ترتیب خیلی بد برابر ۱ میشود، جدول بالا را با این فرض اصلاح میکنیم:
طراحی | قیمت | وزن | cpu | شارژدهی اینترنت | دوربین | اندازه صفحه | معیار|گزینه |
۹ | ۵۹۹ | ۱۵۹ | ۹ | ۴۵۵ | ۱۶ | ۵.۳ | G5 |
۷ | ۶۹۹ | ۱۹۲ | ۷ | ۵۷۴ | ۱۶ | ۵.۷ | V10 |
۹ | ۶۴۹ | ۱۵۲ | ۹ | ۵۰۰ | ۱۲ | ۵.۱ | s7 |
حالا جدول بالا را در قابل یک ماتریس به R وارد میکنیم. راههای زیادی برای این کار وجود دارد و دمدستیترین راه برای این کار استفاده از دستور matrix در R است:
x <- matrix(c(5.3,5.7,5.1,16,16,12,455,574,500,9,7,9,159,192,152,599,699,649,9,7,9),nrow = 3)
اگر کمی به دستور بالا دقت کنید متوجه میشوید که دادههای ماتریس به ترتیب ستونی نوشته شدهاند، این ترتیب در R و بسیاری از نرمافزارهای آماری ترتیب پیشفرض است، اگر خواستید به ترتیب ردیفی دادهها را وارد کنید بعد از nrow = 3 عبارات byrow= T را وارد کنید.
برای راحتی کار با دستور زیر نامهای ستونهای جداول بالا را نیز به ماتریس خود اضافه میکنیم:
colnames(x) <- c("screen","MP","charge","cpu","weight","price","design")
و با دستور زیر نام گزینهها را به ماتریس اضافه میکنیم:
rownames(x) <- c("g5","v10","s7")
در حال حاضر اگر x را تایپ کنید و اینتر بزنید احتمالا باید ماتریس x را به شکل زیر در کنسول ببینید:
قدم دوم، نرمالسازی ماتریس تصمیمگیری
معیارهای ما در این مثال و تقریبا تمامی مسائلی که در تصمیمگیری پیش میآید مقیاسهای متفاوتی دارند، مثلا مقیاس یکی گرم است و مقیاس دیگری واحد پول، به همین دلیل نیاز است تا به نحوی این مقیاسها را از بین ببریم، از طرف دیگر دامنه معیارها هم با هم متفاوت است، مثلا یک اعداد یکی از معیارها بین ۰ تا ۱۰ قرار دارند و اعداد دیگری بین ۴۰۰ و ۶۰۰، به همین دلیل با عملیاتی به نام نرمالسازی از شر این مشکلات خلاص میشویم. راههای زیاد برای نرمالسازی یا بیمقیاسکردن وجود دارد، برای این مثال از روشی به نام نرم استفاده میکنیم که در آن از فرمول زیر استفاده میشود:
این فرمول در واقع هر عنصر ماتریس را بر جذر مجموع مجذورات همان ستون از ماتریس تقسیم میکند. با دستور زیر این کار را در R انجام میدهیم و آن را در ماتریسی به نام r ذخیره میکنیم:
r <- t(t(x)/sqrt(colSums(x^2)))
حاصل کار ماتریس بیمقیاسشدهی زیر خواهد بود:
قدم سوم، تاثیر دادن اهمیت معیارها
در این مرحله باید کاری کنیم که معیارهایی که برای ما اهمیت بیشتری دارند، در فرایند تصمیمگیری نیز تاثیر بیشتری داشته باشند، این کار در الگوریتمهای تصمیمگیری معمولا با اختصاص دادن یک وزن به هر معیار انجام میشود. چندین الگوریتم مختلف برای تعیین وزن معیارها وجود دارد اما الزامی به استفاده یکی از آنها نیست و خود تصمیمگیرنده نیز میتواند برای هر معیار یک وزن قائل شود. فقط دقت کنید که در الگوریتم TOPSIS و بسیاری از الگوریتمهای دیگر مجموع وزن معیارها باید برابر ۱ باشد. فرض کنید طبق جدول زیر به هر معیار وزنی اختصاص میدهیم:
طراحی | قیمت | وزن | cpu | شارژدهی اینترنت | دوربین | اندازه صفحه |
۰٫۱ | ۰٫۳ | ۰٫۰۵ | ۰٫۱ | ۰٫۲ | ۰٫۰۵ | ۰٫۲ |
جدول بالا را در قالب یک بردار به نام w در R به وجود میآوریم:
w <- c(0.2,0.05,0.2,0.1,0.05,0.3,0.1)
قدم بعدی تاثیر دادن این بردار در ماتریس تصمیمگیری است. این عمل را با ضرب کردن عناصر هر ستون ماتریس با عنصر متناظر آن در بردار وزنها انجام میدهیم و نتیجه را در ماتریسی به نام v ذخیره میکنیم:
v <- t(w * t(r)) > v screen MP charge cpu weight price design g5 0.1139127 0.03123475 0.1026105 0.06195856 0.02722842 0.1595457 0.06195856 v10 0.1225098 0.03123475 0.1294471 0.04818999 0.03287960 0.1861811 0.04818999 s7 0.1096141 0.02342606 0.1127588 0.06195856 0.02602969 0.1728634 0.06195856 >
خط اول کد بالا این کار را برای ما انجام میدهد. بردار وزنها در نتیجه نهایی تاثیرگذار خواهد بود.
قدم چهارم، تعیین ایدهآل مثبت و منفی
تا اینجا یک ماتریس به نام v داریم که آن را بیمقیاس کردهایم و وزنهای مورد نظر ما روی آن اعمال شده است، حالا نوبت تعیین ایدهآلهای مثبت و منفی است. این ایدهآلها دو گزینهی فرضی هستند که به ترتیب بهترین و بدترین مقادیر معیارها را دارند، به زبان ساده در هر ستون ماتریس v بهترین عدد را درنظر میگیریم، این اعداد گزینهی فرضی ایدهآل مثبت هستند و اگر بدترین عددها را انتخاب کنیم گزینهی ایدهال منفی را خواهیم داشت.
در جدول اولیهی ما معیارها به دو گروه مثبت و منفی تقسیم شده بودند،(وزن و قیمت معیارهایی منفی بودند). گزینهی ایدهآل در این معیارهای منفی کمترین مقدار را دارد، به همین دلیل هنگام نوشتن دستوری که قرار است در معیارهای منفی بهترین عدد را مشخص کند باید حواسمان باشد که برخلاف معیارهای مثبت، کوچکترین عدد را انتخاب کند. سادهترین روش استفاده از شرط یا دستور if است اما در R به دلیل پشتیبانی از بردار و ماتریس، در این مورد و بسیاری از موارد دیگر نیازی به تعریف شرط نخواهیم داشت، کافیست به نحوی ستونهای متناظر با معیارهای منفی را در یک ۱- ضرب کنیم تا عدد موردنظر همانند معیارهای مثبت همان بزرگترین عدد باشد. یک بردار مشخصهکننده معیارهای مثبت و منفی تعریف میکنیم و همانند بردار وزن آن را در ماتریس v ضرب میکنیم:
> i <- c(1,1,1,1,-1,-1,1) > vm <- t(i * t(v)) > vm screen MP charge cpu weight price design g5 0.1139127 0.03123475 0.1026105 0.06195856 -0.02722842 -0.1595457 0.06195856 v10 0.1225098 0.03123475 0.1294471 0.04818999 -0.03287960 -0.1861811 0.04818999 s7 0.1096141 0.02342606 0.1127588 0.06195856 -0.02602969 -0.1728634 0.06195856
همانطور که میبینید دو ستونی که شامل معیارهای منفی ما میشد خود منفی شدند، حالا فقط کافی است گزینههای ایدهآل مثبت و منفی را مشخص کنیم و در دوبردار به نامهای AP و AN ذخیره کنیم:
> ap <- (apply(vm,MARGIN = 2,FUN = max)) * i > ap screen MP charge cpu weight price design 0.12250984 0.03123475 0.12944708 0.06195856 0.02602969 0.15954574 0.06195856 > an <- (apply(vm,MARGIN = 2,FUN = min)) * i > an screen MP charge cpu weight price design 0.10961407 0.02342606 0.10261049 0.04818999 0.03287960 0.18618109 0.04818999
دو نکته دربارهی کد بالا، خانوادهی apply یک خانواده از دستورات در R هستند که نقش حلقه را ایفا میکنند و در آیندهی نزدیک در پستی دربارهی آنها خواهم نوشت، و نکته دیگر این که بلافاصله بعد از مشخص شدن ایدهآلهای منفی و مثبت، آنها را در بردار i ضرب کردیم تا تاثیر تغییری که به خاطر استفاده نکردن از شرط در الگوریتم داده بودیم از بین برود.
قدم پنجم، محسابهی فاصلهی هر گزینه از گزینههای ایدهآل مثبت و منفی
در این مرحله فاصلهی اقلیدسی هر گزینه(در واقع ردیفهای ماتریس v) را از گزینههای مثبت و منفی محاسبه میکنیم و در دو بردار dp و dn ذخیره میکنیم. فرمول فاصله همان فرمول فاصلهی دو نقطه در فضا در ریاضی دبیرستان است، و با تابع apply که در مرحله قبلی استفاده کردیم به راحتی میتوان محاسبات لازم را بدون نوشتن حلقه انجام داد:
> dp <- apply(v,1,function(x)sqrt(sum((x-ap)^2))) > dp g5 v10 s7 0.02820551 0.03369733 0.02613688 > dn <- apply(v,1,function(x)sqrt(sum((x-an)^2))) > dn g5 v10 s7 0.03464070 0.03078115 0.02657850
قدم آخر، محاسبهی شاخص تصمیمگیری
آخرین مرحله محاسبهی شاخص c برای هر گزینه با فرمول c=dn/dp+dn است، همین فرمول را عینا در R مینویسیم، گزینهای که بیشترین c را داشت گزینهی برتر است:
> c <- dn / (dp + dn) > sort(c,decreasing =TRUE) g5 s7 v10 0.5511979 0.5041887 0.4773864
دستور sort عناصر بردار شما را بر اساس اندازه مرتب میکند، همانطور که میبینید گزینهی G5 در اینجا بیشترین مقدار c را دارد و بهترین گزینه است.
- پینوشت یک: گزینههای مثال صرفا به خاطر از یک نسل بودن و شباهت نسبی انتخاب شدهاند و جنبهی تبلیغاتی ندارند.
- پینوشت دو: معیارها هم صرفا به علت راحتی در محاسبات انتخاب شدهاند و نگارنده به خوبی از بیاهمیت بودن مگاپیکسل دوربین آگاه است
- پینوشت سه: اگر وزنها و معیارهای کیفی را طبق نظر خودتان تغییر دهید احتمالا به جوابی متفاوت برسید.