【数学建模】常用模型算法及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;  
}  

我是史迪仔,原创不易,如果觉得文章还不错,希望 点赞 支持下,谢谢大家~

欢迎关注公众号【程序员史迪仔】,一起学习交流成长!

  • 208
    点赞
  • 1992
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值