数学建模算法总结

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数学建模算法总结相关的知识,希望对你有一定的参考价值。

参考技术A 无总结反省则无进步

写这篇文章,一是为了总结之前为了准备美赛而学的算法,而是将算法罗列并有几句话解释方便以后自己需要时来查找。

数学建模问题总共分为四类:

1. 分类问题 2. 优化问题 3. 评价问题 4. 预测问题

我所写的都是基于数学建模算法与应用这本书

一 优化问题

线性规划与非线性规划方法是最基本经典的:目标函数与约束函数的思想

现代优化算法:禁忌搜索;模拟退火;遗传算法;人工神经网络

模拟退火算法:

简介:材料统计力学的研究成果。统计力学表明材料中不同结构对应于粒子的不同能量水平。在高温条件下,粒子的能量较高,可以自由运动和重新排列。在低温条件下,粒子能量较低。如果从高温开始,非常缓慢地降温(此过程称为退火),粒子就可以在每个温度下达到热平衡。当系统完全被冷却时,最终形成处于低能状态的晶体。

思想可用于数学问题的解决 在寻找解的过程中,每一次以一种方法变换新解,再用退火过程的思想,以概率接受该状态(新解) 退火过程:概率转化,概率为自然底数的能量/KT次方

遗传算法: 遗传算法是一种基于自然选择原理和自然遗传机制的搜索算法。模拟自然界中的生命进化机制,在人工系统中实现特定目标的优化。

遗传算法的实质是通过群体搜索技术(?),根据适者生存的原则逐代进化,最终得到最优解或准最优解。

具体实现过程(P329~331)

* 编码

* 确定适应度函数(即目标函数)

* 确定进化参数:群体规模M,交叉概率Pc,变异概率Pm,进化终止条件

* 编码

* 确定初始种群,使用经典的改良圈算法

* 目标函数

* 交叉操作

* 变异操作

* 选择

改良的遗传算法

两点改进 :交叉操作变为了以“门当户对”原则配对,以混乱序列确定较差点位置 变异操作从交叉操作中分离出来

二 分类问题(以及一些多元分析方法)

* 支持向量机SVM

* 聚类分析

* 主成分分析

* 判别分析

* 典型相关分析

支持向量机SVM: 主要思想:找到一个超平面,使得它能够尽可能多地将两类数据点正确分开,同时使分开的两类数据点距离分类面最远

聚类分析(极其经典的一种算法): 对样本进行分类称为Q型聚类分析 对指标进行分类称为R型聚类分析

基础:样品相似度的度量——数量化,距离——如闵氏距离

主成分分析法: 其主要目的是希望用较少的变量去解释原来资料中的大部分变异,将掌握的许多相关性很高的变量转化成彼此相互独立或不相关的变量。通常是选出比原始变量个数少,能解释大部分资料中的变异的几个新变量,及主成分。实质是一种降维方法

判别分析: 是根据所研究的个体的观测指标来推断个体所属类型的一种统计方法。判别准则在某种意义下是最优的,如错判概率最小或错判损失最小。这一方法像是分类方法统称。 如距离判别,贝叶斯判别和FISHER判别

典型相关分析: 研究两组变量的相关关系 相对于计算全部相关系数,采用类似主成分的思想,分别找出两组变量的各自的某个线性组合,讨论线性组合之间的相关关系

三 评价与决策问题

评价方法分为两大类,区别在于确定权重上:一类是主观赋权:综合资讯评价定权;另一类为客观赋权:根据各指标相关关系或各指标值变异程度来确定权数

* 理想解法

* 模糊综合评判法

* 数据包络分析法

* 灰色关联分析法

* 主成分分析法(略)

* 秩和比综合评价法 理想解法

思想:与最优解(理想解)的距离作为评价样本的标准

模糊综合评判法 用于人事考核这类模糊性问题上。有多层次模糊综合评判法。

数据包络分析法 是评价具有多指标输入和多指标输出系统的较为有效的方法。是以相对效率为概念基础的。

灰色关联分析法 思想:计算所有待评价对象与理想对象的灰色加权关联度,与TOPSIS方法类似

主成分分析法(略)

秩和比综合评价法 样本秩的概念: 效益型指标从小到大排序的排名 成本型指标从大到小排序的排名 再计算秩和比,最后统计回归

四 预测问题

* 微分方程模型

* 灰色预测模型

* 马尔科夫预测

* 时间序列(略)

* 插值与拟合(略)

* 神经网络

微分方程模型 Lanchester战争预测模型。。

