数学建模总结

目录

一、线性规划

二、整数规划

三、非线性规划

四、图与网络模型及方法

1.图与网络的基础理论

2.最短路算法

3.最小生成树

4.着色问题

5.最大流与最小费用流问题


一、线性规划

1.线性规划定义:线性规划(Linear Programming,LP)是一种用于优化线性目标函数的数学方法,该目标函数受制于一组线性不等式或等式约束。线性规划广泛应用于资源分配、生产调度、物流规划等领域。

一般有一个要求max或者min的目标函数,在问题给出的约束条件下求解。

2.规划问题数学模型的3个要素

决策变量问题中要确定的未知量,用于表明规划问题中用数量表示的方案措施等
目标函数是决策变量的函数,优化目标通常求max或者min
约束条件决策变量的取值所受到的约束和限制条件,通常由含决策变量的等式不等式表示

3.线性规划模型的一般步骤:

第一步:分析问题,找出决策变量

第二步:根据问题所给条件,找出决策变量必须满足的一组线性等式或不等式约束,即约束条件

第三步:根据问题的目标,构造关于决策变量的一个线性函数,即为目标函数

有了决策变量、约束条件和目标函数这三个要素之后,一个线性规划模型就建立起来了。

4.例题:

目标:最大化z = 3x + 2y

约束条件:

        1.x + y <= 4

        2.x +2y <= 5

        3.x >= 0, y >= 0

5.代码:

使用python语言,并且需要pulp库

from pulp import LpMaximize, LpProblem, LpVariable

# 创建一个最大化问题
problem = LpProblem("Maximize_Z", LpMaximize)

# 定义变量
x = LpVariable("x", lowBound=0)  # x >= 0
y = LpVariable("y", lowBound=0)  # y >= 0

# 添加目标函数
problem += 3 * x + 2 * y, "Objective"

# 添加约束条件
problem += x + y <= 4, "Constraint 1"
problem += x + 2 * y <= 5, "Constraint 2"

# 求解问题
problem.solve()

# 输出结果
print(f"Status: {problem.status}")
print(f"Objective value: {problem.objective.value()}")

for variable in problem.variables():
    print(f"{variable.name} = {variable.varValue}")

二、整数规划

1.整数规划定义:整数规划(Integer Programming, IP)是线性规划的一个分支,其特点是在求解过程中,要求所有或部分决策变量必须取整数值。整数规划在很多实际问题中有重要应用,例如生产计划、排班、物流和运输等。

2.几个关于整数线性规划的问题:背包问题、标准指派问题、旅行商问题

3.非线性约束条件的线性化:在实际问题中,有时引入0-1变量可以把一些特定的非线性约束条件进行线性化

4.蒙特卡洛法

蒙特卡洛发也称为计算机随机模拟法,原于赌城摩纳哥的蒙特卡洛。它是基于大量时间的统计结果来实现一些确定性问题。

因为非线性(整数)规划目前尚未有成熟准确的求解方法,所以用蒙特卡洛法可以得到一些结果。

例如求解圆周率,用蒙特卡洛求解代码为:

import random

def estimate_pi(num_samples):
    # 初始化在圆内的点的计数
    inside_circle = 0

    # 循环生成随机样本点
    for _ in range(num_samples):
        # 生成一个随机点 (x, y),x 和 y 的值在 [0, 1] 区间内
        x = random.uniform(0, 1)
        y = random.uniform(0, 1)
        
        # 判断点 (x, y) 是否在单位圆内,即满足 x^2 + y^2 <= 1
        if x**2 + y**2 <= 1:
            inside_circle += 1

    # 估计 π 的值,π ≈ 4 * (在圆内的点数 / 总点数)
    pi_estimate = (inside_circle / num_samples) * 4
    return pi_estimate

# 设置样本数量
num_samples = 100000

# 调用函数计算 π 的估计值
pi_estimate = estimate_pi(num_samples)

# 输出估计的 π 值
print(f"Estimated value of π: {pi_estimate}")

