1. 程式人生 > >給你一個演算法應該怎麼寫程式(數學建模 matlab)

給你一個演算法應該怎麼寫程式(數學建模 matlab)

本文寫作初衷:有個可愛的學妹問我在看到一個演算法程式設計時的步驟是什麼,然而作為偽大神的我,感覺並不知道怎麼回答,感覺我程式設計就倆字死磕,錯了咋辦?再來一遍!不行咋辦?換個寫法試試!還不行?百度!不過既然人家誠心誠意的問了 o( ̄▽ ̄)o,還是說點建設性的經驗。

演算法是什麼?

  • 演算法是處理解決問題的思路及辦法,程式語言是按照一定語法把演算法表達來。
  • 打個比方,你頭腦裡有了一套新思想,一個新發現,你可以用中文寫出來,也可以用英文寫出來,讓大家明白。思想和發現可以比作是演算法,用中文或英文可以比作是程式語言。
  • 因此核心是演算法,但程式語言是實現演算法的載體。在計算機等系統中,演算法是處理某一問題的思路方法,而程式語言能具體表達演算法從而使之執行起來通過演算法需要完成的任務。

前提假設:

要實現的這個演算法網上能搜到大量的資料,比如演算法的思想是什麼,使用這個演算法的例子(翻書太慢,不夠豐富,不推薦)。

1. 給你一個演算法,在動手程式設計之前,你應該把注意力放在哪裡?

演算法的核心是解決問題的思路和方法,所弄明白一個演算法的思路是非常有必要的(當然對於某些教材上的演算法給出的步驟詳細的不能再詳細,不知道思路比著步驟也能編,這裡不考慮)。

比如:
在用遺傳演算法解決問題時:

  1. 百度搜索遺傳演算法,比較多見的是百度百科,CSND部落格,部落格園,知乎,指令碼之家的內容,也是我重點關注的搜尋結果。前三者的內容相對比較系統,可以從中掌握這個演算法的由來,思想,一般步驟。知乎大神的回答一般比較深入淺出,或者站在一個比較高的高度耳目一新,指令碼之家給出的程式碼比較實用,百度知道一些論壇智庫裡面也會有一些有價值的回答,但是沒有前面幾個的思想有深度。

  2. 這裡從百度百科的結果看思想:
    圖片來自百度百科截圖——遺傳演算法
    理解好這個介紹就完成了一大步(因為具體問題的具體實現不能照搬,只有思想是通用的):從這裡理解了遺傳演算法就是模擬生物種群優勝劣汰的過程,適者生存,不適者淘汰。並且知道了這個演算法的解決問題一般步驟是編碼,產生初代種群,迭代演化(演化要計算個體適應度,交叉,變異,產生新的個體)
    此外還需要理解每個過程都需要實現什麼功能:
    圖片來自百度百科截圖——遺傳演算法
    這裡看得出整個演算法的關鍵是構造一個迭代(不懂迭代就理解維迴圈),迭代的每一步需要計算種群中每個個體的適應度,優勝劣汰種群中的個體,種群中交叉運算,變異運算(是為了生成新的可行解,為尋找到初始種群裡之外更好可行解提供了可能)得到新的種群

  3. 看懂了思想,知道了每步是幹啥的,就可以聯絡我們要解決的問題思考(思考的方式大致就是要解決的問題有什麼?可以對應到演算法的那一部分上?這樣對應後其他部分要怎麼實現?):

    • 要求解的問題是最優化問題,那麼個體(染色體)是不是就對應著每個可行解?
    • 適應度是不是對應著目標函式值?
    • 求得了適應度要怎麼按照適應度的大小繁殖後代?(用rand函式怎麼實現?)
    • 如何實現交叉運算?(不同可行解之間哪些部分可以交換,並且有益於構造出不同的可行解?)
    • 如何實現變異運算?(隨機改變某些個體的某些屬性的值可以嗎?)
      對於這些細節有了大致的思路或者模糊的思路就可以開始嘗試編寫程式碼了
  4. 如果覺得自己的思路太模糊,可以看看別人的例子程式碼(如果沒有理解這些演算法的思想,直接看已有的程式碼,要看懂很困難,當然也可以從裡面的迴圈和分支結構作為突破口,畫畫流程圖,寫一寫註釋,來幫助理解程式碼),也可以直接上手去編,遇到問題再去網上找解決方案(matlab那麼雜,函式那麼多,記不住的東西就要善於網上去搜)