灰色预测模型 主要特点:使用的不是原始数据序列,而是生成的数据序列 优点:不需要很多数据·,能利用微分方程来充分挖掘系统的本质,精度高。能将无规律的原始数据进行生成得到规律性较强的生成序列。 缺点:只适用于中短期预测,只适合指数增长的预测

马尔科夫预测 某一系统未来时刻情况只与现在状态有关,与过去无关。

马尔科夫链

时齐性的马尔科夫链

时间序列(略)

插值与拟合(略)

神经网络(略)

网络流算法与建模总结

[2016-12-16]

【算法】

1.最大流

容量限制:对于∀u,v∈V ,要求 f (u,v) ≤ c(u,v)。

流量平衡:对于∀u∈V −{s,t},要求∑f(u,v)=0。 

dinic

  1. 根据残量网络计算层次图。
  2. 在层次图中使用DFS沿阻塞流(不考虑反向弧时的极大流 层次图中的)进行增广直到不存在增广路
  3. 重复以上步骤直到无法增广

使用当前弧优化

int cur[N];
int vis[N],d[N],q[N],head,tail;
bool bfs(){
    memset(vis,0,sizeof(vis));
    memset(d,0,sizeof(d));
    head=tail=1;
    q[tail++]=s;d[s]=0;vis[s]=1;
    while(head!=tail){
        int u=q[head++];
        for(int i=h[u];i;i=e[i].ne){
            int v=e[i].v;
            if(!vis[v]&&e[i].c>e[i].f){
                vis[v]=1;d[v]=d[u]+1;
                q[tail++]=v;
                if(v==t) return 1;
            }
        }
    }
    return 0;
}
int dfs(int u,int a){
    if(u==t||a==0) return a;
    int flow=0,f;
    for(int &i=cur[u];i;i=e[i].ne){
        int v=e[i].v;
        if(d[v]==d[u]+1&&(f=dfs(v,min(a,e[i].c-e[i].f)))>0){
            flow+=f;
            e[i].f+=f;
            e[((i-1)^1)+1].f-=f;
            a-=f;
            if(a==0) break;
        }
    }
    return flow;
}
int dinic(){
    int flow=0;
    while(bfs()){
        for(int i=s;i<=t;i++) cur[i]=h[i];
        flow+=dfs(s,INF);
    }
    return flow;
}
View Code

【2017-01-24】还有一种优化,增广后还有a说明在这一层从u这个点不可能在增广了,设置d[u]=-1就可以了

使用这个优化会快一点,貌似这个和当前弧用一个就可以,因为会带来常数问题

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e4+5,M=1e5+5,INF=1e9;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<\'0\'||c>\'9\'){if(c==\'-\')f=-1; c=getchar();}
    while(c>=\'0\'&&c<=\'9\'){x=x*10+c-\'0\'; c=getchar();}
    return x*f;
}

struct edge{
    int v,c,f,ne;
}e[M<<1];
int cnt,h[N];
inline void ins(int u,int v,int c){
    cnt++;
    e[cnt].v=v;e[cnt].c=c;e[cnt].f=0;e[cnt].ne=h[u];h[u]=cnt;
    cnt++;
    e[cnt].v=u;e[cnt].c=0;e[cnt].f=0;e[cnt].ne=h[v];h[v]=cnt;
}
int cur[N],d[N],vis[N];
int q[N],head,tail;
bool bfs(){
    head=tail=1;
    memset(vis,0,sizeof(vis));
    d[s]=1;vis[s]=1;q[tail++]=s;
    while(head!=tail){
        int u=q[head++];
        for(int i=h[u];i;i=e[i].ne){
            int v=e[i].v;
            if(!vis[v]&&e[i].c>e[i].f){
                vis[v]=1;d[v]=d[u]+1;
                q[tail++]=v;
                if(v==t) return true;
            }
        }
    }
    return false;
}
int dfs(int u,int a){
    if(u==t||a==0) return a;
    int flow=0,f;
    for(int &i=cur[u];i;i=e[i].ne){
        int v=e[i].v;
        if(d[v]==d[u]+1&&(f=dfs(v,min(e[i].c-e[i].f,a)))>0){
            flow+=f;
            e[i].f+=f;
            e[((i-1)^1)+1].f-=f;
            a-=f;
            if(a==0) break;
        }
    }
    if(a) d[u]=-1;
    return flow;
}

int dinic(){
    int flow=0;
    while(bfs()){
        for(int i=s;i<=t;i++) cur[i]=h[i];
        flow+=dfs(s,INF);
    }
    return flow;
}
View Code

