匈牙利算法的MATLAB实现

匈牙利算法的MATLAB实现

首先是CSDN上这篇文章很清晰的讲解了匈牙利算法的思路。顺着思路本弱鸡也尝试动手写了一下。
分派问题(匈牙利算法)与MATLAB实现
匈牙利算法的matlab实现

主程序

clc;
clear;
% 主程序:基于匈牙利算法
% 输入的矩阵是前面计算好的COST(calculating_price.m)
% 需要对矩阵进行处理,基站列需要扩充,基站最多要满足M个CR用户的请求;行也需要调整。总之就是要将矩阵处理成方阵

%% 矩阵处理 - 处理成符合意义的方阵
% load('COST.mat');
COST = [1 4 3;2 inf 1;3 6 9];
[x,y] = size(COST);
COST_P = COST;
if x ~= y
    % 赋值BS列
    BS_column = repmat(COST(:,y),1,x-1);
    COST_P = [COST_P BS_column];
    % 矩阵补零变成方阵
    zero = zeros(1,x+y-1);
    Add_zero = repmat(zero,y-1,1);
    COST_P = [COST_P;Add_zero];
end% 处理之后的COST_P矩阵应该是一个方阵
COST_expand = COST_P;
[m,n] = size(COST_P);

%% 处理矩阵 - 匈牙利算法
COST_P = rc(COST_P);
%% 计算覆盖的行数和列数
[line_sum,row_index1,row_sum1,column_index,column_sum,row_index2,row_sum2,matrix_out] = line_count(COST_P);
%% 处理划线的数量小于矩阵的维数
while (line_sum < m)
    rest_min = min(min(matrix_out)); % 找到没有被覆盖的最小值,将没有被覆盖的每行减去最小值,被覆盖的每列加上最小值。转到上一步。
    row_num = 1:1:m;
    row_index = sort([row_index1 row_index2]);
    row_num(row_index) = [];% 找到没有被覆盖的行
    COST_P(row_num,:) = COST_P(row_num,:) - rest_min;% 没有被覆盖的行减去最小值
    COST_P(:,column_index) = COST_P(:,column_index) + rest_min;% 被覆盖的列加上最小值
    [line_sum,row_index1,row_sum1,column_index,column_sum,row_index2,row_sum2,matrix_out] = line_count(COST_P);
end
%% 输出匹配结果
[asig] = fenpei(COST_P);
if x ~= y
    del_index =x+1:1:x+y-1;
    asig(del_index) = []
end
%% 计算COST
cost_sum = 0;
n = length(asig);
for i = 1:n
    cost_sum = cost_sum + COST_expand(i,asig(i));
end
cost_sum

行处理和列处理

行处理和列处理相对来说比较简单

function [pro_rc] = rc(matrix)
% 匈牙利算法中处理行和处理列,rc表示row和column
% 算法的第一步行减去当前行的最小值以及列减去当前列的最小值
% 输入参数是填入待处理的矩阵;输出参数是处理之后的矩阵
m = length(matrix);
for i=1:m
    rowi_min = min(matrix(i,:));
    matrix(i,:) = matrix(i,:)-rowi_min;
end
for i=1:m
    columni_min = min(matrix(:,i));
    matrix(:,i) = matrix(:,i)-columni_min;
end
pro_rc = matrix;
end

进一步处理矩阵——划线(即匈牙利算法中覆盖所有0的线数量等于矩阵的维度,说明处理好了)

划线的处理思路是,(1)先看行,如果这个行中的0的数量大于1,则可以选择划线,line_count可以+1,并且删除掉这一行。(2)所有行中0数量大于1的处理完之后,删除掉这些行形成新的矩阵,用同样的思路处理列,在形成新的矩阵。(3)这样新的矩阵中就只剩下一种情况,每行中只有一个0(每列中只有一个0),用行算就行。

