数学建模——商人过河(Matlab)

本文通过数学模型解决了商人与随从安全过河的问题,确保在任何时刻任一岸的商人数都不少于随从数,避免发生危险。采用状态转移的方法,通过允许状态集合和允许决策集合来寻找所有可能的安全过河路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

三名商人各带–个随从乘船渡河,一只小船只能容纳二人,由他们自己划行.随从们密约,在河的任一岸,一旦随从的人数比商人多,就杀人越货.但是如何乘船渡河的大权掌握在商人们手中.商人们怎样才能安全渡河呢?
对于这类智力游戏,经过一番逻辑思索是可以找出解决办法的.这里用数学模型求解,一是为了给出建模的示例,二是因为这类模型可以解决相当广泛的一类问题,比逻辑思索的结果容易推广.

问题分析

问题已经理想化,所以可以抽象成一个多步决策问题,即在往返的过程中都要保证两岸及船上的商人数大于等于随从数。用允许安全状态变量表示岸上的人员状态,允许决策变量表示船上的人员状况,可以找到状态随决策变化的规律,问题转换为在允许状态下的决策达到安全渡河。

建立模型

此处自行参考《数学模型》姜启源第四版的——商人们怎样安全过河的案例

以下给出代码实现的流程图
在这里插入图片描述

代码

main.m

%% 商人安全过河问题
close, clear, clc
%% 声明全局变量
% setOfAllowedStates 允许状态集合
% setOfDecisionVariables 允许决策集合
% collectionOfStatusMarkers 状态标记集合
% numberOfBusinessmen 商人的总人数
% numberOfFollowers 随从的总人数
% maximumLoadCapacity 船的最大承载人数
% totalSetOfDecisionVariables 允许决策个数
% totalSetOfAllowedStates 允许状态个数
% 0ollectionOfStatusMarkersBegin 初始编号
% collectionOfStatusMarkersProcess 中间编号
% collectionOfStatusMarkersEnd 终止编号
global setOfAllowedStates setOfDecisionVariables collectionOfStatusMarkers numberOfBusinessmen collectionOfStatusMarkersProcess cnt k;
global collectionOfStatusMarkersBegin collectionOfStatusMarkersEnd numberOfFollowers maximumLoadCapacity totalSetOfDecisionVariables totalSetOfAllowedStates;
setOfAllowedStates = [];
setOfDecisionVariables = [];
totalSetOfDecisionVariables = 0;
totalSetOfAllowedStates = 0; 

%% 输入
fprintf('输入大于3 3 2时候将不再作图\n')
numberOfBusinessmen = input('请输入商人的人数:');
numberOfFollowers = input('请输入随从的人数:');
maximumLoadCapacity = input('请输入船的最大承载人数:');

%% 作图属性
axis([0 numberOfBusinessmen 0 numberOfBusinessmen]);
set(gca, 'XTick', 0 : 1 : numberOfBusinessmen), set(gca, 'YTick', 0 : 1 : numberOfFollowers);
xlabel('此岸商人数'), ylabel('此岸随从数'), title('允许状态集合')
grid on

%% 允许状态集合
for businessmen = 0 : numberOfBusinessmen
    for followers = 0 : numberOfFollowers
        if(businessmen == 0 || businessmen == numberOfBusinessmen || (businessmen >= followers && numberOfBusinessmen - businessmen >= numberOfFollowers - followers))
            totalSetOfAllowedStates = totalSetOfAllowedStates + 1;
            setOfAllowedStates(totalSetOfAllowedStates, :) = [businessmen, followers];
            hold on
            plot(businessmen, followers, 'b*', 'MarkerSize', 10);
        end
    end
end

