【2023年第十三届MathorCup高校数学建模挑战赛】思路总结分析

2023-05-16

【写在前面的话】
我们选择A题,分析A题题目可以得知属于一种组合优化模型,类似于旅行商问题,0-1背包问题等等。该类问题通常采用遗传算法,粒子群算法,模拟退火算法等算法进行求解。由于本题需要我们建立出数学模型之后通过转换为QUBO模型,从而建立量子退火模型,从而可以实现在量子计算机中求解。所以我们直接选择采用模拟退火模型。

第一问太简单,我TM直接穷举🤣,代码给你们看看(别太荒谬哈哈):

clc;clear;
load bank.mat
max=0;
i_max=0;
j_max=0;
for j=1:2:200
    for i=1:10
        banifit=1000000*bank(i,j)*0.08*(1-bank(i,j+1))-1000000*bank(i,j)*bank(i,j+1);
        if(banifit>max)
            max=banifit;
            i_max=i;
            j_max=j;
        end
    end
end

开玩笑的,正儿八经还得用模拟退火模型求解嘿嘿
第一问的代码(这么详细的注释,我哭死,你怎么还不哭😡):

clc;
clear;
tic
load bank.mat
x1=bank(:,1:2:end);
x1=x1(:);
x1=x1';
x2=bank(:,2:2:end);
x2=x2(:);
x2=x2';
n=length(x1);
%% 参数初始化
T0 = 1000;   % 初始温度
T = T0; % 迭代中温度会发生改变,第一次迭代时温度就是T0
maxgen = 500;  % 最大迭代次数
Lk = 20;  % 每个温度下的迭代次数
alfa = 0.95;  % 温度衰减系数

%%  生成初始解
% 初始解中每个元素都为0,表示一个阈值都不选
way0 = zeros(1,n); 
load_matrix=way0;
% 计算相应的银行收益率
profit0=sum(1000000*0.08*way0.*x1.*(1-x2)-1000000*way0.*x1.*x2);

%% 记录一些中间过程的参数,方便最后画图
max_profit = profit0;     % 初始化找到的最佳的解对应的利润为profit0
PROFIT = zeros(maxgen,1); % 记录每一次外层循环结束后找到的max_profit (方便画图)

%% 模拟退火过程
for iter = 1 : maxgen  % 外循环, 我这里采用的是指定最大迭代次数
    for i = 1 : Lk  %  内循环,在每个温度下开始迭代       
        [way1,load_matrix] = gen_new_way(way0,load_matrix,n);  % 调用我们自己写的gen_new_way函数生成新的阈值方案
        profit1 =sum(1000000*0.08*way1.*x1.*(1-x2)-1000000*way1.*x1.*x2); % 计算新阈值方案的利润
        if profit1 > profit0 
            way0 = way1; % 更新当前阈值方案为新的阈值方案
            profit0 = profit1;
        else
            p = exp(-(profit0 - profit1)/T); % 根据Metropolis准则计算一个概率
            if rand(1) < p   % 生成一个随机数和这个概率比较,如果该随机数小于这个概率
                way0 = way1;  % 更新当前阈值方案为新阈值方案
                profit0 = profit1;
            end
        end
        % 判断是否要更新找到的最佳的解
        if profit0 > max_profit  % 如果当前解更好,则对其进行更新
            max_profit = profit0;  % 更新最大的利润
            best_way = way0;  % 更新找到的最好的阈值方案
        end
    end
    PROFIT(iter) = max_profit; % 保存本轮外循环结束后找到的最大的利润
    T = alfa*T;   % 温度下降     
end

%%结果
disp('最佳的阈值方案是:'); disp(best_way)
disp('此时最优值是:'); disp(max_profit)
plot(1:maxgen,PROFIT,'r-');
xlabel('迭代次数');
ylabel('银行最大收益值');
hold on
toc

主函数中用到的gen_new_way函数:

function [way1, delta_weight] = gen_new_way(way0, n, weight)
% way0:原来的装载方案;  n是物品个数  ; weight是物品的重量
% way1:新的装载方案; delta_weight:重量的变化量
       i = randi([1,n],1);  % 随机选取一个物品i
       if way0(i) == 1  % 如果物品i在原来的背包中
           way0(i) = 0;  % 从背包里取出物品i
           empty_ind = find(way0==0); % 看看背包中哪几个位置是空的
           length_empth_ind = length(empty_ind);  % 计算空位置的个数
           ind = randi([1, length_empth_ind], 1); % 在这几个空位置中取一个随机下标 
           j = empty_ind(ind);  % j就是随机选择出来的背包中的某个空位置
           way0(j) = 1;  % 装入物品j
           delta_weight = weight(j) - weight(i);  % 计算重量的变化量
       else  % 如果物品i原来就不在背包中
           way0(i) = 1; % 在背包中放入物品i
           delta_weight = weight(i);  % 计算重量的变化量
           if rand(1) < 0.5 % 判断是否需要再取出一个物品(这里的0.5是需要再取出物品的概率,可以自己调整)
               fill_ind = find(way0==1); % 看看背包中哪几个位置有物品
               length_fill_ind = length(fill_ind);  % 计算有物品的位置的个数
               ind = randi([1, length_fill_ind], 1); % 在这几个有物品的位置中取一个随机下标 
               j = fill_ind(ind);  % j就是随机选择出来的背包中的某个有物品的位置
               way0(j) = 0;  % 取出物品j
               delta_weight = weight(i) - weight(j);  % 计算重量的变化量
           end
       end
       way1 = way0;  % 将调整后的way0赋值给way1
end

第二问就是第一问的一个拓展,主函数差不多,要变得是收益函数的形式和新解的产生方法(正经起来了不是?):

clc;
clear;
tic
load bank.mat
x1=bank(:,1:2:6);
x1=x1(:);
x1=x1';
x2=bank(:,2:2:6);
x2=x2(:);
x2=x2';
n=length(x1);
%% 参数初始化
T0 = 1000;   % 初始温度
T = T0; % 迭代中温度会发生改变,第一次迭代时温度就是T0
maxgen = 50;  % 最大迭代次数
Lk = 20;  % 每个温度下的迭代次数
alfa = 0.95;  % 温度衰减系数

%%  生成初始解
% 初始解中每个元素都为0,表示一个阈值都不选
way0 = zeros(1,n);
% 随机选取三个阈值
i_1= randi([1,10],1); 
i_2= randi([11,20],1); 
i_3= randi([21,30],1); 
way0(i_1)=1;
way0(i_2)=1;
way0(i_3)=1;
% 计算三个阈值的组合通过率和组合坏账率
T_tresh_ind=find((way0)~=0);
T_tresh=x1(T_tresh_ind(1))*x1(T_tresh_ind(2))*x1(T_tresh_ind(3));
H_tresh_ind=find((way0)~=0);
H_tresh=1/3*(x2(H_tresh_ind(1))+x2(H_tresh_ind(2))+x2(H_tresh_ind(3)));
% 计算相应的银行收益率
profit0=sum(1000000*0.08*T_tresh*(1-H_tresh)-1000000*T_tresh*H_tresh);

%% 记录一些中间过程的参数,方便最后画图
max_profit = profit0;     % 初始化找到的最佳的解对应的利润为profit0
PROFIT = zeros(maxgen,1); % 记录每一次外层循环结束后找到的max_profit 