function [line_sum,row_index,row_sum,column_index,column_sum,row_index2,row_sum2,matrix_out] = line_count(matrix)
% 对处理过的矩阵进行划线统计
% 在匈牙利算法中对行和列进行划线的时候,是可以先从行开始计算,先检索有不少于两个0的行,有的话就划掉,这一个行就直接从
% 矩阵中删除掉形成新的矩阵,接着循环;列也是这样。
% 等到矩阵中都没有不少于两个0的行和列的时候,就直接以行为准,划零,统计行数;
% line_sum是划线总数,row_sum是统计行划了几条线,column_sum是统计列划了几条线,matrix_out是看最后画完线的矩阵
[m,n] = size(matrix); %统计矩阵的维度m*n
line_sum = 0;%存放划0的数量

%% 处理行
row_index = [];
j = 1;
for i=1:m
    a = length(find(matrix(i,:)==0));
    if a>1
        line_sum = line_sum + 1;
        row_index(j) = i;
        j = j + 1;
    end
end
row_sum = length(row_index);
matrix(row_index,:) =[]; %将输入矩阵删除掉行中含不少于2个0的行

%% 处理列
column_index = [];
k = 1;
for i=1:n
    b = length(find(matrix(:,i)==0));
    if b>1
        line_sum = line_sum +1;
        column_index(k) = i;
        k = k + 1;
    end
end
column_sum = length(column_index);
matrix(:,column_index) = [];
        
%% 处理只有一个0
[m,n] = size(matrix);%上一步处理完的矩阵有可能不是方阵
row_index2 = [];
j = 1;
for i=1:m
    a = length(find(matrix(i,:)==0));
    if a == 1
        line_sum = line_sum +1;
        row_index2(j) = i;
        j = j+1;
    end
end
row_sum2 = length(row_index2);
matrix(row_index2,:) = [];
matrix_out = matrix;
end   

分配

分配的思路是,行或列只有1个0的必然首先分配,并且这个0所在的行和列要不能出现0,所以直接行和列变成inf,原本想删除掉,发现有问题。最后可能会出现[0 0 ;0 0 ]这种情况,所以判断了多个0的时候直接取第一个。缺点就是没有办法显示多组解。

function [asig] = fenpei(matrix)
% 函数用于最后的分配
% 函数输入是最后的0划线数量等于矩阵的维度的最终被处理过的矩阵
% 函数的输出是最后的分配方案,[2 3 1 4]表示User1被分配到Task2,依次类推
[m,n] = size(matrix);
asig = [];% 存放决策的矩阵,[4 2 3 1]表示第一个用户被分配到任务4,以此类推
p = 1;
q = 1;
temp = length(find(matrix==0));
while (temp~=0)
    %% 处理1个0
    for i = 1:m
        zero = length(find(matrix(i,:)==0));
        if zero == 1
            row_0_1(p) = i;% row_0_1表示只有1个0的行
            column_row_0_1(p) = find(matrix(i,:)==0);% column_row_0_1表示只与一个0的行对应的列
            asig(i) = find(matrix(i,:)==0);% 用户i被分配任务
            p = p+1;
        end
    end
    
    for j = 1:n
        zero = length(find(matrix(:,j)==0));
        if zero == 1
            column_0_1(q) = j;
            row_column_0_1(q) = find(matrix(:,j)==0);
            asig(row_column_0_1(q)) = j;% 任务可被分配
            q = q+1;
        end
    end
    row_delete = [row_0_1 row_column_0_1];
    column_delete = [column_row_0_1 column_0_1];
    matrix(row_delete,:) = matrix(row_delete,:)+inf;
    matrix(:,column_delete) = matrix(:,column_delete)+inf;
    
    %% 处理多余1个0
    for i = 1:m
        zero = find(matrix(i,:)==0);
        if length(zero) ~= 0
            asig(i) = zero(1);
            matrix(i,:) = matrix(i,:) + inf;
            matrix(:,zero(1)) = matrix(:,zero(1)) + inf;
        end
    end
    
    temp = length(find(matrix==0));
end
end

~~ 代码能力比较弱,整体代码显得比较臃肿 后面还想着继续弄一下,反正现在跑应该没啥问题了吧。~~

你可能感兴趣的:(matlab,匈牙利算法,MATLAB,一对一匹配,Matching)