一文搞懂:0-1整数规划与蒙特卡罗模拟(附MATLAB代码详解)

发布于:2025-09-03 ⋅ 阅读:(12) ⋅ 点赞:(0)

前言:欢迎各位光临本博客,这里小编带你直接手撕Make/Makefile (自动化构建),文章并不复杂,愿诸君耐其心性,忘却杂尘,道有所长!!!!

解释_chmod_命令_(3).gif

**🔥个人主页:IF’Maxue-CSDN博客

🎬作者简介:C++研发方向学习者

📖**个人专栏:
《C语言》
《C++深度学习》
《Linux》
《数据结构》
《数学建模》
**

⭐️人生格言:生活是默默的坚持,毅力是永久的享受。不破不立,远方请直行!

一、0-1整数规划:直接用intlinprog函数搞定

先从简单的开始——0-1整数规划,就是问题里的变量只能取0或1(比如“选不选某个方案”“用不用某个资源”)。这类问题不用自己写复杂算法,直接调用MATLAB的intlinprog函数就行,具体用法看下面这张图:

image.png

函数里只需按要求填“目标函数系数”“约束条件”等参数,就能自动算出最优解,适合快速解决0-1变量的规划问题。

二、蒙特卡罗模拟:本质是“暴力随机试”

如果问题复杂,或者不想纠结公式,蒙特卡罗方法就是个好选择——核心思路是“大量随机模拟,从结果里找最优”,比如想找最省钱的方案,就随机试1万次,挑花钱最少的那次。下面用3个例题讲透怎么用代码实现。

例题一:0-1变量约束求“最小和”

1. 问题背景

假设有6个变量(只能是0或1),要满足几个约束条件(比如“变量1+2+3至少为1”“变量4+6至少为1”等),最终要找这6个变量的和最小的解(相当于“用最少的变量满足约束”)。

2. 代码解析

代码分“定义部分”和“核心循环”,咱们逐行拆:

(1)定义部分:初始化参数
clear;clc;%清屏(清空工作区和命令行,避免之前的代码干扰)
n = 10000; %模拟次数(“蒙”1万次,次数越多越可能找到最优解)
res_min = +Inf;%最小值初始化(先设成“无穷大”,这样后面遇到更小的数才能更新)
res_x = 0;%存最优解(最后要输出的那组0-1变量)

这里重点说res_min = +Inf:就像你要找“班里最矮的人”,先假设“最矮是10米”,再逐个比,比10米矮就更新——用“无穷大”初始化,能确保第一次找到满足条件的解时,一定能更新它。

(2)核心循环:随机试,找最优
for i = 1 : n  %循环1万次,每次模拟一组解
    %生成6个0或1(像掷6次硬币,正面1、反面0)
    x = randi([0,1],6,1); 
    
    %判断是否满足约束条件(括号里是题目给的所有约束,必须都满足才往下算)
    if((x(1) + x(2) + x(3) >=1) & (x(4) + x(6) >=1) & (x(3) + x(5) >= 1) & (x(2) + x(4) >= 1) & (x(1) >= 1) & (x(2) + x(4) + x(6) >= 1))
        sum_x = sum(x);%算当前这组解的变量和(比如x是[1,0,0,1,0,0],sum_x就是2)
        
        %如果当前和比之前的“最小和”还小,就更新最优解
        if(sum_x < res_min)
            res_x = x;%存下这组更好的解
            res_min = sum_x;%更新最小和
        end
    end
end

简单说:每次随机造6个0/1,检查是否符合所有要求,符合就算“变量和”,如果这个和比之前的最小值还小,就记下来——1万次试完,最后记下来的就是大概率最优的解。

例题四:工厂设备分配求“最优成本”

1. 问题背景

有6台设备要分给4个企业(每个企业至少分到1台设备),已知“第j台设备分给第i个企业”的成本(存在C这个成本矩阵里),要找“总成本最大”的分配方案(从代码里sum > res能看出来是求最大成本)。

2. 代码解析
(1)定义部分:初始化数据
clear;clc;%清屏
n = 10000; %模拟1万次
%成本矩阵C:C(j,i)表示“第j台设备分给第i个企业”的成本(共6台设备、4个企业)
C = [4,2,3,4;
     6,4,5,5;
     7,6,7,6;
     7,8,8,6;
     7,9,8,6;
     7,10,8,6];