%% 模拟退火过程
for iter = 1 : maxgen  % 外循环, 我这里采用的是指定最大迭代次数
    for i = 1 : Lk  %  内循环,在每个温度下开始迭代       
        [way1] = gen_new_way_2(way0,n);  % 调用我们自己写的gen_new_way函数生成新的装载方案
        T_tresh_ind=find((way1)~=0);
        T_tresh=x1(T_tresh_ind(1))*x1(T_tresh_ind(2))*x1(T_tresh_ind(3));
        H_tresh_ind=find((way1)~=0);
        H_tresh=1/3*(x2(H_tresh_ind(1))+x2(H_tresh_ind(2))+x2(H_tresh_ind(3)));
        profit1 =sum(1000000*0.08*T_tresh*(1-H_tresh)-1000000*T_tresh*H_tresh);
        if profit1 > profit0  
            way0 = way1; % 更新当前阈值方案为新的阈值方案
            profit0 = profit1;
        else
            p = exp(-(profit0 - profit1)/T); % 根据Metropolis准则计算一个概率
            if rand(1) < p   % 生成一个随机数和这个概率比较,如果该随机数小于这个概率
                way0 = way1;  % 更新当前阈值方案为新的阈值方案
                profit0 = profit1;
            end
        end
        % 判断是否要更新找到的最佳的解
        if profit0 > max_profit  % 如果当前解更好,则对其进行更新
            max_profit = profit0;  % 更新最大的利润
            best_way = way0;  % 更新找到的最好的阈值方案
        end
    end
    PROFIT(iter) = max_profit; % 保存本轮外循环结束后找到的最大的利润
    T = alfa*T;   % 温度下降     
end

%% 结果
disp('最佳的阈值方案是:'); disp(best_way)
disp('此时最优值是:'); disp(max_profit)
plot(1:maxgen,PROFIT,'r-');
xlabel('迭代次数');
ylabel('银行最大收益值');
hold on
toc

主函数中用到的gen_new_way_2函数:

function [way1] = gen_new_way_2(way0,n)
% way0:原来的阈值方案;  
% way1:新的阈值方案;
       way0_split=reshape(way0,10,[]);
       way0_1=way0_split(:,1);
       way0_2=way0_split(:,2);
       way0_3=way0_split(:,3);
       i=randi([1,n],1);
       if i<=10
           if way0(i) == 1  % 如果阈值i在原来的选择范围中
               empty_ind = find(way0_1==0); % 查看哪几个阈值是没有被选中的
               length_empth_ind = length(empty_ind);  % 计算空位置的个数
               ind = randi([1, length_empth_ind], 1); % 在这几个空位置中取一个随机下标 
               j = empty_ind(ind);  % j就是随机选择出来的阈值中的某个空位置
               way0(j) = 1;  % 将该阈值纳入考虑范畴
               way0(i) = 0;  % 从已选择的阈值中删除第i个阈值
           else
               empty_ind = find(way0_1==1);
               j = empty_ind(1);
               way0(j)=0;
               way0(i)=1;
           end
       elseif i<=20
           if way0(i) == 1 
               empty_ind = find(way0_2==0); 
               length_empth_ind = length(empty_ind); 
               ind = randi([1, length_empth_ind], 1); 
               j = empty_ind(ind); 
               way0(j+10) = 1; 
               way0(i) = 0; 
           else
               empty_ind = find(way0_2==1);
               j = empty_ind(1);
               way0(j+10)=0;
               way0(i)=1;
           end
       else
           if way0(i) == 1  
               empty_ind = find(way0_3==0);
               length_empth_ind = length(empty_ind); 
               ind = randi([1, length_empth_ind], 1);
               j = empty_ind(ind); 
               way0(j+20) = 1; 
               way0(i) = 0; 
           else
               empty_ind = find(way0_3==1);
               j = empty_ind(1);
               way0(j+20)=0;
               way0(i)=1;
           end
       end
       way1 = way0;  % 将调整后的阈值选择赋值给way1
end


其实我感觉我第二问的产生新解的函数写的有点复杂了,有点冗余,第三问虽然问题更复杂了,涉及的数据更多了,但是代码又简化了很多嘿嘿:

clc;
clear;
tic
load bank.mat
x1=bank(:,1:2:end);
x1=x1(:);
x1=x1';
x2=bank(:,2:2:end);
x2=x2(:);
x2=x2';
%% 参数初始化
n=length(x1);
T0 = 1000;   % 初始温度
T = T0; % 迭代中温度发生改变,第一次迭代时温度T0
maxgen = 500;  % 最大迭代次数
Lk = 20;  % 每个温度下的迭代次数
alfa = 0.96;  % 温度衰减系数

