元胞自动机模拟多出口疏散模型的matlab实现

版权声明:欢迎评论交流,转载请注明原作者。 https://blog.csdn.net/m0_37809890/article/details/86652211

2019美赛D题算法之一~

元胞自动机模拟多出口疏散模型的matlab实现

通过设定危险度规则,使用基于双端队列的01bfs算法快速计算每个格子的危险度,使元胞自发地倾向危险度更低的方向来模拟人员疏散模型。

  1. 设定规则:自发,社会力,网络
  2. 计算危险度:
  3. 元胞模拟:概率,社会力

规定

  1. 地图:每个小方格对应真实空间中0.4m*0.4m的空间;
  2. 地图元素值:0代表墙壁,1代表行人,2代表空格,3及以上的数字代表出口,连续的出口方格具有相同的值,不同的出口值不同。
  3. 危险度:按八个方向连通的走法,走到任一出口处所需的步数记为该方格的危险度。

算法流程

  1. 读入地图;
  2. 计算危险度:以每个出口为起点进行基于双端队列的01bfs算法,使用这种做法可以在 O ( ) O(空方格数*出口个数) 内计算出每个空格的危险度。
    算法:
    1. 将所有方格的危险度记为-1.
    2. 记当前出口编号为3;
    3. 初始化队列,将属于该出口的任一方格加入队列头部,记录其危险度为0.
    4. 取出队列头所存储的下标,记为当前格。遍历扫描当前格周围的8个格子,记为目标格。
    5. 如果目标格与当前格同属于当前出口的方格,那么目标格的预期危险度是0,否则目标格的预期危险度是当前格的危险度+1.
    6. 如果目标格的现有危险度是-1或者现有危险度大于预期危险度,就将其的现有危险度置为预期危险度,并将其加入队列:如果预期危险度是0,将其加入队列头,否则加入队列尾。
    7. 如果队列不为空,返回4.
    8. 如果还有没有访问过的出口,就将出口编号+1然后返回3,否则结束。
  3. 通过排序得出危险度从低到高的格子顺序,方便遍历;
  4. 依据既定规则向地图内的空格子加入人,产生位置随机,越珍贵的展品附件产生概率越大;
  5. 按危险度从低到高的顺序扫描所有格子,如果当前格子内是人,就计算它周围9个格子(包括自身)中未被占据的空格的前进概率,计算方法是 β ( D m D i ) \beta*(D_m-D_i) ,其中 β \beta 是归一化系数。按这个概率进行决定人下一时刻的位置。到达出口后不再变化。
  6. 显示当前时刻的地图状态。
  7. 统计未到达出口的人的数量,如果不为0,返回5.。

代码

注:代码已修改。修改后的代码和输入数据见https://download.csdn.net/download/m0_37809890/10945394

% 元胞自动机
clc; clear;
map = map_read();
colormap([0.8 0.8 0.8;1 1 1;1 0 0;0 1 1;0 1 1]);
danger = cal_danger(map);
danger_order = cal_danger_oreder(danger);
map = add_people(map, 0.20);
[K,t] = move(map, danger, danger_order);


function [K,t] = move(map, danger, danger_order)
    % 移动
    K = zeros(4000,3); t = 0;
    map_show(map);
    sel = zeros(1,27);
    while(size(find(map==1),1))
        t=t+1; 
        K(t,1)=size(find(map(30:110, 340:360  )==1),1);
        K(t,2)=size(find(map(3:25,   210:280  )==1),1);
        K(t,3)=size(find(map(110:162,420:460  )==1),1);

        go = [0,-1; 0,1; -1,0; 1,0; 1,1; 1,-1; -1,1; -1,-1; 0,0];
        % flag = 0;
        for i = 1:size(danger_order,1)
            ua = danger_order(i,1); ub = danger_order(i,2);
            if map(ua,ub)~=1 
                continue; 
            end
            mx = 0; cnt = 0;
            for j = 1:size(go,1)
                va = ua+go(j,1); vb = ub+go(j,2);
                if(map_at(map,va,vb)>1)
                    mx = max(mx,danger(va,vb));
                end
            end
            for j = 1:size(go,1)
                va = ua+go(j,1); vb = ub+go(j,2);
                if(map_at(map,va,vb)>1)
                    for k = 1:mx+1-danger(va,vb)
                        cnt = cnt+1; sel(cnt)=j;
                    end
                end
            end
            if(cnt==0) 
                continue;
            end
            target = sel(unidrnd(cnt));
            va = ua+go(target,1); vb = ub+go(target,2);

            map(ua,ub)=2;
            if(map(va,vb)==2)
                map(va,vb)=1;
            end
        end
        map_show(map);
        pause(0.01);
    end