三、非线性规划

1.非线性规划定义:非线性规划(Nonlinear Programming, NLP)是优化问题的一种,其中目标函数或约束条件至少有一个是非线性的。非线性规划问题在许多实际应用中非常常见,如工程设计、经济学、能源管理等领域。

2.非线性规划的求解:

无约束非线性规划:用函数求极值的方法,求函数的梯度(偏导),一般有梯度下降法、牛顿法和拟牛顿法。

有约束非线性规划:一般尽量将约束问题转化为无约束问题。一般对只有等式约束的非线性规划可以用拉格朗日乘子法,对一般形式(可能有不等式约束无法用拉格朗日乘子法)可以用罚函数法。

3.非线性规划的python实现

问题目标:最小化f(x, y) = x^2 + y^2

约束条件:1.x + y = 1     2.x >= 0, y >= 0

一般还是用库来直接求解非线性规划问题

import numpy as np
from scipy.optimize import minimize

# 定义目标函数
def objective_function(x):
    return x[0]**2 + x[1]**2

# 定义约束条件
def constraint_eq(x):
    return x[0] + x[1] - 1

# 定义变量的边界
bounds = [(0, None), (0, None)]

# 定义约束
constraints = {'type': 'eq', 'fun': constraint_eq}
# constraints = [{'type': 'ineq', 'fun': lambda x: 1 - x[0] - x[1]}] 若约束是x+y>=1

# 初始猜测值
initial_guess = [0.5, 0.5]

# 使用SciPy的minimize函数进行优化
result = minimize(objective_function, initial_guess, bounds=bounds, constraints=constraints)

# 输出结果
print(f"Status: {result.success}")
print(f"Objective value: {result.fun}")
print(f"Solution: x = {result.x[0]}, y = {result.x[1]}")

4.凸规划:凸规划是非线性规划的一个重要子集,它的特征是在目标函数和约束条件上都有特定的“凸性”要求。可以求得全局最优解。

四、图与网络模型及方法

1.图与网络的基础理论

定义:图G是由一对集合(V,E)组成,V是顶点集,E是边集

分类:无向图、有向图、权重图

表示:邻接矩阵、邻接表

2.最短路算法

最短路算法是图论中的一个重要问题,旨在寻找从一个顶点到另一个顶点的最短路径。实际问题如管道铺设、线路安排、厂区布置、设备更新等,都可被归结为最短路径问题来解决。

固定起点的最短路:Dijkstra算法,适用于无负权边的图

原理:

  • 使用优先队列(最小堆)来实现,逐步扩展到距离源点最近的点
  • 每次从未访问的顶点中选择距离源点最近的点,更新其邻居的最短路径估计值

算法步骤:

  • 初始化源顶点到所有其他顶点的距离为无穷大,源顶点到自己的距离为0。
  • 使用优先队列(最小堆)选择当前距离最小的顶点。
  • 更新与当前顶点相邻的顶点的距离。
  • 重复步骤2和3,直到所有顶点都被处理。

代码:

import heapq

def dijkstra(graph, start):
    # 初始化距离
    dist = {vertex: float('infinity') for vertex in graph}
    dist[start] = 0
    priority_queue = [(0, start)]
    
    while priority_queue:
        current_distance, current_vertex = heapq.heappop(priority_queue)
        
        # 如果当前距离大于记录的最短距离,跳过
        if current_distance > dist[current_vertex]:
            continue
        
        # 更新相邻顶点的距离
        for neighbor, weight in graph[current_vertex].items():
            distance = current_distance + weight
            if distance < dist[neighbor]:
                dist[neighbor] = distance
                heapq.heappush(priority_queue, (distance, neighbor))
    
    return dist

# 示例图:顶点与边的权重
graph = {
    'A': {'B': 1, 'C': 4},
    'B': {'A': 1, 'C': 2, 'D': 5},
    'C': {'A': 4, 'B': 2, 'D': 1},
    'D': {'B': 5, 'C': 1}
}

