【數學建模】常用模型算法及MATLAB代碼彙總



一、蒙特卡洛算法

1、定義

蒙特卡洛算法是以概率和統計的理論、方法爲基礎的一種數值計算方法,將所求解的問題同一定的概率模型相聯繫,用計算機實現統計模擬或抽樣,以獲得問題的近似解,故又稱隨機抽樣法或統計實驗法。


2、適用範圍

可以較好的解決多重積分計算、微分方程求解、積分方程求解、特徵值計算和非線性方程組求解等高難度和複雜的數學計算問題。


3、特點

蒙特卡洛算法可以應用在很多場合,但求的是近似解,在模擬樣本越大的情況下,越接近於真實值,單樣本數增加會帶來計算量的大幅上升。對於一些簡單問題來說,蒙特卡洛是個笨辦法,但對於許多問題來說,它往往是個有效,有時甚至是唯一可行的方法。


4、舉例

y = x^2 ,y = 12 - x 與 X 軸在第一象限與 X 軸圍成一個曲邊三角形。設計一個隨機試驗,求該圖形的近似值。


(1)作圖
在這裏插入圖片描述
Code:

%作圖
x = 0:0.25:12;
y1 = x.^2;
y2 = 12 - x;
plot(x, y1, x, y2)
xlabel('x');ylabel('y');
%產生圖例
legend('y1=x^2', 'y2=12-x');
title('蒙特卡洛算法');
%圖中x軸和y軸的範圍,中括號前面是y軸範圍,中括號後面是x軸範圍
axis([0 15 0 15]);
text(3, 9, '交點');
%加上網格線
grid on

(2)設計的隨機試驗的思想:在矩形區域[0,12]*[0.9]上產生服從均與分佈的10^7個隨機點,統計隨機點落在曲邊三角形內的個數,則曲邊三角形的面積近似於上述矩形的面積乘以頻率。

Code:

%蒙特卡洛算法的具體實現
%產生一個110000000列的矩陣,矩陣中每個數是從012之間隨機取
x = unifrnd(0, 12, [1, 10000000]);
y = unifrnd(0, 9, [1, 10000000]);
frequency = sum(y<x.^2&x<=3)+ sum(y<12-x&x>=3);
area = 12*9*frequency/10^7;
disp(area);

所求近似值:
在這裏插入圖片描述


參考博客:https://blog.csdn.net/u013414501/article/details/50478898



二、數據擬合

1、定義

已知有限個數據點,求近似函數,可不過已知數據點,只要求在某種意義下它在這些點上的總偏差最小,從而能較好的反應數據的整體變化趨勢。


2、常用方法

  • 一般採用最小二乘法。
  • 擬合的實現分爲 MATLAB 和 excel 實現。MATLAB 的實現就是 polyfit 函數,主要是多項式擬合。

3、舉例

(1) 數據如下:

   序號 	x         y       z
	1	426.6279	0.066	2.897867
	2	465.325	    0.123   1.621569
	3	504.0792	0.102	2.429227
	4	419.1864	0.057	3.50554
	5	464.2019	0.103	1.153921
	6	383.0993	0.057	2.297169
	7	416.3144	0.049	3.058917
	8	464.2762	0.088	1.369858
	9	453.0949	0.09	3.028741
	10	376.9057	0.049	4.047241
	11	409.0494	0.045	4.838143
	12	449.4363	0.079	4.120973
	13	372.1432	0.041	3.604795
	14	389.0911	0.085	2.048922
	15	446.7059	0.057	3.372603
	16	347.5848	0.03	4.643016
	17	379.3764	0.041	4.74171
	18	453.6719	0.082	1.841441
	19	388.1694	0.051	2.293532
	20	444.9446	0.076	3.541803
	21	437.4085	0.056	3.984765
	22	408.9602	0.078	2.291967
	23	393.7606	0.059	2.910391
	24	443.1192	0.063	3.080523
	25	514.1963	0.153	1.314749
	26	377.8119	0.041	3.967584
	27	421.5248	0.063	3.005718
	28	421.5248	0.063	3.005718
	29	421.5248	0.063	3.005718
	30	421.5248	0.063	3.005718
	31	421.5248	0.063	3.005718
	32	421.5248	0.063	3.005718
	33	421.5248	0.063	3.005718
	34	421.5248	0.063	3.005718
	35	421.5248	0.063	3.005718
	36	421.5248	0.063	3.005718
	37	416.1229	0.111	1.281646
	38	369.019	    0.04	2.861201
	39	362.2008	0.036	3.060995
	40	417.1425	0.038	3.69532

(2) 方法一:使用MATLAB編寫代碼

%讀取表格
A = xlsread('E:\表格\1.xls', 'Sheet1', 'A1:AN2');
B = A;
[I, J] = size(B);
 
