使用matlab对二部图进行匹配(匈牙利算法)

使用matlab应用匈牙利算法对二部图进行匹配。
算法的代码主体来自知乎用户洪九(李戈)的专栏文章策略算法工程师之路-图优化算法(一)(二分图&最小费用最大流)

具体算法思想文章里和其它地方都有,不在这里赘述。主要通过例子来说明算法具体运行过程中所作的操作对匈牙利算法进行说明。

首先对于如下图的一个二部图
在这里插入图片描述
左侧有6个点,右侧有7个点,点之间的边都画出来了。

第一步:对 x 1 x_1 x1进行匹配,找到它的第一个可匹配项 y 1 y_1 y1,发现此时 y 1 y_1 y1还未被匹配,因此将 x 1 x_1 x1 y 1 y_1 y1进行配对,完成对 x 1 x_1 x1的处理,进入下一步。
在这里插入图片描述
第二步:对 x 2 x_2 x2进行匹配,找到它的第一个可匹配项 y 2 y_2 y2,发现此时 y 2 y_2 y2还未被匹配,因此将 x 2 x_2 x2 y 2 y_2 y2进行配对,完成对 x 2 x_2 x2的处理,进入下一步。
在这里插入图片描述
第三步:对 x 3 x_3 x3进行匹配,找到它的第一个可匹配项 y 1 y_1 y1,但是现在 y 1 y_1 y1已经被 x 1 x_1 x1给匹配了。
因此询问 x 1 x_1 x1你能不能腾腾地儿,把 y 1 y_1 y1让出来。
x 1 x_1 x1一看自己还可以选 y 2 y_2 y2,但是 y 2 y_2 y2又已经被 x 2 x_2 x2给匹配了。
因此询问 x 2 x_2 x2能不能腾腾地儿,把 y 2 y_2 y2让出来。
x 2 x_2 x2发现自己还能匹配 y 5 y_5 y5,并且 y 5 y_5 y5还未被匹配,不冲突。
于是 x 2 x_2 x2将自己的匹配项从 y 2 y_2 y2改到 y 5 y_5 y5,并告诉 x 1 x_1 x1我已经把 y 2 y_2 y2腾出来了。
x 1 x_1 x1接到 y 2 y_2 y2已经被腾出来的信号后,将自己的匹配项从 y 1 y_1 y1改到 y 2 y_2 y2,并告诉 x 3 x_3 x3我已经把 y 1 y_1 y1腾出来了。
x 3 x_3 x3接到 y 1 y_1 y1已经被腾出来的信号后,直接将 x 3 x_3 x3 y 1 y_1 y1进行配对。完成对 x 3 x_3 x3的处理,进入下一步。
在这里插入图片描述
第四步:对 x 4 x_4 x4进行匹配,找到它的第一个可匹配项 y 3 y_3 y3,发现此时 y 3 y_3 y3还未被匹配,因此将 x 4 x_4 x4 y 3 y_3 y3进行配对,完成对 x 4 x_4 x4的处理,进入下一步。
在这里插入图片描述
第五步:对 x 5 x_5 x5进行匹配,找到它的第一个可匹配项 y 4 y_4 y4,发现此时 y 4 y_4 y4还未被匹配,因此将 x 5 x_5 x5 y 4 y_4 y4进行配对,完成对 x 5 x_5 x5的处理,进入下一步。
在这里插入图片描述
第六步:对 x 6 x_6 x6进行匹配,找到它的第一个可匹配项 y 4 y_4 y4,但是现在 y 4 y_4 y4已经被 x 5 x_5 x5给匹配了。
因此询问 x 5 x_5 x5你能不能腾腾地儿,把 y 4 y_4 y4让出来。
x 5 x_5 x5一看自己只有 y 4 y_4 y4一个可选项,没法让,因此告诉 x 6 x_6 x6我让不出来。
x 6 x_6 x6接到 y 4 y_4 y4已经让不出来的信号后,再检视自身是否有其它可选项,结果是没有,因此 x 6 x_6 x6不进行配对,完成对 x 6 x_6 x6的处理。
在这里插入图片描述
至此所有 x x x点都被处理完毕,算法结束。最大匹配包含 5条匹配边。
在这里插入图片描述

具体代码如下,可以将M和N的值改变,并通过随机数生成M与N之间的连接边进行测试:

clc
clear
close all

global M
global N
global Map
global p
global vis
% M,N分别表示左右侧集合的元素数量
M = 6;
N = 7;
% 邻接矩阵存图
Map = [1 1 0 1 0 0 0;
    0 1 0 0 1 0 0;
    1 0 0 1 0 0 1;
    0 0 1 0 0 1 0;
    0 0 0 1 0 0 0;
    0 0 0 1 0 0 0];
% Map = rand(M, N) > 0.7;

% 记录当前右侧元素所对应的左侧元素
p = zeros(N, 1);
% 记录右侧元素是否已被访问过
vis = zeros(N, 1);

cnt = Hungarian();


function cnt = Hungarian()
global M
global N
global Map
global p
global vis

graph_num = 1;
cnt = 0;
display_graph(graph_num, '原始二部图');
for i = 1: M
    % 重置vis数组
    vis = zeros(N, 1);
    if match(i)
        cnt = cnt + 1;
    end
    graph_num = graph_num + 1;
    display_graph(graph_num, ['第' num2str(i) '步']);
end
graph_num = graph_num + 1;
display_graph(graph_num, '最终结果');
end

function result = match(i)
global M
global N
global Map
global p
global vis
for j = 1: N
    % 有边且未访问
    if Map(i, j) && ~vis(j)
        % 记录状态为访问过
        vis(j) = 1;
        % 如果暂无匹配,或者原来匹配的左侧元素可以找到新的匹配
        if p(j) == 0 || match(p(j))
            % 当前左侧元素成为当前右侧元素的新匹配
            p(j) = i;
            % 返回匹配成功
            result = true;
            return;
        end
    end
end
% 循环结束,仍未找到匹配,返回匹配失败
result = false;
return;
end

function display_graph(graph_num, title_name)
global M
global N
global Map
global p
global vis

max_num = max(M, N);
temp = max_num - 1;
figure(graph_num);
cla
set( gca, 'XTick', [], 'YTick', [] );
set( gca, 'TickLength', [0 0]);
box on
hold on
xlim([-1, 2]);
ylim([-max_num, 1]);
% 绘制边
[row,col] = find(Map);
for j = 1: length(row)
   plot([0, 1], [(row(j) - 1) * -temp/(M-1), (col(j) - 1) * -temp/(N-1)], 'k', 'LineWidth', 1); 
end
% 绘制当前匹配边
for j = 1: length(p)
    if p(j) ~= 0
        plot([0, 1], [(p(j) - 1) * -temp/(M-1), (j - 1) * -temp/(N-1)], 'r', 'LineWidth', 2); 
    end
end
% 绘制点
scatter(zeros(1, M), 0:-temp/(M-1):-temp, 20, [217/255 83/255 25/255], 'filled');
scatter(ones(1, N), 0:-temp/(N-1):-temp, 20, [0 114/255 189/255], 'filled');
for j = 1: M
    text(-0.2, (j - 1) * -temp/(M-1), ['x_' num2str(j)]);
end
for j = 1: N
    text(1.1, (j - 1) * -temp/(N-1), ['y_' num2str(j)]);
end

title(title_name);
end
  • 20
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值