setOfAllowedStates = [setOfAllowedStates, ones(totalSetOfAllowedStates, 1); setOfAllowedStates, zeros(totalSetOfAllowedStates, 1)];
totalSetOfAllowedStates = totalSetOfAllowedStates * 2;
fprintf('共有%d种允许状态\n', totalSetOfAllowedStates)
printfAllowedStates = input('是否打印允许状态集合y/n?','s');
if printfAllowedStates == 'y'
    fprintf('允许状态集合:')
    setOfAllowedStates
end
%% 允许决策集合
for businessmen = 0 : numberOfBusinessmen
    for followers = 0 : numberOfFollowers
        if(businessmen + followers >= 1 && businessmen + followers <= maximumLoadCapacity)
            totalSetOfDecisionVariables = totalSetOfDecisionVariables + 1;
            setOfDecisionVariables(totalSetOfDecisionVariables, :) = [businessmen, followers];
        end
    end
end
fprintf('共%d个允许决策\n',totalSetOfDecisionVariables);
printfDecisionVariables = input('是否打印允许决策集合y/n?','s');
if printfDecisionVariables == 'y'
    fprintf('允许决策集合:');
    setOfDecisionVariables
end
%% 状态标记集合
% 初始状态利用哈希表
collectionOfStatusMarkers = zeros(totalSetOfAllowedStates, 1);
collectionOfStatusMarkersBegin = find(ismember(setOfAllowedStates, [numberOfBusinessmen, numberOfBusinessmen, 1], 'rows') == 1); % 初始状态编号
collectionOfStatusMarkersEnd = find(ismember(setOfAllowedStates, [0, 0, 0], 'rows') == 1); % 终止状态编号
cnt = 0;
k = 1;
collectionOfStatusMarkers(collectionOfStatusMarkersBegin) = 1;
collectionOfStatusMarkersProcess(1) = collectionOfStatusMarkersBegin;

%% 模拟过河(递归实现)
crossTheRiver(collectionOfStatusMarkersBegin);
if cnt == 0
    fprintf('This question currently has no answer.\n')
else
    fprintf('共有%d个方法\n', cnt)
end

crossTheRiver.m

function crossTheRiver(present)
    global setOfAllowedStates setOfDecisionVariables collectionOfStatusMarkers collectionOfStatusMarkersProcess k  numberOfBusinessmen;
    global collectionOfStatusMarkersEnd totalSetOfDecisionVariables cnt;
    if present == collectionOfStatusMarkersEnd
        printCurrentResults();
        if numberOfBusinessmen <= 3
            plotCurrentResults(cnt + 1);
        end
    else
        for i = 1 : totalSetOfDecisionVariables
            possible(1, [1 2]) = setOfAllowedStates(present, [1 2]) + ((-1)^(setOfAllowedStates(present, 3))) * setOfDecisionVariables(i, :);
            possible(1, 3) = 1 - setOfAllowedStates(present, 3);
            tag = ismember(setOfAllowedStates, possible, 'rows');
            if sum(tag) == 1
                next = find(tag == 1);
                if collectionOfStatusMarkers(next) == 0
                    collectionOfStatusMarkers(next) = 1;
                    k = k + 1;
                    collectionOfStatusMarkersProcess(k) = next;
                    crossTheRiver(next);
                    collectionOfStatusMarkers(next) = 0;
                    k = k - 1;
                end
            end
        end
    end
end

printCurrentResults.m

function printCurrentResults()
    global setOfAllowedStates numberOfBusinessmen collectionOfStatusMarkersProcess cnt k numberOfFollowers
    cnt = cnt + 1;
    fprintf('方法 %d:\n',cnt);
    fprintf('\t\t\t\t此\t岸\t\t\t\t\n');
    fprintf('\t商人数\t\t\t随从数\t\t\t船的位置\n');
    for i=1:k
        fprintf('%d:\t %d\t\t\t\t %d',i,setOfAllowedStates(collectionOfStatusMarkersProcess(i),1),setOfAllowedStates(collectionOfStatusMarkersProcess(i),2));
        if setOfAllowedStates(collectionOfStatusMarkersProcess(i),3)==1 % 船在此岸
            fprintf('\t\t\t\t\t此岸\n');
        else
            fprintf('\t\t\t\t\t彼岸\n');
        end
    end
    fprintf('\n');