res_x = 0;%存最优分配方案(比如[1,2,3,4,1,2]表示设备1分企业1、设备2分企业2...)
res = 0;%存最优成本(最后要输出的最大成本)
(2)核心循环:随机分配,查约束,算成本
for k = 1:n  %循环1万次
    flag = 1;%标记是否满足“每个企业至少1台”(1=满足,0=不满足)
    %生成分配方案:1行6列,每个数是1-4(表示设备分给1-4号企业)
    x = randi([1,4],1,6); 
    
    %检查每个企业是否都分到设备(遍历4个企业)
    for j = 1 : 4 
        %ismember(j,x):判断j号企业是否在分配方案x里(在=1,不在=0)
        if(ismember(j,x) == 0)
            flag = 0;%有企业没分到设备,标记为不满足
            break;%不用再查了,跳出循环
        end
    end
    
    %如果满足“每个企业至少1台”,就计算总成本
    if(flag == 1)
        sum = 0;%当前方案的总成本
        %遍历6台设备,累加每台设备的成本
        for j = 1 : 6
            sum = sum + C(j, x(j));%C(j,x(j))=第j台设备分给x(j)号企业的成本
        end
        
        %如果当前总成本比之前的最优成本大,就更新
        if(sum > res)
            res = sum;%更新最优成本
            res_x = x;%更新最优分配方案
        end
    end
end

举个例子:如果x = [1,1,2,3,4,4],就会检查“1、2、3、4号企业都在x里吗?”——在,就计算成本:设备1分企业1(成本4)+设备2分企业1(成本2)+…+设备6分企业4(成本6),最后看这个总和是不是比之前的最大成本还大,是就记下来。

例题二:蒲丰投针实验——用随机算π

1. 问题背景

这是个经典实验:在画满平行线的纸上随机投针,通过“针与线相交的概率”反推出π的值。核心原理是:相交概率P = 2L/(aπ)(L是针长,a是平行线间距),所以π = 2L/(Pa)。

2. 代码解析
(1)定义部分:初始化实验参数
clear;clc;
a = 10;%平行线之间的间距(比如10cm)
L = 5;%针的长度(比如5cm,要小于a才合理)
n = 10000; %抛针次数(抛1万次,次数越多算的π越准)

%生成随机参数
ph = rand(n,1) * pi;%随机角度(0到π,因为针旋转对称,超过π重复)
x = rand(n,1) * a/2;%针的中点到最近一条平行线的距离(0到a/2,再远就有更近的线)
m = 0; %记录针与线相交的次数
y = (L/2) * sin(ph); %相交的“临界距离”(针刚好碰到线时,x的最大值)

%绘图设置:坐标轴范围(x轴角度0-π,y轴距离0-a/2),显示边框
axis([0,pi, 0,a/2]); 
box on; 

这里的y = (L/2)sin(ph)很关键:想象针绕中点转,当针与线的夹角是ph时,针的一半长度在垂直方向的投影是(L/2)sin(ph)——如果针中点到线的距离x小于这个投影,针就会和线相交(比如你把筷子斜着放,离桌边太近就会掉下去)。

(2)核心循环:抛针、计数、绘图
for i = 1 : n
    %判断是否相交:x(i)(中点距离)<= y(i)(临界距离)→ 相交
    if(x(i) <= y(i))
        m = m + 1;%相交次数加1
        plot(ph(i),x(i),'b.');%在图上画一个蓝色小点(记录这次相交的位置)
        hold on;%保持图形,后续点叠加画
    end
end
(3)计算π值
P = m/n;%相交概率(相交次数/总抛针次数)
mypi = 2*L/(P*a)%用公式反推π(代入L=5、a=10、P的值)

实验结果的可视化如图所示(蓝色小点就是相交的投针位置):

image.png

比如抛1万次,相交3180次,P=0.318,代入公式得mypi = 2*5/(0.318*10)≈3.14,和真实π值很接近。

五、张麻子例题三:蒙特卡罗解决特定场景问题

除了上面的通用场景,蒙特卡罗还能解决具体业务问题,比如张麻子例题的场景(如图所示):

image.png

这类问题的核心思路和前面一致:先明确“变量范围”(比如某个参数的取值区间)、“约束条件”(比如业务上的限制)、“目标函数”(比如求利润最大/成本最小),然后用代码做“大量随机模拟”,从结果里挑最优——本质还是“暴力试,但试得足够多,就接近最优解”。

总结:蒙特卡罗的核心逻辑

不管是0-1规划、设备分配还是算π,蒙特卡罗的本质就3步:

  1. 定规则:明确“变量能取什么值”“要满足什么约束”“要优化什么目标(最大/最小)”;
  2. 随机试:用randi/rand生成大量随机解,逐个检查是否满足约束;
  3. 找最优:从满足约束的解里,挑出目标函数最优的那个。

优点是简单好懂,不用推导复杂公式;缺点是需要足够多的模拟次数(次数越多越准)——适合解决公式难推、但能快速验证解是否有效的问题。


网站公告

今日签到

点亮在社区的每一天
去签到