%數據擬合
%x爲矩陣的第一行,y爲矩陣的第二行
x = A(1,:);
y = A(2,:);
%polyfit爲matlab中的擬合函數,第一個參數是數據的橫座標
%第二個參數是數據的縱座標,第三個參數是多項式的最高階數
%返回值p中包含n+1個多項式係數
p = polyfit(x, y, 2);
disp(p);
%下面是作圖的代碼
x1 = 300:10:600;
%polyval是matlab中的求值函數,求x1對應的函數值y1
y1 = polyval(p,x1);
plot(x,y,'*r',x1,y1,'-b');
%plot(x,'DisplayName','x','YDataSource','x');
%figure(gcf);

(3) 方法三:使用matlab的圖形化擬合包(推薦)


  • 將數據導入工作區並通過cftool命令打開matlab的圖形化擬合包
    在這裏插入圖片描述

  • 選擇x、y變量
    在這裏插入圖片描述

  • 選擇擬合方式和最高項次數
    在這裏插入圖片描述

  • 得到擬合結果
    在這裏插入圖片描述

使用圖形化擬合工具不僅簡單快捷,還可以使用多種擬合方式,尋找到最好的擬合曲線。



三、數據插值

1、定義

  • 在離散數據的基礎上補插連續函數,使得這條連續曲線通過給定的全部離散數據點。即求過已知有限個數據點的近似函數。

  • 從定義上看,插值和擬合有一定的相似度,但插值要求近似函數通過給定的所有離散數據,而擬合併不要求這樣,只要近似函數能較好的反映數據變化的趨勢即可(近似含義不同),當測量值是準確的,沒有誤差時,一般用插值;當測量值與真實值有誤差時,一般用數據擬合。


2、作用

插值是離散函數逼近的重要方法,利用它可通過函數在有限個點處的取值情況,估算出函數在其他點處的近似值。


3、舉例

%years、service和wage是原始數據
years = 1950:10:1990;
service = 10:10:30;
wage = [ 150.697  199.592  187.625  179.323  195.072; 250.287  203.212  179.092  322.767  226.505;153.706  426.730  249.633  120.281  598.243];
[X, Y] = meshgrid(years, service);
% % 三維曲線
% plot3(X, Y, wage)
% 三維曲面
figure
surf(X, Y, wage)
%interp2是matlab中的二維插值函數,前兩個參數是已知位置,後兩個是未知位置,w是未知位置的插值結果
w = interp2(service,years,wage,15,1975);

在這裏插入圖片描述

可參考:數學建模常用模型02 :插值與擬合



四、圖論

1、最短路問題

最短路問題就是選擇一條距離最短的路線。

例如:一名貨櫃車司機奉命在最短的時間內將一車貨物從甲地運往乙地。從甲地到乙地的公路網縱橫交錯,因此有多種行車路線,這名司機應選擇哪條線路呢?假設貨櫃車的運行速度是恆定的,那麼這一問題相當於需要找到一條從甲地到乙地的最短路。(Dijkstra算法)

具體介紹見這裏:最短路徑—Dijkstra算法和Floyd算法


(1)Dijkstra算法

先給出一個無向圖
在這裏插入圖片描述

用Dijkstra算法找出以A爲起點的單源最短路徑步驟如下
在這裏插入圖片描述


代碼模板:

#include<iostream>  
#include<cstdio>  
#include<cstdlib>  
#include<cmath>  
#include<cstring>  
#include<algorithm>  
#include<vector>  
#include<fstream>  
using namespace std;  
  
const int maxnum = 100;  
const int maxint = 2147483647;  
int dist[maxnum];     // 表示當前點到源點的最短路徑長度  
int prev[maxnum];     // 記錄當前點的前一個結點  
int c[maxnum][maxnum];   // 記錄圖的兩點間路徑長度  
int n, line;             // n表示圖的結點數,line表示路徑個數  
void Dijkstra(int n, int v, int *dist, int *prev, int c[maxnum][maxnum])  
{  
    bool s[maxnum];    // 判斷是否已存入該點到S集合中  
    for(int i=1; i<=n; ++i)  
    {  
        dist[i] = c[v][i];  
        s[i] = 0;     // 初始都未用過該點  
        if(dist[i] == maxint)  
            prev[i] = 0;  
        else  
            prev[i] = v;  
    }  
    dist[v] = 0;  
    s[v] = 1;  
  
    // 依次將未放入S集合的結點中,取dist[]最小值的結點,放入結合S中  
    // 一旦S包含了所有V中頂點,dist就記錄了從源點到所有其他頂點之間的最短路徑長度  
    for(int i=2; i<=n; ++i)  
    {  
        int tmp = maxint;  
        int u = v;  
        // 找出當前未使用的點j的dist[j]最小值  
        for(int j=1; j<=n; ++j)  
            if((!s[j]) && dist[j]<tmp)  
            {  
                u = j;              // u保存當前鄰接點中距離最小的點的號碼  
                tmp = dist[j];  
            }  
        s[u] = 1;    // 表示u點已存入S集合中  
  
        // 更新dist  
        for(int j=1; j<=n; ++j)  
            if((!s[j]) && c[u][j]<maxint)  
            {  
                int newdist = dist[u] + c[u][j];  
                if(newdist < dist[j])  
                {  
                    dist[j] = newdist;  
                    prev[j] = u;  
                }  
            }  
    }  
}  
void searchPath(int *prev,int v, int u)  
{  
    int que[maxnum];  
    int tot = 1;  
    que[tot] = u;  
    tot++;  
    int tmp = prev[u];  
    while(tmp != v)  
    {  
        que[tot] = tmp;  
        tot++;  
        tmp = prev[tmp];  
    }  
    que[tot] = v;  
    for(int i=tot; i>=1; --i)  
        if(i != 1)  
            cout << que[i] << " -> ";  
        else  
            cout << que[i] << endl;  
}  
  