# 计算从顶点 'A' 到其他顶点的最短路径
print(dijkstra(graph, 'A'))

所有顶点对之剑的最短路算法:Floyd(带负权边的图也适用)

步骤:

  • 初始化距离矩阵,矩阵中[ i ][ j ]代表顶点 i 到顶点  j 的初始距离。
  • 对每对顶点,尝试通过每个中间顶点更新最短路径。
  • 遍历所有可能的中间顶点,更新最短路径。

代码:

def floyd_warshall(graph):
    # 初始化距离矩阵
    dist = {u: {v: float('infinity') for v in graph} for u in graph}
    for u in graph:
        dist[u][u] = 0
        for v, w in graph[u].items():
            dist[u][v] = w
    
    # 更新距离矩阵
    for k in graph:
        for i in graph:
            for j in graph:
                if dist[i][j] > dist[i][k] + dist[k][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
    
    return dist

# 示例图:顶点与边的权重
graph = {
    'A': {'B': 1, 'C': 4},
    'B': {'C': 2, 'D': 5},
    'C': {'D': 1},
    'D': {}
}

# 计算所有顶点对之间的最短路径
print(floyd_warshall(graph))

3.最小生成树

定义:最小生成树(Minimum Spanning Tree, MST)是一个包含图中所有顶点的无环子图,其边的权重之和最小。在图论和网络设计中,最小生成树有着广泛的应用,比如在设计和构建物理网络(如电信、电力网络)时,最小生成树可以帮助找到成本最低的网络连接方式。

基础概念:

  • 生成树:包含图中所有顶点的树。
  • 最小生成树:在所有生成树中,边的权重之和最小的那棵树。
  • 边的权重:通常表示连接两个顶点的代价或距离。

两个算法:

kruiskal算法

原理:Kruskal算法基于贪心策略,逐步选择权重最小的边,加入生成树中,直到所有顶点都被连接。

步骤:

  1. 将图中的所有边按权重从小到大排序。
  2. 初始化一个空的最小生成树。
  3. 从最小权重的边开始,逐一检查是否会形成环路:
    • 使用并查集(Union-Find)来检测是否形成环路。
    • 如果不形成环路,则将这条边加入最小生成树。
  4. 重复步骤3,直到加入的边数等于顶点数减一。

代码:

class UnionFind:
    def __init__(self, size):
        self.root = [i for i in range(size)]
        self.rank = [1] * size

    def find(self, x):
        if self.root[x] != x:
            self.root[x] = self.find(self.root[x])  # 路径压缩
        return self.root[x]

    def union(self, x, y):
        rootX = self.find(x)
        rootY = self.find(y)
        if rootX != rootY:
            if self.rank[rootX] > self.rank[rootY]:
                self.root[rootY] = rootX
            else:
                self.root[rootX] = rootY
                if self.rank[rootX] == self.rank[rootY]:
                    self.rank[rootY] += 1

def kruskal(graph):
    result = []  # 存储最小生成树的边
    i, e = 0, 0  # 边的索引和已加入最小生成树的边数
    graph = sorted(graph, key=lambda item: item[2])  # 按边的权重排序
    uf = UnionFind(len(graph[0][0]))

    while e < len(graph[0][0]) - 1:
        u, v, w = graph[i]
        i += 1
        x = uf.find(u)
        y = uf.find(v)

        if x != y:
            e += 1
            result.append((u, v, w))
            uf.union(x, y)

    return result

# 示例图的边列表表示,每条边为 (起点, 终点, 权重)
graph = [
    ('A', 'B', 10),
    ('B', 'C', 15),
    ('A', 'C', 20),
    ('C', 'D', 30),
    ('B', 'D', 50)
]

print(kruskal(graph))

prim算法

原理:Prim算法也是基于贪心策略,从一个起始顶点出发,逐步扩展生成树,选择权重最小的边,使得生成树不断增加一个顶点。

步骤:

  1. 选择一个起始顶点,将其加入生成树。
  2. 初始化一个空的最小生成树。
  3. 从当前生成树中的顶点出发,选择一条权重最小的边,使得边的另一端的顶点不在生成树中。
  4. 将选中的边的另一端的顶点加入生成树,重复步骤3,直到生成树包含所有顶点。

代码:

import heapq

def prim(graph):
    mst = []  # 存储最小生成树的边
    visited = set()  # 存储已访问的顶点
    start = list(graph.keys())[0]  # 随便选择一个起始顶点
    visited.add(start)
    edges = [(weight, start, to) for to, weight in graph[start].items()]
    heapq.heapify(edges)  # 将边转换为最小堆

    while edges:
        weight, from_node, to_node = heapq.heappop(edges)
        if to_node not in visited:
            visited.add(to_node)
            mst.append((from_node, to_node, weight))
            for next_to, next_weight in graph[to_node].items():
                if next_to not in visited:
                    heapq.heappush(edges, (next_weight, to_node, next_to))

    return mst

# 示例图的邻接表表示
graph = {
    'A': {'B': 1, 'C': 4},
    'B': {'A': 1, 'C': 2, 'D': 5},
    'C': {'A': 4, 'B': 2, 'D': 1},
    'D': {'B': 5, 'C': 1}
}

print(prim(graph))

4.着色问题

着色问题的基本概念

  • 图着色:给图中的每个顶点分配一个颜色,使得相邻的顶点颜色不同。
  • 图的染色数:图的着色问题的目标是找到使用的最小颜色数,这个最小颜色数称为图的染色数。

着色问题的类型

  1. 节点着色问题(Vertex Coloring):给图的每个顶点分配颜色。
  2. 边着色问题(Edge Coloring):给图的每条边分配颜色,使得相邻的边颜色不同。

着色问题的难度

  • NP-难问题:节点着色问题是一个NP-难问题,特别是对于一般图来说,找到最小的染色数是一个计算复杂的问题。
  • 特殊图的处理:一些特殊类型的图(如平面图、树等)可以用更高效的方法解决。

常见的算法和方法

  1. 贪心算法:一种简单而有效的方法,通过逐步分配颜色来解决着色问题。
  2. 回溯法:通过递归尝试不同的颜色分配,回溯到上一步寻找其他可能的解。
  3. 图的分解法:将图分解成多个子图,分别求解着色问题。
  4. 启发式算法和元启发式算法:如模拟退火、遗传算法、局部搜索等,用于近似求解。

5.最大流与最小费用流问题

最大流问题和最小费用流问题是图论中的两个重要问题,广泛应用于网络设计、资源分配等领域。下面分别介绍这两个问题及其解决算法。

最大流问题

概述

最大流问题是在一个流网络中找到从源点(source)到汇点(sink)能够传输的最大流量。流网络是一个有向图,其中每条边都有一个容量限制,表示流量的上限。

基本概念

  • 流网络:包含一个源点 s 和一个汇点 t。
  • 容量:每条边 (u,v)的容量 c(u,v)。
  • 流量:每条边 (u,v)的实际流量 f(u,v)。
  • 流守恒:对于除源点和汇点外的每个顶点 v,进入 v 的流量等于离开 v的流量。

Ford-Fulkerson算法

Ford-Fulkerson算法是一种增广路径算法,基于寻找增广路径并不断增加流量,直到没有增广路径为止。

步骤

  1. 初始化:所有边的初始流量为0。
  2. 寻找增广路径:在残余网络中找到一条从源点到汇点的路径,使得所有边都有剩余容量。
  3. 更新流量:沿增广路径更新流量。
  4. 重复:重复步骤2和3,直到找不到增广路径。

代码

from collections import deque

def bfs(rGraph, s, t, parent):
    # 创建一个 visited 数组,标记节点是否已访问
    visited = [False] * len(rGraph)
    queue = deque([s])
    visited[s] = True

    # BFS遍历寻找增广路径
    while queue:
        u = queue.popleft()
        for ind, val in enumerate(rGraph[u]):
            if not visited[ind] and val > 0:  # 未访问且有剩余容量的边
                queue.append(ind)
                visited[ind] = True
                parent[ind] = u
                if ind == t:
                    return True
    return False

def ford_fulkerson(graph, source, sink):
    # 复制图的邻接矩阵作为残余图
    rGraph = [row[:] for row in graph]
    parent = [-1] * len(graph)
    max_flow = 0

    # 当存在增广路径时,继续寻找并更新流量
    while bfs(rGraph, source, sink, parent):
        path_flow = float('Inf')
        s = sink

        # 找到增广路径中最小的边容量
        while s != source:
            path_flow = min(path_flow, rGraph[parent[s]][s])
            s = parent[s]

        v = sink
        while v != source:
            u = parent[v]
            rGraph[u][v] -= path_flow  # 更新残余图的容量
            rGraph[v][u] += path_flow  # 反向边的容量增加
            v = parent[v]

        max_flow += path_flow

    return max_flow

# 示例图
graph = [
    [0, 16, 13, 0, 0, 0],
    [0, 0, 10, 12, 0, 0],
    [0, 4, 0, 0, 14, 0],
    [0, 0, 9, 0, 0, 20],
    [0, 0, 0, 7, 0, 4],
    [0, 0, 0, 0, 0, 0]
]

source = 0
sink = 5
print("The maximum possible flow is", ford_fulkerson(graph, source, sink))

最小费用问题

概述

最小费用流问题是在一个流网络中找到一种流方案,使得从源点到汇点的总流量满足需求,并且总费用最小。每条边除了容量之外,还具有一个单位流量的费用。

基本概念

  • 费用:每条边 (u,v) 的单位流量费用 cost(u,v)。
  • 总费用:流量方案的总费用为所有边的费用乘以流量的总和。

解决方法

最小费用流问题可以通过修改后的Bellman-Ford算法Dijkstra算法结合增广路径算法来解决。

 代码:

from collections import deque

def spfa(graph, cost, source, sink, n):
    dist = [float('inf')] * n
    in_queue = [False] * n
    parent = [-1] * n
    flow = [0] * n

    dist[source] = 0
    flow[source] = float('inf')
    queue = deque([source])
    in_queue[source] = True

    while queue:
        u = queue.popleft()
        in_queue[u] = False
        for v in range(n):
            if graph[u][v] > 0 and dist[v] > dist[u] + cost[u][v]:
                dist[v] = dist[u] + cost[u][v]
                parent[v] = u
                flow[v] = min(flow[u], graph[u][v])
                if not in_queue[v]:
                    queue.append(v)
                    in_queue[v] = True

    return parent, flow[sink] if dist[sink] != float('inf') else 0

def min_cost_max_flow(graph, cost, source, sink):
    n = len(graph)
    max_flow = 0
    min_cost = 0

    while True:
        parent, path_flow = spfa(graph, cost, source, sink, n)
        if path_flow == 0:
            break

        max_flow += path_flow
        v = sink
        while v != source:
            u = parent[v]
            graph[u][v] -= path_flow  # 更新流量
            graph[v][u] += path_flow  # 反向边的流量增加
            min_cost += path_flow * cost[u][v]
            v = parent[v]

    return max_flow, min_cost

# 示例图
graph = [
    [0, 3, 2, 0, 0],
    [0, 0, 1, 3, 0],
    [0, 0, 0, 2, 3],
    [0, 0, 0, 0, 4],
    [0, 0, 0, 0, 0]
]

cost = [
    [0, 2, 4, 0, 0],
    [0, 0, 1, 2, 0],
    [0, 0, 0, 1, 3],
    [0, 0, 0, 0, 2],
    [0, 0, 0, 0, 0]
]

source = 0
sink = 4
max_flow, min_cost = min_cost_max_flow(graph, cost, source, sink)
print("Maximum flow:", max_flow)
print("Minimum cost:", min_cost)
  • 28
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值