%%  生成初始解
% 初始解中每个元素都为0,表示一个阈值都不选取
way0 = zeros(1,n); 
% 随机选取三个阈值 
i_1= randi([1,10],1);  
i_2= randi([11,20],1); 
i_3= randi([21,30],1); 
way0(i_1)=1;
way0(i_2)=1;
way0(i_3)=1;
% 计算三个阈值的组合通过率和组合坏账率
T_tresh_ind=find((way0)~=0);
T_tresh=x1(T_tresh_ind(1))*x1(T_tresh_ind(2))*x1(T_tresh_ind(3));
H_tresh_ind=find((way0)~=0);
H_tresh=1/3*(x2(H_tresh_ind(1))+x2(H_tresh_ind(2))+x2(H_tresh_ind(3)));
% 计算相应的银行收益率
profit0=sum(1000000*0.08*T_tresh*(1-H_tresh)-1000000*T_tresh*H_tresh);

%% 记录一些中间过程的参数,方便最后画图
max_profit = profit0;     % 初始化找到的最佳的解对应的利润为profit0
PROFIT = zeros(maxgen,1); % 记录每一次外层循环结束后找到的max_profit 

%% 模拟退火过程
for iter = 1 : maxgen  % 外循环, 我这里采用的是指定最大迭代次数
    for i = 1 : Lk  %  内循环,在每个温度下开始迭代       
        [way1] = gen_new_way_3(way0,n);  % 调用我们自己写的gen_new_way函数生成新的阈值方案
        % 计算三个阈值的组合通过率和组合坏账率和相应的银行收益率
        T_tresh_ind=find((way1)~=0);
        T_tresh=x1(T_tresh_ind(1))*x1(T_tresh_ind(2))*x1(T_tresh_ind(3));
        H_tresh_ind=find((way1)~=0);
        H_tresh=1/3*(x2(H_tresh_ind(1))+x2(H_tresh_ind(2))+x2(H_tresh_ind(3)));
        profit1 =sum(1000000*0.08*T_tresh*(1-H_tresh)-1000000*T_tresh*H_tresh);
        %权衡是否采用新的阈值方案
        if profit1 > profit0  % 如果没有超重,而且新装载方案的利润更高
            way0 = way1; % 更新当前装载方案为新装载方案
            profit0 = profit1;
        else
            p = exp(-(profit0 - profit1)/T); % 根据Metropolis准则计算一个概率
            if rand(1) < p   % 生成一个随机数和这个概率比较,如果该随机数小于这个概率
                way0 = way1;  % 更新当前装载方案为新装载方案
                profit0 = profit1;
            end
        end
        % 判断是否要更新找到的最佳解
        if profit0 > max_profit  % 如果当前解更好,则对其进行更新
            max_profit = profit0;  % 更新最大的利润
            best_way = way0;  % 更新找到的最好的装载方案
        end
    end
    PROFIT(iter) = max_profit; % 保存本轮外循环结束后找到的最大的利润
    T = alfa*T;   % 温度下降     
end

%% 结果
disp('最佳的阈值组合是:'); disp(best_way)
disp('此时最优值是:'); disp(max_profit)
plot(1:maxgen,PROFIT,'r-');
xlabel('迭代次数');
ylabel('银行最大收益值');
hold on
toc

主函数中用到的gen_new_way_3函数:

function [way1] = gen_new_way_3(way0,n)
       empty_ind = find(way0==1); % 查找目前的阈值选择的是哪几个,生成其下标索引
       length_empth_ind = length(empty_ind);  
       ind = randi([1, length_empth_ind], 1); 
       j = empty_ind(ind);%随机选取目前下标中的一个
       way0(j) = 0;%将该阈值剔除出我们的选择范围
       i=randi([1,n],1);%随机选择一个新的阈值
       while(panduan(i,empty_ind)==1)%判断该新阈值是否与原阈值属于同一个银行卡
            i=randi([1,n],1);%如果已有,则重新生成,保证一个银行卡只选择一个阈值
       end
       way0(i) = 1; %确保该新阈值是有效阈值之后,将其纳入选择范围
       way1 = way0;  % 将调整后的阈值选择赋值给way1
end


其中用到的panduan函数:

function [result] = panduan(i,index)
%输入随机生成的阈值下标和参数为1的索引向量
%判断新生成的阈值下标是否处于原阈值下标属于同一个银行卡
%如果属于同一个银行卡,则返回1,如果不属于,则返回0
    if i>=index(1)-mod(index(1),10)+1 && i<=index(1)-mod(index(1),10)+10
        result=1;
    elseif i>=index(2)-mod(index(2),10)+1 && i<=index(2)-mod(index(2),10)+10
        result=1;
    elseif i>=index(3)-mod(index(3),10)+1 && i<=index(3)-mod(index(3),10)+10
        result=1;
    else
        result=0;
    end
end