int main()  
{  
    //freopen("input.txt", "r", stdin);  
    // 各數組都從下標1開始  
    // 輸入結點數  
    cin >> n;  
    // 輸入路徑數  
    cin >> line;  
    int p, q, len;          // 輸入p, q兩點及其路徑長度  
    // 初始化c[][]爲maxint  
    for(int i=1; i<=n; ++i)  
        for(int j=1; j<=n; ++j)  
            c[i][j] = maxint;  
    for(int i=1; i<=line; ++i)  
    {  
        cin >> p >> q >> len;  
        if(len < c[p][q])       // 有重邊  
        {  
            c[p][q] = len;      // p指向q  
            c[q][p] = len;      // q指向p,這樣表示無向圖  
        }  
    }  
   for(int i=1; i<=n; ++i)  
        dist[i] = maxint;  
    for(int i=1; i<=n; ++i)  
    {  
        for(int j=1; j<=n; ++j)  
            printf("%-16d", c[i][j]);  
        printf("\n");  
    }  
    Dijkstra(n, 1, dist, prev, c);   //僅調用函數求出了源點到其他點的距離 改法:Dijkstra(n, x, dist, prev, c);  其中x=1,2,3,4,...,n  
  
//    for(int i=1; i<=n; ++i)   //dist存儲了源點到其他點的距離情況  
//    {  
//        printf("%-16d", dist[i]);  
//    }  
    printf("\n");  
     // 最短路徑長度  
    cout << "源點到最後一個頂點的最短路徑長度: " << dist[n] << endl;  
     // 路徑  
    cout << "源點到最後一個頂點的路徑爲: ";  
    searchPath(prev, 1, n);  
    return 0;  
}  
  
  
/* 
輸入數據: 
 5 
 7 
 1 2 10 
 1 4 30 
 1 5 100 
 2 3 50 
 3 5 10 
 4 3 20 
 4 5 60 
 輸出數據: 
 999999 10 999999 30 100 
 10 999999 50 999999 999999 
 999999 50 999999 20 10 
 30 999999 20 999999 60 
 100 999999 10 60 999999 
 源點到最後一個頂點的最短路徑長度: 60 
 源點到最後一個頂點的路徑爲: 1 -> 4 -> 3 -> 5 
*/ 

(2)Floyd算法

#include<iostream>  
#include<cstdio>  
#include<cstdlib>  
#include<cmath>  
#include<cstring>  
#include<algorithm>  
#include<vector>  
#include<fstream>  
using namespace std;  
  
//設點與點之間的距離均爲double型  
double INFTY=2147483647;  
const int MAX=1000;  
double dis[MAX][MAX];  
double a[MAX][MAX];  
int path[MAX][MAX];  
int n,m; //結點個數  
  
void Floyd()  
{  
    int i,j,k;  
    for(i=1;i<=n;i++)  
    {  
        for(j=1;j<=n;j++)  
        {  
            dis[i][j]=a[i][j];  
            if(i!=j&&a[i][j]<INFTY)  
            {  
                path[i][j]=i;  
            }  
            else  
                path[i][j]=-1;  
        }  
    }  
  
    for(k=1;k<=n;k++)  
    {  
        for(i=1;i<=n;i++)  
        {  
            for(j=1;j<=n;j++)  
            {  
                if(dis[i][k]+dis[k][j]<dis[i][j])  
                {  
                    dis[i][j]=dis[i][k]+dis[k][j];  
                    path[i][j]=path[k][j];  
                }  
            }  
        }  
    }  
}  
  
int main()  
{  
    //freopen("datain.txt","r",stdin);  
    int beg,enda;  
    double dist;  
    scanf("%d%d",&n,&m);  
    for(int i=1;i<=n;i++)  
    {  
       for(int j=1;j<=n;j++)  
       {  
            if(i==j)  
                a[i][j]=0;  
            else  
                a[i][j]=INFTY;  
       }  
    }  
    for(int i=1;i<=m;i++)  
    {  
        scanf("%d%d%lf",&beg,&enda,&dist);  
        a[beg][enda]=a[enda][beg]=dist;  
    }  
    Floyd();  
    for(int i=1;i<=n;i++)  
    {  
       for(int j=1;j<=n;j++)  
       {  
            printf("%-12lf",dis[i][j]);  
       }  
       printf("\n");  
    }  
    return 0;  
}  


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章