end

plotCurrentResults.m

function plotCurrentResults(cntPicture)
    global setOfAllowedStates collectionOfStatusMarkersProcess k numberOfBusinessmen numberOfFollowers;
    figure(cntPicture)
    axis([0 numberOfBusinessmen 0 numberOfBusinessmen]);
    set(gca, 'XTick', 0 : 1 : numberOfBusinessmen), set(gca, 'YTick', 0 : 1 : numberOfFollowers);
    xlabel('此岸商人数'), ylabel('此岸随从数'), title(['方法', num2str(cntPicture)])
    grid on
    for i = 2 : k
        hold on
        plot([setOfAllowedStates(collectionOfStatusMarkersProcess(i - 1),1), setOfAllowedStates(collectionOfStatusMarkersProcess(i),1)],...
            [setOfAllowedStates(collectionOfStatusMarkersProcess(i - 1),2), setOfAllowedStates(collectionOfStatusMarkersProcess(i),2)], 'LineWidth',3)
        pause(0.2)
    end
end

动态展示

在这里插入图片描述


如有错误以及可以改进的地方欢迎在下方评论区留言!

商人过河问题可以使用数学建模来解决,而MATLAB是一个非常适合进行数学建模和计算的工具。下面是一个使用MATLAB进行商人过河问题数学建模的示例: 首先,我们可以使用二进制向量来表示商人、野人和船的位置状态,其中0表示左岸,1表示右岸。假设商人和野人的数量分别为M和N。 下面是MATLAB代码示例: % 商人和野人的数量 M = 3; N = 3; % 初始状态(左岸) initial_state = [ones(1, M+N), 0]; % 目标状态(右岸) target_state = [zeros(1, M+N), 1]; % 状态转移规则函数 state_transition = @(state, action) state + action; % 判断状态是否合法 is_valid_state = @(state) ... all(state(1:M) >= state(M+1:end) | state(1:M) == 0) && ... all(state(M+1:end) >= state(1:M) | state(M+1:end) == 0); % 判断是否达到目标状态 is_goal_state = @(state) all(state == target_state); % 使用递归函数解决问题 solution = recursive_solve(initial_state, []); % 递归函数 function solution = recursive_solve(state, path) % 如果已经达到目标状态,则返回路径 if is_goal_state(state) solution = path; return; end % 遍历所有可能的动作 actions = generate_actions(state); for i = 1:size(actions, 1) action = actions(i, :); % 计算新状态 new_state = state_transition(state, action); % 如果新状态合法,则继续递归求解 if is_valid_state(new_state) solution = recursive_solve(new_state, [path; action]); % 如果找到解,则返回 if ~isempty(solution) return; end end end % 如果找不到解,则返回空 solution = []; end % 生成所有可能的动作 function actions = generate_actions(state) M = sum(state(1:end-1) == 1); N = sum(state(1:end-1) == 0); % 动作格式:[商人移动数 野人移动数 船移动方向] % 商人独自移动 actions = [-1 0 -1; -2 0 -1; 1 0 1; 2 0 1]; % 野人独自移动 actions = [actions; 0 -1 -1; 0 -2 -1; 0 1 1; 0 2 1]; % 商人和野人一起移动 for i = 1:M for j = 1:N if i + j <= 2 actions = [actions; -i -j -1; i j 1]; end end end end 这个示例代码使用了递归求解的方法来找到商人过河问题的解。代码中的state_transition函数定义了状态转移规则,is_valid_state函数判断状态是否合法,is_goal_state函数判断是否达到目标状态。generate_actions函数生成所有可能的动作。 注意:这只是商人过河问题的一个简单数学建模示例,实际问题可能需要更多的约束和复杂的规则。你可以根据具体需求进行修改和扩展。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值