2. matlab程式設計應該熟練哪些知識?

參考另一篇部落格哪些知識點是學習matlab應該熟練掌握的?,基本功熟練,才能看到問題有想法思路(能力決定思維)

比如針對這次數學建模第三輪訓練——下料問題,假設建立了這樣的模型:

這裡寫圖片描述

  • 可行解(切割方案)是由兩個矩陣和兩個向量表示的,矩陣A的每一行表示用一根5000mm長的材料切割出50種鋼管的數量,由於鋼管種類多達50種,如果窮舉所有可能切法是困難的,所以矩陣A的行數暫定為200行,矩陣B的每一行表示用一根6000mm長的材料切割出50跟鋼管的數量,同樣暫定其行數為200行,列向量a的行數同A的行數一樣,表示按照每種切割方法切割原材料的根數,列向量b表示按照B的每行的切割方法切割原材料的根數。
  • 目標函式是這種切割方案產生的廢料的量。
  • 約束條件是每根原材料夠長,切割出每種鋼管數目達到需求。
    (精力有限,省去數學語言描述)

    怎麼應用約束條件,是直接根絕約束條件生成可行解,還是隨機生成後判斷是否滿足約束?前者設計困難,後者效率可能非常低下。

    1. 編寫程式碼(本例中要遺傳的個體比較複雜,就不進行編碼解碼,直接把80個可行解作為初始種群,相應的代價是交叉和變異不容易):
close all;clear;clc;
%% 資料
AL=5000;BL=6000;
NL=[] % 50種鋼管的長度
NN=[] % 50種鋼管的數量
%% 初始化,上面的步驟a)
T = 1000; % 迭代1000M = 80; % 種群規模
P = []; % 種群
for m=1:M % 初始化種群
    temp.A = 
    temp.B = 
    temp.x = 
    temp.y = 
    P(m)=temp; % 上面需要構造一個合適的方法生成初始可行解,實現較麻煩,
    % 我的大致思路是
end
%% 迭代(迭代包括個體評價,選擇,交叉,變異)
for t=1:T % 至多進化T%%%%%%%%%% 個體評價,假設已有一個函式score,輸入AB、x、y,返回浪費材料的長度
    sc = zeros(1,M);
    for m=1:M
        temp = P(m);
        sc(m) = score(temp.A,temp.B,temp.x,temp.y); % 計算得分
    end
    % 做一個合適的變換,讓浪費材料長度變為得分(適應度),這裡簡化了
    sc = -sc;
    sc = sc-min(sc)/(max(sc) - min(sc));
%%%%%%%%%% 選擇,輪盤賭演算法,就是讓得分對應於輪盤上扇形的寬度
%選擇50次,落到哪個扇區,對應的個體就有一個後代
    sumsc = sum(sc);
    csum = cumsum(sc);% 累加 a1,a1+a2,a1+a2+a3,...,sum(a)
    oldP = P;
    for m=1:M
        [~,index]=find(sumsc*rand>csum);
        if(isempty(index)) index=0;
        P(m) = oldP(index+1);  
    end
%%%%%%%%%% 交叉運算,這裡隨機選擇30對進行交叉運算,這裡假設實現了交叉運算元
%across ,輸入兩個個體,函式交換兩個個體裡矩陣A,B,x,y裡的部分行,列,資料
    toex = ceil(M*rand(30,2));
    for i=1:30
        P(toex(i,:)) = across(P(toex(i,:)));% 兩兩做交叉運算
    end
%%%%%%%%% 變異運算,隨機選取幾個物件,改變A,B,x,y裡的部分資料,
%這裡假設已經實現了變異運算元variation
    toch = ceil(M*rand(10,1));% 假設10個發生變異
    for i=1:10
        P(toch(i)) = variation(toch(i));
    end
end
% 目前已經按照遺傳演算法的思想寫出了整體骨架,細節有空再完善

未完待續