匈牙利匹配算法 (Hungarian Algorithm) 详解

目录

  1. 算法概述
  2. 核心思想
  3. 基本概念
  4. 算法原理与图解
  5. 详细求解过程
  6. [Python 代码实现](#Python 代码实现)
  7. 应用场景

1. 算法概述

匈牙利算法 (Hungarian Algorithm)是一种用于求解二部图最大权完美匹配(或最小权完美匹配)的组合优化算法。

  • 时间复杂度: O(n³),其中 n 为矩阵维度
  • 核心目标: 在 n×n 的代价矩阵中,为每行选择一个且仅一个元素,使得所选元素之和最小(或最大)

通俗理解

假设有 n 个工人和 n 个任务,每个工人完成每个任务的成本不同。如何安排任务分配,使得总成本最低?这就是匈牙利算法要解决的问题。


2. 核心思想

匈牙利算法基于以下关键定理:

核心定理

如果对代价矩阵的某一行或某一列 的所有元素同时加上(或减去)同一个常数,那么最优匹配方案不变

思路

通过反复对矩阵进行行列变换,最终将矩阵转换为每行每列都含有 0 元素的形式,然后从 0 元素中选出 n 个,使得它们不同行不同列,即为最优匹配。

流程图

复制代码
┌─────────────────────┐
│   输入 n×n 代价矩阵   │
└─────────┬───────────┘
          ▼
┌─────────────────────┐
│ 步骤1: 行规约         │  每行减去该行最小值
└─────────┬───────────┘
          ▼
┌─────────────────────┐
│ 步骤2: 列规约         │  每列减去该列最小值
└─────────┬───────────┘
          ▼
┌─────────────────────┐
│ 步骤3: 用最少的线覆盖 │  尝试用 ≤n 条线覆盖所有0
│ 所有0元素             │
└─────────┬───────────┘
          ▼
     线的数量 == n ?
    ╱              ╲
  是                否
  ▼                  ▼
┌──────────┐   ┌─────────────────┐
│ 从0中选出 │   │ 步骤4: 找未被线   │
│ n个不同行 │   │ 覆盖的最小值,     │
│ 不同列的  │   │ 未覆盖行减该值,   │
│ 元素即为  │   │ 覆盖列加该值,     │
│ 最优匹配  │   │ 返回步骤3         │
└──────────┘   └─────────────────┘

3. 基本概念

3.1 二部图 (Bipartite Graph)

二部图是顶点可以分成两个不相交集合 U 和 V 的图,所有边都连接 U 和 V 中的顶点。

复制代码
工人集合 U              任务集合 V

  工人A ──────────────── 任务1
    │╲                  ╱│
    │ ╲                ╱ │
    │  ╲              ╱  │
  工人B ──────────────── 任务2
    │╲               ╱  │
  工人C ──────────────── 任务3

  每条边表示工人可以完成该任务
  每条边有权重(完成该任务的成本)

3.2 完美匹配 (Perfect Matching)

在二部图中,如果一个匹配覆盖了所有顶点,则称为完美匹配

3.3 代价矩阵 (Cost Matrix)

任务1 任务2 任务3
工人A 4 1 3
工人B 2 0 5
工人C 3 2 2

矩阵中 C[i][j] 表示工人 i 完成任务 j 的代价。

3.4 覆盖线 (Covering Lines)

用最少的行划线列划线来覆盖矩阵中所有的 0 元素。这是判断当前是否可以得到最优解的关键步骤。


4. 算法原理与图解

4.1 为什么行列规约有效?

证明核心定理: 对第 i 行所有元素同时减去常数 k:

复制代码
规约前代价:  C[i][j]
规约后代价:  C[i][j] - k

如果最优匹配方案中选择了元素 C[i][j]:
  规约后总代价 = 原总代价 - k

所有匹配方案的总代价都减少了 k,因此相对大小关系不变!
列规约同理。

4.2 覆盖线数 == 矩阵阶数 的含义

根据 König 定理:在二部图中,最大匹配数 = 最小点覆盖数。

  • 如果最少覆盖线数 = n → 可以从 0 元素中选出 n 个不同行不同列的 → 找到了最优解
  • 如果最少覆盖线数 < n → 尚未找到最优解 → 需要继续迭代

4.3 迭代调整原理

当覆盖线数 < n 时:

复制代码
找到未被覆盖区域的最小值 d

规则:
① 所有未被覆盖的元素 → 减去 d    (产生新的0)
② 所有被两条线覆盖的元素 → 加上 d (保证已有0不消失)
③ 被一条线覆盖的元素 → 不变

这样调整后:
- 不会破坏已有的0(被线覆盖的0仍为0或变为正数被一条线覆盖时)
- 会产生新的0
- 覆盖线数至少增加1

图示:

复制代码
调整前:                    调整后:
  × │ × │ a               ×-d │  ×  │ a-d
───────┼──────            ───────┼───────
  b │ × │ c               b-d  │  ×  │ c-d
───────┼──────            ───────┼───────
  × │ d │ e                ×-d │ d-d │ e-d
                            ↑       ↑
                         新的0   原有的d变为0

× = 被线覆盖的元素    a,b,c,d,e = 未被覆盖的元素

5. 详细求解过程

示例问题

4 个工人完成 4 个任务,代价矩阵如下,求最小总代价的任务分配:

复制代码
原始代价矩阵:
    T1  T2  T3  T4
W1 [ 2,  1,  3,  4 ]
W2 [ 3,  2,  1,  2 ]
W3 [ 5,  4,  3,  1 ]
W4 [ 4,  3,  2,  3 ]

步骤 1: 行规约(每行减去该行最小值)

复制代码
W1 行最小值 = 1 →  [2-1, 1-1, 3-1, 4-1] = [1, 0, 2, 3]
W2 行最小值 = 1 →  [3-1, 2-1, 1-1, 2-1] = [2, 1, 0, 1]
W3 行最小值 = 1 →  [5-1, 4-1, 3-1, 1-1] = [4, 3, 2, 0]
W4 行最小值 = 2 →  [4-2, 3-2, 2-2, 3-2] = [2, 1, 0, 1]

行规约后:
    T1  T2  T3  T4
W1 [ 1,  0,  2,  3 ]
W2 [ 2,  1,  0,  1 ]
W3 [ 4,  3,  2,  0 ]
W4 [ 2,  1,  0,  1 ]

步骤 2: 列规约(每列减去该列最小值)

复制代码
T1 列最小值 = 1 →  [1-1, 2-1, 4-1, 2-1] = [0, 1, 3, 1]
T2 列最小值 = 0 →  [0-0, 1-0, 3-0, 1-0] = [0, 1, 3, 1]
T3 列最小值 = 0 →  [2-0, 0-0, 2-0, 0-0] = [2, 0, 2, 0]
T4 列最小值 = 0 →  [3-0, 1-0, 0-0, 1-0] = [3, 1, 0, 1]

列规约后:
    T1  T2  T3  T4
W1 [ 0,  0,  2,  3 ]    ← 有两个0
W2 [ 1,  1,  0,  1 ]
W3 [ 3,  3,  2,  0 ]
W4 [ 1,  1,  0,  1 ]

步骤 3: 尝试用最少的线覆盖所有 0

复制代码
    T1  T2  T3  T4
W1 [ 0,  0,  2,  3 ]    ← 划线覆盖 W1 行 (覆盖了 T1列0 和 T2列0)
W2 [ 1,  1,  0,  1 ]    ← 划线覆盖 T3 列 (覆盖了 W2, W4)
W3 [ 3,  3,  2,  0 ]    ← 划线覆盖 T4 列 (覆盖了 W3)
W4 [ 1,  1,  0,  1 ]

覆盖线: W1行, T3列, T4列 → 共 3 条线
3 < 4,继续迭代!

步骤 4: 找未被覆盖的最小值,调整矩阵

覆盖线为 W1行、T3列、T4列,逐元素分析覆盖状态:

复制代码
覆盖状态 (c = 被至少一条线覆盖, _ = 未被覆盖):
    T1    T2    T3    T4
W1 [ c,    c,    cc,   cc  ]  ← W1整行被W1行线覆盖
W2 [ _,    _,    c,    c   ]  ← T3列线覆盖W2-T3, T4列线覆盖W2-T4
W3 [ _,    _,    c,    c   ]  ← T3列线覆盖W3-T3, T4列线覆盖W3-T4
W4 [ _,    _,    c,    c   ]  ← T3列线覆盖W4-T3, T4列线覆盖W4-T4

c = 单线覆盖, cc = 双线覆盖, _ = 未覆盖

未被覆盖的元素(只有 _ 标记的位置):

复制代码
W2-T1(1), W2-T2(1),
W3-T1(3), W3-T2(3),
W4-T1(1), W4-T2(1)

最小值 d = 1

注意 : W2-T3、W2-T4、W3-T3、W3-T4、W4-T3、W4-T4 虽然不是 0(W2-T3、W4-T3 除外),但它们已被 T3列线 或 T4列线覆盖,不属于未覆盖区域。

调整规则:

复制代码
- 未覆盖区域: 减 1
- 双线覆盖区域: 加 1
- 单线覆盖区域: 不变

逐元素计算:
  W1: [0, 0, 2+1, 3+1] = [0, 0, 3, 4]
      T1,W1行线覆盖→不变; T2,W1行线覆盖→不变; T3,W1行线+T3列线→加1; T4,W1行线+T4列线→加1
  W2: [1-1, 1-1, 0, 1] = [0, 0, 0, 1]
      T1,T2未覆盖→减1; T3,T3列线覆盖→不变; T4,T4列线覆盖→不变
  W3: [3-1, 3-1, 2, 0] = [2, 2, 2, 0]
      T1,T2未覆盖→减1; T3,T3列线覆盖→不变; T4,T4列线覆盖→不变
  W4: [1-1, 1-1, 0, 1] = [0, 0, 0, 1]
      T1,T2未覆盖→减1; T3,T3列线覆盖→不变; T4,T4列线覆盖→不变

调整后:

复制代码
    T1  T2  T3  T4
W1 [ 0,  0,  3,  4 ]
W2 [ 0,  0,  0,  1 ]
W3 [ 2,  2,  2,  0 ]
W4 [ 0,  0,  0,  1 ]

步骤 5: 再次尝试用最少的线覆盖所有 0

复制代码
调整后矩阵:
    T1  T2  T3  T4
W1 [ 0,  0,  3,  4 ]
W2 [ 0,  0,  0,  1 ]
W3 [ 2,  2,  2,  0 ]
W4 [ 0,  0,  0,  1 ]

0 元素位置:
  W1: T1, T2
  W2: T1, T2, T3
  W3: T4
  W4: T1, T2, T3

划线覆盖过程:
  ① 划 W1 行 → 覆盖了 W1 的全部 0 (T1, T2)
  ② 划 W2 行 → 覆盖了 W2 的全部 0 (T1, T2, T3)
  ③ 划 W4 行 → 覆盖了 W4 的全部 0 (T1, T2, T3)
  ④ 划 T4 列 → 覆盖了 W3-T4 的 0
  检查: 所有 0 都被覆盖了!

    T1  T2  T3  T4
W1 [ 0,  0,  3,  4 ]    ← 划线覆盖 W1 行
W2 [ 0,  0,  0,  1 ]    ← 划线覆盖 W2 行
W3 [ 2,  2,  2,  0 ]    ← 划线覆盖 T4 列
W4 [ 0,  0,  0,  1 ]    ← 划线覆盖 W4 行

覆盖线: W1行, W2行, W4行, T4列 → 共 4 条线
4 == 4,找到了最优解!

步骤 6: 从 0 元素中选出 n 个不同行不同列的

复制代码
0 元素位置:
  W1: T1, T2
  W2: T1, T2, T3
  W3: T4
  W4: T1, T2, T3

不同行不同列的选择:
  W1→T2, W2→T1, W3→T4, W4→T3  → 全部不同行不同列 ✓
  或
  W1→T1, W2→T2, W3→T4, W4→T3  → 全部不同行不同列 ✓

最终结果

复制代码
选择方案: W1→T2, W2→T1, W3→T4, W4→T3

原始矩阵:
    T1  T2  T3  T4
W1 [ 2,  1,  3,  4 ]
W2 [ 3,  2,  1,  2 ]
W3 [ 5,  4,  3,  1 ]
W4 [ 4,  3,  2,  3 ]

最优匹配 (★ 表示选中):
    T1  T2  T3  T4
W1 [ ·,  ★,  ·,  · ]  代价 1
W2 [ ★,  ·,  ·,  · ]  代价 3
W3 [ ·,  ·,  ·,  ★ ]  代价 1
W4 [ ·,  ·,  ★,  · ]  代价 2

最小总代价 = 1 + 3 + 1 + 2 = 7

6. Python 代码实现

python 复制代码
"""
匈牙利算法 (Hungarian Algorithm) 完整实现
用于求解 n×n 代价矩阵的最小权完美匹配问题

  ┌─────────────────────────┬────────────────────────────────┐
  │          函数           │              职责               │
  ├─────────────────────────┼────────────────────────────────┤
  │ _row_reduction          │ 行规约:每行减去最小值         │
  ├─────────────────────────┼────────────────────────────────┤
  │ _col_reduction          │ 列规约:每列减去最小值         │
  ├─────────────────────────┼────────────────────────────────┤
  │ _find_independent_zeros │ 贪心找独立 0 元素,标记为 ★    │
  ├─────────────────────────┼────────────────────────────────┤
  │ _extract_matching       │ 从 ★ 矩阵提取匹配结果          │
  ├─────────────────────────┼────────────────────────────────┤
  │ _is_perfect_matching    │ 判断是否找到完美匹配           │
  ├─────────────────────────┼────────────────────────────────┤
  │ _find_augmenting_path   │ 沿 ★ 和 ○ 交替回溯,找增广路径 │
  ├─────────────────────────┼────────────────────────────────┤
  │ _augment_matching       │ 沿增广路径翻转,★ 数量 +1      │
  ├─────────────────────────┼────────────────────────────────┤
  │ _cover_lines_with_konig │ König 定理构造覆盖线           │
  ├─────────────────────────┼────────────────────────────────┤
  │ _adjust_matrix          │ 找未覆盖最小值,调整矩阵       │
  ├─────────────────────────┼────────────────────────────────┤
  │ hungarian_algorithm     │ 主流程:调用上述函数完成迭代   │
  └─────────────────────────┴────────────────────────────────┘

  三个示例运行结果与之前一致,总代价分别为 7、5、0.60。
"""
import numpy as np


def _row_reduction(cost):
    """行规约:每行减去该行最小值,保证每行至少有一个 0"""
    for i in range(cost.shape[0]):
        cost[i] -= np.min(cost[i])


def _col_reduction(cost):
    """列规约:每列减去该列最小值,保证每列至少有一个 0"""
    for j in range(cost.shape[1]):
        cost[:, j] -= np.min(cost[:, j])


def _find_independent_zeros(cost):
    """
    贪心寻找独立 0 元素,标记为 ★

    返回:
        star_matrix: ★ 标记矩阵,star[i][j]=True 表示位置 (i,j) 被选中
        col_covered: 列覆盖标记,包含 ★ 的列被标记为 True
    """
    n = cost.shape[0]
    star_matrix = np.zeros((n, n), dtype=bool)
    col_covered = np.zeros(n, dtype=bool)

    # 临时行标记,用于贪心选择
    row_marked = np.zeros(n, dtype=bool)
    for i in range(n):
        for j in range(n):
            if cost[i][j] == 0 and not row_marked[i] and not col_covered[j]:
                star_matrix[i][j] = True
                row_marked[i] = True
                col_covered[j] = True

    # 标记包含 ★ 的列
    col_covered[:] = False
    for j in range(n):
        for i in range(n):
            if star_matrix[i][j]:
                col_covered[j] = True

    return star_matrix, col_covered


def _extract_matching(star_matrix):
    """从 ★ 矩阵中提取匹配结果:row_ind[i] = j 表示行 i 匹配列 j"""
    n = star_matrix.shape[0]
    row_ind = [-1] * n
    for i in range(n):
        for j in range(n):
            if star_matrix[i][j]:
                row_ind[i] = j
    return row_ind


def _is_perfect_matching(col_covered):
    """判断是否找到完美匹配:所有列都被 ★ 覆盖"""
    return np.sum(col_covered) == col_covered.shape[0]


def _find_augmenting_path(star_matrix, prime_matrix, start, n):
    """
    从 primed 0 出发,沿 ★ 和 ○ 交替回溯,找到增广路径

    返回:
        path: 增广路径,交替包含 primed 0 和 ★ 的坐标
    """
    row_z, col_z = start
    path = [(row_z, col_z)]
    r, c = row_z, col_z
    while True:
        # 找同一列中的 ★
        for i in range(n):
            if star_matrix[i][c]:
                path.append((i, c))
                r = i
                break
        # 找同一行中的 ○ (primed 0)
        found_prime = False
        for j in range(n):
            if prime_matrix[r][j]:
                path.append((r, j))
                c = j
                found_prime = True
                break
        if not found_prime:
            break
    return path


def _augment_matching(star_matrix, path):
    """
    沿增广路径翻转:primed 0 (○) → ★,★ → 取消标记
    这样 ★ 的数量增加 1
    """
    for idx, (r, c) in enumerate(path):
        if idx % 2 == 0:
            star_matrix[r][c] = True    # ○ → ★
        else:
            star_matrix[r][c] = False   # ★ → 取消


def _cover_lines_with_konig(star_matrix, cost, n):
    """
    使用 König 定理的构造方法,用最少的线覆盖所有 0 元素

    过程:
    1. 找未被覆盖的 0 元素,primed (○) 标记
    2. 如果该行有 ★:标记该行,取消标记 ★ 所在列
    3. 如果该行没有 ★:找到增广路径,翻转后 ★ 数量 +1
    4. 没有未被覆盖的 0 时,覆盖线数已确定

    返回:
        row_covered: 行覆盖标记
        col_covered: 列覆盖标记
        found_perfect: 是否找到了完美匹配
    """
    col_covered = np.zeros(n, dtype=bool)
    # 标记包含 ★ 的列
    for j in range(n):
        for i in range(n):
            if star_matrix[i][j]:
                col_covered[j] = True

    row_covered = np.zeros(n, dtype=bool)
    prime_matrix = np.zeros((n, n), dtype=bool)

    while True:
        # 找一个未被覆盖的 0 元素
        found = False
        row_z, col_z = -1, -1
        for i in range(n):
            if row_covered[i]:
                continue
            for j in range(n):
                if not col_covered[j] and cost[i][j] == 0:
                    row_z, col_z = i, j
                    found = True
                    break
            if found:
                break

        if not found:
            # 没有未被覆盖的 0 了,覆盖线数确定
            return row_covered, col_covered, False

        # primed 该 0 元素
        prime_matrix[row_z][col_z] = True

        # 检查该行是否有 ★
        col_star = -1
        for j in range(n):
            if star_matrix[row_z][j]:
                col_star = j
                break

        if col_star >= 0:
            # 有 ★: 标记该行,取消标记 ★ 所在列
            row_covered[row_z] = True
            col_covered[col_star] = False
        else:
            # 没有 ★: 找到增广路径!
            path = _find_augmenting_path(star_matrix, prime_matrix, (row_z, col_z), n)
            _augment_matching(star_matrix, path)

            # 清除标记,重新计算列覆盖
            prime_matrix[:] = False
            row_covered[:] = False
            col_covered[:] = False
            for j in range(n):
                for i in range(n):
                    if star_matrix[i][j]:
                        col_covered[j] = True

            if _is_perfect_matching(col_covered):
                return row_covered, col_covered, True
            return row_covered, col_covered, False


def _adjust_matrix(cost, row_covered, col_covered):
    """
    调整矩阵:为下一轮迭代准备更多 0 元素

    规则:
    - 未被覆盖的元素: 减 min_val
    - 被两条线覆盖的元素: 加 min_val
    - 被一条线覆盖的元素: 不变
    """
    n = cost.shape[0]

    # 找未被覆盖元素中的最小值
    min_val = float('inf')
    for i in range(n):
        if row_covered[i]:
            continue
        for j in range(n):
            if not col_covered[j] and cost[i][j] < min_val:
                min_val = cost[i][j]

    # 调整矩阵
    for i in range(n):
        for j in range(n):
            if not row_covered[i] and not col_covered[j]:
                cost[i][j] -= min_val      # 未覆盖
            elif row_covered[i] and col_covered[j]:
                cost[i][j] += min_val      # 双线覆盖


def hungarian_algorithm(cost_matrix):
    """
    匈牙利算法求解最小代价匹配

    参数:
        cost_matrix: n×n 的二维列表或 numpy 数组
                     cost_matrix[i][j] 表示工人 i 完成任务 j 的代价

    返回:
        row_ind: 匹配结果中各行对应的列索引
                 row_ind[i] = j 表示行 i 匹配列 j
        total_cost: 最小总代价
    """
    cost = np.array(cost_matrix, dtype=float)
    n = cost.shape[0]

    # 步骤 1-2: 行规约 + 列规约
    _row_reduction(cost)
    _col_reduction(cost)

    # 步骤 3-5: 迭代找最优匹配
    max_iter = n * n
    for _ in range(max_iter):
        # 贪心找独立 0 元素
        star_matrix, col_covered = _find_independent_zeros(cost)

        # 判断是否找到完美匹配
        if _is_perfect_matching(col_covered):
            row_ind = _extract_matching(star_matrix)
            total_cost = sum(cost_matrix[i][row_ind[i]] for i in range(n))
            return row_ind, total_cost

        # 用 König 定理覆盖所有 0
        row_covered, col_covered, found = _cover_lines_with_konig(
            star_matrix, cost, n
        )

        if found:
            row_ind = _extract_matching(star_matrix)
            total_cost = sum(cost_matrix[i][row_ind[i]] for i in range(n))
            return row_ind, total_cost

        # 调整矩阵,增加 0 元素
        _adjust_matrix(cost, row_covered, col_covered)

    raise RuntimeError("匈牙利算法未在最大迭代次数内收敛")


def print_solution(cost_matrix, row_ind):
    """
    打印匹配结果

    参数:
        cost_matrix: 原始代价矩阵
        row_ind: 匹配结果,row_ind[i] = j 表示行 i 匹配列 j
    """
    n = len(row_ind)
    total = 0

    print("\n匹配结果:")
    print("-" * 50)
    for i in range(n):
        j = row_ind[i]
        cost = cost_matrix[i][j]
        total += cost
        print(f"  工人 {i+1} → 任务 {j+1},代价 = {cost}")
    print("-" * 50)
    print(f"  最小总代价 = {total}")

    # 可视化矩阵,★ 标记选中的位置
    print("\n代价矩阵 (★ = 选中):")
    header = "       " + "  ".join(f"T{j+1}" for j in range(n))
    print(header)
    for i in range(n):
        row_str = f"  W{i+1}  ["
        for j in range(n):
            if row_ind[i] == j:
                row_str += f" ★{cost_matrix[i][j]} "
            else:
                row_str += f"  {cost_matrix[i][j]} "
        row_str += "]"
        print(row_str)


# ============================================================
#                        运行示例
# ============================================================
if __name__ == "__main__":

    print("=" * 60)
    print("        匈牙利算法 ------ 最小代价匹配求解")
    print("=" * 60)

    # ---- 示例 1: 4×4 代价矩阵 ----
    cost_matrix_1 = [
        [2, 1, 3, 4],
        [3, 2, 1, 2],
        [5, 4, 3, 1],
        [4, 3, 2, 3]
    ]

    print("\n【示例 1】4×4 代价矩阵:")
    print("  T1  T2  T3  T4")
    for i, row in enumerate(cost_matrix_1):
        print(f"W{i+1} {row}")

    row_ind, total_cost = hungarian_algorithm(cost_matrix_1)
    print_solution(cost_matrix_1, row_ind)

    # ---- 示例 2: 3×3 代价矩阵 ----
    cost_matrix_2 = [
        [4, 1, 3],
        [2, 0, 5],
        [3, 2, 2]
    ]

    print("\n\n【示例 2】3×3 代价矩阵:")
    print("  T1  T2  T3")
    for i, row in enumerate(cost_matrix_2):
        print(f"W{i+1} {row}")

    row_ind, total_cost = hungarian_algorithm(cost_matrix_2)
    print_solution(cost_matrix_2, row_ind)

    # ---- 示例 3: 目标检测中的应用 ----
    # 预测框与真实框之间的 IoU 代价矩阵
    # IoU 越大越好 → 用 1-IoU 作为代价
    print("\n\n【示例 3】目标检测中的 IoU 匹配:")
    print("  预测框与真实框的 IoU 矩阵:")

    iou_matrix = [
        [0.9, 0.2, 0.1],
        [0.1, 0.8, 0.3],
        [0.2, 0.1, 0.7]
    ]

    # 转换为代价矩阵: cost = 1 - IoU
    cost_matrix_3 = [[1 - iou for iou in row] for row in iou_matrix]

    print("  IoU 矩阵:")
    for row in iou_matrix:
        print(f"    {row}")
    print("  代价矩阵 (1-IoU):")
    for row in cost_matrix_3:
        print(f"    {row}")

    row_ind, total_cost = hungarian_algorithm(cost_matrix_3)
    print("\n  匹配结果:")
    for i, j in enumerate(row_ind):
        print(f"    真实框 {i+1} ↔ 预测框 {j+1} (IoU = {iou_matrix[i][j]})")
    print(f"  总代价 = {total_cost:.2f}")
    print(f"  平均 IoU = {(len(row_ind) - total_cost) / len(row_ind):.2f}")
相关推荐
春栀怡铃声2 小时前
常考排序的梳理
数据结构·算法·排序算法
第二只羽毛2 小时前
第六章 图
大数据·数据结构·算法·深度优先·图论·广度优先·宽度优先
csuzhucong3 小时前
puzzle(1052)仙人指路
算法
XiYang-DING3 小时前
【LeetCode】链表 + 快慢指针找中间 + 反转链表 | 2130. 链表最大孪生和
算法·leetcode·链表
Charlie_lll3 小时前
力扣解题-67. 二进制求和
算法·leetcode·职场和发展
Yzzz-F3 小时前
GYM106247B[数论 构造一个数字和因子 使得等于n个因子之和=数字]
算法
CyberMuse3 小时前
欧拉公式(Euler‘s Formula)在信号系统中的应用
算法
吕司4 小时前
LeetCode Hot Code —— 和为K的子数组
数据结构·算法·leetcode
承渊政道4 小时前
【优选算法】(实战剖析链表核心操作技巧)
开发语言·数据结构·c++·vscode·学习·算法·链表