end

function res = map_at(map,a,b)
    % 如果(a,b)在map内,返回map(a,b),否则返回0
    if(a>0&&b>0&&a<=size(map,1)&&b<=size(map,2))
        res = map(a,b);
    else
        res = 0;
    end
end

function map = map_read()
    % 读入地图
    load("./floor_1/map2_2.mat",'A');
    map = A';
%   map = [
%           0, 0, 0, 0, 0, 0
%           0, 2, 2, 1, 2, 0
%           3, 2, 0, 2, 1, 0
%           0, 2, 2, 2, 1, 0
%           0, 0, 0, 0, 0, 0
%   ];
end

function map = add_people(map,rate)
    % 向地图里面加人
    rnd = rand(size(map));
    for i = 1:size(map,1)
        for j = 1:size(map,2)
            if rnd(i,j)<rate && map(i,j)==2
                map(i,j)=1;
            end
        end
    end
end

function [] = map_show(map)
    % 显示地图
    imshow(map,'InitialMagnification','fit','DisplayRange',[0,2]);
    %imagesc(map);
    hold on;
end

function danger = cal_danger(map) 
    % 计算危险度
    danger = -ones(size(map));
    % 危险度影响: 到出口的距离
    for sign = 3:4
        [a,b] = find(map==sign);
        danger = bfs_01(map,danger,[a(1),b(1)]);
    end
    % 危险度影响: 到墙的距离
    go = [0,-1; 0,1; -1,0; 1,0];
    for ua = 1:size(map,1)
        for ub = 1:size(map,2)
            if(map(ua,ub)>=0)
                flag = 0;
                for k=size(go,1)
                    va=ua+go(k,1); vb=ub+go(k,2);
                    if(map_at(map,va,vb)==0)
                        flag=1;
                        break;
                    end
                end
                if(flag)
                    danger(ua,ub)=danger(ua,ub)+0;
                end
            end
        end
    end
end

function danger = bfs_01(map,danger,st)
    % 01bfs计算单种出口影响的复杂度
    n = numel(find(map~=0));
    lef=n; rig=n; que = zeros(n*2,2); % 初始化双端队列
    que(rig,:)=st; rig = rig+1; %push_back
    danger(st(1),st(2)) = 0;
    while(lef<rig)
        ua=que(lef,1); ub=que(lef,2); lef=lef+1; % pop_front
        go=[0,-1; 0,1; -1,0; 1,0];
        for i = 1:size(go,1)
            va=ua+go(i,1); vb=ub+go(i,2);
            if(map_at(map,va,vb)>=1)
                nd = danger(ua,ub)+2;
                if(danger(ua,ub)==0 && map(ua,ub)==map(va,vb))
                    nd = 0;
                end
                if(danger(va,vb)==-1 || danger(va,vb)>nd)
                    danger(va,vb) = nd;
                    if(nd==0)                           
                        lef=lef-1; que(lef,:)=[va,vb];%push_front
                    else
                        que(rig, :)=[va,vb]; rig=rig+1; % push_back
                    end
                end

            end
        end
    end
    
end

function danger_order = cal_danger_oreder(danger)
    % 将危险度进行排序
    n = numel(find(danger~=-1));
    danger_order = zeros(n,3); k = 1;
    for i = 1:size(danger,1)
        for j = 1:size(danger,2)
            if(danger(i,j)~=-1)
                danger_order(k,1) = i;
                danger_order(k,2) = j;
                danger_order(k,3) = danger(i,j);
                k = k + 1;
            end
        end
    end
    [~, pos] = sort(danger_order(:,3));
    danger_order = danger_order(pos,1:2);
end

猜你喜欢

转载自blog.csdn.net/m0_37809890/article/details/86652211