【问题标题】:Hungarian Algorithm - Wikipedia method doesn't work for this example匈牙利算法 - 维基百科方法不适用于此示例
【发布时间】:2017-10-18 05:51:49
【问题描述】:

我正在尝试用 C 语言实现匈牙利算法。

我有矩阵:

35  0  0  0
 0 30  0  5
55  5  0 10
 0 45 30 45

我已经到了必须找到覆盖所有零的最少行数的阶段(进行尽可能多的分配)。显然,通过检查,这是第 1 列和第 3 列以及第 1 行。

Wikipedia suggests the following method:

  • 第 1 行有三个零:选择任何一个(我选择第一个)并分配它
  • 第 2 行:分配第一个零
  • 第 3 行:分配第三个零
  • 第 4 行未分配(因为唯一的零位于已分配零的列中)

如果我在上面的矩阵中遵循这个,我会得到:

35   0'  0   0
 0' 30   0   5
55   5   0' 10
 0  45  30  45

其中零素数是分配的零。然后,按照下面 Wikipedia 的说明,我标记第 4 行(未分配的零)、第 1 列(未分配零的列),然后是第 2 行(标记的列中包含零的行)。

所以这表明达到全零的最小行是:

+--------
|
+--------
|

但这并没有达到(2, 3) 的零。相关C代码:

for (i = 0; i < M->size; i++) {
    for (j = 0; j < M->size; j++) {
        if (M->values[i][j] == 0) {
            if (assigned_cols[j] == 0) {
                assigned_cols[j] = 1; // We've assigned something in this col
                assigned_rows[i] = 1; // We've assigned something in this row.
                marked_rows[i] = 0;
                total--;
                break; // Go to the next row
            } else {
                marked_cols[j] = 1; // Then there exists a zero in this col in an unassigned row
                mark_col(M, j); // marks all elements in column j
                total++;
            }
        }
    }
}

此代码选择哪些零是零素数(分配零)。

然后这段代码在新标记的列中标记所有具有赋值的行:

 for (i = 0; i < M->size; i++) {
    if (marked_cols[i] == 1) {
        for (j = 0; j < M->size; j++) {
        //iterating through rows
            if (M->values[j][i] == 0) {
                // then (j,i) is a zero in a marked col
                // mark the row
                if (marked_rows[j] != 1) {
                    total++;
                    marked_rows[j] = 1;
                }
                break; // no need to continue more
            }
        }
    }
}

但是对于我上面的矩阵,这个(以及维基百科的解释)失败了。怎么会?

【问题讨论】:

标签: c algorithm hungarian-algorithm


【解决方案1】:

维基百科对算法缺乏解释,作业将在最后一步完成!

步骤 0

35  0  0  0
 0 30  0  5
55  5  0 10
 0 45 30 45

步骤 1-2 所有行列都至少有一个 0,因此步骤 1 使数组保持不变

35  0  0  0
 0 30  0  5
55  5  0 10
 0 45 30 45

第 3 步
矩阵中的所有零必须通过标记尽可能少的行和/或列来覆盖

- - - -
|   |
|   |
|   |

请注意,到目前为止还没有完成任何作业,您需要覆盖all zeros。你的封面没有覆盖零(2,3)!

现在取未覆盖的 min 元素,例如 5(在位置 (2,4) 取 5)

-减少(减少 5)所有未覆盖的元素。
- 将所有被两条线交叉的元素增加(5 倍)。
-其余保持不变
所以数组:

40  0  5  0
 0 25  0  0
55  0  0  5
 0 40 30 40

现在再次检查所需的最少行数:现在您需要 4 行(等于数组行的大小 n=4,所以我们停止)。

最后分配: 从只有一个零的行开始,这肯定会被分配:

40  0  5  _
 0 25  _  0
55  _  0  5
 _ 40 30 40

存在多个赋值(我使用 _ 进行赋值)。

更具体地说,我们得到了两项任务:(上述一项,总成本为 5)和:

40  _  5  0
 0 25  0  _
55  0  _  5
 _ 40 30 40

同样花费 5!


编辑

根据评论,我似乎没有得到 op 要求的确切部分,所以我将回答这个特定部分,保留上述算法的一般描述。

错误(由于错误的维基百科描述)在这里:

其中零素数是分配的零。然后,按照维基百科的 下面的说明,我标记第 4 行(未分配的零),第 1 列(col 与 未分配的零),然后是第 2 行(标记列中有零的行)。

到现在为止完全同意,但是……还不完整!!!

正确标记第 2 行时,您需要转到第 2 步(维基百科的) 并在这种情况下再次检查列为零的列,第 3 列应该 也被标记,这也会导致第 3 行也被标记(由于在新标记的第 3 列中分配了零)和 你停在那里(不应标记其他行或列)!!

所以总体上标记的列和行:

 +       +
35   0'  0   0
 0' 30   0   5  +
55   5   0' 10  +
 0  45  30  45  +

以及通过选择标记的列和未标记的行获得的行:

- - - -
|   |
|   |
|   |

这是答案第一部分中描述的正确结果,并导致下一阶段的正确结果(也在上面解释过)。

可以在 ma​​thstackexchange 上找到一篇非常相似的帖子,其中陈述字面意思相同:

finding the minimum number of lines to cover all zeros in an assignment probem

【讨论】:

  • 抱歉,这不是我想要的。我问的是“第 3 步”,即如何您发现这些是覆盖所有零的三行。我知道 (2,3) 没有被覆盖 - 这是我的错误:P
  • 1 1 0 1 0 // 1 1 0 1 0 // 1 1 0 1 0 // 0 1 1 1 0 // 1 1 1 1 1 ,算法不工作
猜你喜欢
  • 1970-01-01
  • 2011-05-03
  • 1970-01-01
  • 2010-11-16
  • 1970-01-01
  • 2012-05-21
  • 2012-09-09
  • 2017-02-12
  • 2021-12-14
相关资源
最近更新 更多