2.最小割

最大流的对偶问题

流网络G =(V,E)的割(cut)[S,T]将点集V划分为S和T(T =V −S)两个部分,

使得源s∈S且汇t∈T。符号[S,T]代表一个边集合{ u,v | u,v ∈E,u∈S,v∈T}。

穿过割 [S,T]的净流(net flow)定义为 f (S,T),割[S,T]的容量(capacity)定义为c(S,T),一般 记为c[S,T]。

一个网络的最小割(minimum cut)也就是该网络中容量最小的割。 

增广路算法结束时,所有还有流量(从s走)的点组成S,没有流量的点组成T

最大流的流量就是最小割的容量

3.最小费用最大流

(1)spfa费用流

用spfa找最短路来增广

保存pre[i]和pos[i]分别是最短路中的父节点和入边

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=5005,M=5e4+5,INF=1e9;
int read(){
    char c=getchar();int x=0,f=1;
    while(c<\'0\'||c>\'9\'){if(c==\'-\')f=-1; c=getchar();}
    while(c>=\'0\'&&c<=\'9\'){x=x*10+c-\'0\'; c=getchar();}
    return x*f;
}
int n,m,s,t,u,v,w,c;
struct edge{
    int v,ne,c,f,w;
}e[M<<1];
int cnt,h[N];
inline void ins(int u,int v,int c,int w){
    cnt++;
    e[cnt].v=v;e[cnt].c=c;e[cnt].f=0;e[cnt].w=w;
    e[cnt].ne=h[u];h[u]=cnt;
    cnt++;
    e[cnt].v=u;e[cnt].c=0;e[cnt].f=0;e[cnt].w=-w;
    e[cnt].ne=h[v];h[v]=cnt;
}
int d[N],pre[N],pos[N],q[N],head=1,tail=1,inq[N];
inline void lop(int &x){if(x==N) x=1;else if(x==0) x=N-1;}
bool spfa(){
    memset(d,127,sizeof(d));
    d[s]=0;pre[t]=-1;
    head=tail=1;
    memset(inq,0,sizeof(inq));
    q[tail++]=s;inq[s]=1;
    while(head!=tail){
        int u=q[head++];lop(head);inq[u]=0;
        for(int i=h[u];i;i=e[i].ne){
            int v=e[i].v,w=e[i].w;
            if(d[v]>d[u]+w&&e[i].c>e[i].f){
                d[v]=d[u]+w;
                pre[v]=u;
                pos[v]=i;
                if(!inq[v]){
                    if(d[v]<d[q[head]]) head--,lop(head),q[head]=v;
                    else q[tail++]=v,lop(tail);
                    inq[v]=1;
                }
            }
        }
    }
    return pre[t]==-1?0:1;
}
void mcmf(){
    ll flow=0,cost=0;
    while(spfa()){
        int f=INF;
        for(int i=t;i!=s;i=pre[i]) f=min(f,e[pos[i]].c-e[pos[i]].f);
        flow+=f;
        cost+=f*d[t];
        for(int i=t;i!=s;i=pre[i]){
            e[pos[i]].f+=f;
            e[((pos[i]-1)^1)+1].f-=f;
        }
    }
    printf("%lld %lld",flow,cost);
}
int main(int argc, const char * argv[]) {
    n=read();m=read();s=read();t=read();
    for(int i=1;i<=m;i++){
        u=read();v=read();c=read();w=read();
        ins(u,v,c,w);
    }
    mcmf();
    return 0;
}
View Code

(2)zkw费用流

http://www.artofproblemsolving.com/community/c1368h1020435

研究了好长时间.........

修改D的做法和KM很想,但同时缺点明显,感觉没大有意义

原始对偶算法貌似就是改了个多路增广,并且过程跟dinic还有点不一样

貌似没有明显优化

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=5005,M=5e4+5,INF=1e9;
int read(){
    char c=getchar();int x=0,f=1;
    while(c<\'0\'||c>\'9\'){if(c==\'-\')f=-1; c=getchar();}
    while(c>=\'0\'&&c<=\'9\'){x=x*10+c-\'0\'; c=getchar();}
    return x*f;
}
int n,m,s,t,u,v,w,c;
struct edge{
    int v,ne,c,f,w;
}e[M<<1];
int cnt,h[N];
inline void ins(int u,int v,int c,int w){
    cnt++;
    e[cnt].v=v;e[cnt].c=c;e[cnt].f=0;e[cnt].w=w;
    e[cnt].ne=h[u];h[u]=cnt;
    cnt++;
    e[cnt].v=u;e[cnt].c=0;e[cnt].f=0;e[cnt].w=-w;
    e[cnt].ne=h[v];h[v]=cnt;
}
int d[N],q[N],head=1,tail=1,inq[N];
inline void lop(int &x){if(x==N) x=1;else if(x==0) x=N-1;}
bool spfa(){
    memset(d,127,sizeof(d));
    d[s]=0;
    head=tail=1;
    memset(inq,0,sizeof(inq));
    q[tail++]=s;inq[s]=1;
    while(head!=tail){
        int u=q[head++];lop(head);inq[u]=0;
        for(int i=h[u];i;i=e[i].ne){
            int v=e[i].v,w=e[i].w;
            if(d[v]>d[u]+w&&e[i].c>e[i].f){
                d[v]=d[u]+w;
                if(!inq[v]){
                    if(d[v]<d[q[head]]) head--,lop(head),q[head]=v;
                    else q[tail++]=v,lop(tail);
                    inq[v]=1;
                }
            }
        }
    }
    return d[t]<INF;
}
int cur[N],ans,mark[N];
int dfs(int u,int a){//printf("dfs %d %d\\n",u,a);
    if(u==t||a==0) return a;
    int flow=0,f;
    mark[u]=1;
    for(int &i=cur[u];i;i=e[i].ne){
        int v=e[i].v,w=e[i].w;
        if(d[v]==d[u]+w&&!mark[v]&&(f=dfs(v,min(a,e[i].c-e[i].f)))>0){
            ans+=f*w;
            flow+=f;
            e[i].f+=f;
            e[((i-1)^1)+1].f-=f;
            a-=f;
            if(a==0) break;
        }
    }
    return flow;
}
int zkw(){
    int flow=0;
    while(spfa()){
        mark[t]=1;
        while(mark[t]){
            memset(mark,0,sizeof(mark));
            for(int i=1;i<=n;i++) cur[i]=h[i];
            flow+=dfs(s,INF);
        }
    }
    return flow;
}

int main(int argc, const char * argv[]) {
    n=read();m=read();s=read();t=read();
    for(int i=1;i<=m;i++){
        u=read();v=read();c=read();w=read();
        ins(u,v,c,w);
    }
    int flow=zkw();
    printf("%d %d",flow,ans);
    return 0;
}
View Code

 

【建模】

1.公平分配问题

把X分配给Y,一个X有两个Y可选,让分配到最多的最少

二分图模型,X和Y构成二分图

二分最多最少值mid

s--1-->X--1-->Y--mid-->t

看maxflow==|X|


 

2.最大闭合子图

定义一个有向图G = (V,E)的闭合图(closure)10是该有向图的一个点集,且该点集的所 有出边都还指向该点集。即闭合图内的任意点的任意后继也一定在闭合图中。 

在原图点集的基础上增加源s和汇t;

将原图每条有向边 u,v ∈E替换为容量为c(u,v)=∞的有向边u,v ∈EN;

增加连接源s到原图每个正权点v(wv >0)的有向边s,v ∈EN,容量为c(s,v)=wv;

增加连接原图每个负权点v(wv <0)到汇t的有向边v,t ∈EN,容量为c(v,t)=−wv 

(这样下来两个边权都是正数)

s--点权-->正权点----INF----负权点--|点权|-->t

胡波涛论文中有详细证明

我们也可以简单的思考最小割,要么(1)把s-->正u割了,要么(2)把负v-->t割了

(1)相当于不选择这个u,他的后继就没必要选了,同时损失wu

(2)相当于选择了u,同时选择了他的后继v,所以损失wv

 


3.二分图最大匹配

s--1-->X--1-->Y--1-->t 


 

4.最小路径覆盖问题

G 的最小路径覆盖是G 的所含路径条数最少的路径覆盖。

有点逆向思考的感觉
最差情况所有的点都是一条路径
两个点连起来的话就少一条路径一个点
拆成入点X和出点Y,构成二分图,ans=n-最大匹配数

 


 

5.最小割挺套路的,想割什么东西连一条INF

 


 

6.最大密度子图

http://www.cnblogs.com/candy99/p/6430552.html

 

以上是关于数学建模算法总结的主要内容,如果未能解决你的问题,请参考以下文章

图论算法的数学模型

网络流算法与建模总结

数学建模笔记总结

数学建模笔记总结

数学建模笔记总结

2018数学建模国赛总结(A题/编程选手视角)