第二三问运行结果每次可能都会有些许不同,因为并不是只有一个最优解,所以他会在几个最优解之间跳动,这是正常现象。
还不点赞?!怎么回事?!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【2023年第十三届MathorCup高校数学建模挑战赛】思路总结分析 的相关文章

  • 如何使QGraphicsItem不随QGraphicsView放大缩小而改变大小

    一 简述 在使用QGraphicsView过程中 xff0c 有时候我们需要对view进行缩放 xff0c 但是对于一般正常的加入view中的item都会随着view的大小变化而变化 xff0c 但是如果我们想让某些item不随view的缩
  • 【linux系统如何查看内核版本、操作系统版本等信息】

    有时候需要查看linux系统的内核版本 xff0c 可以有多种方法 xff0c 方法如下 xff1a xff08 下面以优麒麟系统为例 xff09 方法1 xff1a 打开mate终端 xff0c 在命令行输入以下命令 xff1a unam
  • 【linux系统如何安装arm交叉编译工具链】

    文章目录 前言一 arm交叉编译器介绍命名规则具体编译器 二 Arm GNU Toolchain安装总结 前言 本文简要介绍arm交叉编译器及工具链的安装方法 一 arm交叉编译器介绍 命名规则 交叉编译工具链的命名规则为 xff1a ar
  • 比较冒泡排序、选择排序和快速排序的时间(C语言实现)

    文章目录 前言代码设计代码实现运行结果结果分析稳定性测试 总结 前言 本文主要比较冒泡排序 快速排序 选择排序的时间 冒泡排序和快速排序的思想可以参考我转载的以下博文 xff1a https blog csdn net gogo0707 a
  • freertos应用程序常见错误排查

    freertos系统应用程序常见问题 对一些比较常见的问题 xff0c 下面简要的以 FAQ 问答 的形式给出可能的原因和解决方法 问题现象 xff1a 在一个 Demo 应用程序中增加了一个简单的任务 xff0c 导致应用程序崩溃 任务创
  • keil5编译工程常见问题汇总

    简介 我们在编译keil工程的时候总是遇到很多问题 xff0c 我把一些常见的问题和解决方案汇总下来 xff0c 仅供大家参考 问题汇总 问题1 问题描述 选择arm v6版本编译器 xff0c 编译keil5工程 xff0c 报错 xff
  • mdk arm debug配置

    简述 本文简要讲述启动调试之前如何配置debug 点击魔术棒 xff0c 进入debug选项界面 xff0c 如下图 xff1a 我们可以选择软件仿真 xff0c 也可以选择硬件仿真 xff08 软件仿真不需要接开发板和仿真器 xff09
  • stm32高级定时器实现pwm互补输出

    简介 stm32设备一般都有很多类型的定时器 xff0c 常见的有systick timer 基本定时器 通用定时器 高级定时器 看门狗定时器 RTC等等 xff0c 本文简单介绍高级定时器是如何实现pwm互补输出 详细 我这里使用的dev
  • shell脚本基础知识(入门)

    简介 本文会全面介绍shell脚本的基础知识 脚本格式 要把shell命令放到一个 脚本 当中 xff0c 有一个要求 xff1a 脚本的第一行必须写成类似这样的格式 xff1a bin bash bash是一个shell解释器 xff0c
  • 记ADB shell for循环踩坑

    abd 里面的shell的电脑Linux的shell有点不太一样 以下这些案例均不能执行 xff1a for i 61 1 i lt 61 100 i 43 43 do echo i done for i in 1 100 do echo
  • linux线程调度策略简述

    简述 linux系统调度执行的最小单位是线程 xff0c 线程的调度策略有以下三种 xff1a xff08 1 xff09 SCHED FIFO 其静态优先级必须设置为1 99 xff0c 这将意味着一旦线程处于就绪态 xff0c 他就能立
  • stm32串口发送接口

    简介 本文记录一下stm32标准库实现串口发送功能和接收功能的接口函数 轮询方式发送串口数据 1 标准库实现 span class token comment 61 61 61 61 61 61 61 61 61 61 61 61 61 6
  • linux系统线程池

    简述 一个进程中的线程就好比是一家公司里的员工 xff0c 员工的数目应该根据公司的业务多少来定 xff0c 太少了忙不过来 xff0c 但是太多了也浪费资源 最理想的情况是让进程有一些初始数目的线程 xff08 线程池 xff09 xff
  • STM32串口环形缓冲区

    目录 1 xff1a 概述 2 xff1a 代码 1 xff1a 概述 1 1 xff1a 本篇实现串口驱动 xff0c 实现printf函数的重定向 xff0c 实现串口的中断接受和发送 xff0c 效仿modbus协议中的3 5T超时机
  • 天地飞6通道遥控器解码

    在做四轴 xff0c 想到要改造一下遥控器 我用的是天地飞6通道2 4G版 改造的思路是这样的 xff1a 用M8单片机读取PPM信号 xff0c 用液晶显示出6个通道的解码 xff0c 当然这个解码还需要根据飞控板进行一下 校准 xff0
  • 如何计算网络协议校验和以及为什么这么计算

    一 校验和是如何计算的 xff1f 这个问题牵扯到计算机最底层最神秘的部分 二进制运算 说实话 xff0c 从学计组计统起 xff0c 我就比较讨厌思考二进制的相关运算 但毕竟是学这个的 xff0c 只好硬着头皮想了 首先发送方校验和的计算
  • JAVA又臭又长,是一门垃圾语言,早晚会被淘汰

    1 JAVA又臭又长 xff0c 是一门垃圾语言 xff0c 早晚会被淘汰 2 JAVA能做的 xff0c python 等上层解释类语言大部分都可以坐到 3 JAVA为了面向对象而面向对象 xff0c 导致程序冗长 xff0c 效率低下
  • 封装OkHttp

    懒汉 安全 加同步 私有的静态成员变量 只声明不创建 私有的构造方法 提供返回实例的静态方法 private static OkHttpClient okHttpClient 61 null private OkHttp3Util publ
  • WXP380 电容式MEMS高性能数字气压传感器 电容式MEMS压力传感器快速代DPS310 BMP380 SLP06 HP303B “电容式”噪声超低的高精度MEMS气压传感器;

    午芯高科WXP380气压传感器 稳定供应 填补国内空白 2 WXP380气压传感器的 电容式 MEMS芯片填补了国内技术空白 xff01 高性能 xff1a 电容式 噪声超低的高精度MEMS气压传感器 xff1b 高度差测量精确度小至2cm
  • 解决ros的rosdep update报错的问题。

    参见以下文章 xff1a https blog csdn net leida wt article details 115120940 如果 xff1a https raw githubusercontent com ros rosdist

随机推荐