【问题标题】:Complexity of a greedy assignment algorithm贪心分配算法的复杂性
【发布时间】:2016-07-04 12:15:51
【问题描述】:

The assignment problem 要求在给定的 n×n 矩阵的不同行和列中找到一组具有最大可能和的 n 元素。可以通过匈牙利算法在 O(n^3) 中优化求解。但是,让我们考虑以下次优贪心算法:

  1. 选择剩余矩阵中的最大元素
  2. 将此元素添加到结果集中,即将此元素的行与其列匹配,并从矩阵中删除这些行和列
  3. 从步骤 1 开始重复。

实现这种算法的有效数据结构是什么?如果只需要 O(n^2 log(n)) 对所有元素进行排序,它的时间复杂度可以是 O(n^2) 吗?

【问题讨论】:

  • O(n^2) 相当简单,但有什么意义呢?
  • 重点是通过牺牲最优性来降低复杂度,但是如何在 O(n^2) 中做到这一点?
  • 对不起第一次误会你了。您希望您的算法在 O(n^2) 包括 循环(第 3 步)中,对吗?
  • 对,我想找到所有这n个元素的总和可能最大。

标签: algorithm time-complexity mathematical-optimization


【解决方案1】:

O(n2logn) 的复杂度可以使用以下方法实现。

您最初存储 n 行和列中的每一个的排序版本。(此答案稍后将阐明此存储的行和列将使用的确切结构)请注意,即使您将根据单元格,单元格的值应与它们的初始索引一起存储。删除行和列时,初始索引将有所帮助。 (算法的第 2 步)

经过上面提到的预处理,有一个循环迭代n次。在这个循环内将是你的算法的步骤。每次迭代;

第 1 步:检查每个已排序的行和列中的最大元素,更新全局最大元素的值和地址。

步骤 2.1: 将元素添加到生成的数组/列表/等中,并将其从对应行列的排序行列结构中删除。

步骤 2.2: 遍历每个已排序的行结构。对于每一行,我们需要删除一列。由于我们有矩阵和最大元素的地址,我们还可以访问我们为每一行删除的列的确切值和地址。利用这一点,在每个已排序的行结构中,给定它已排序,执行二进制搜索以找到该元素并将其删除。

事情就是这样!如果将此排序后的行结构存储为一个简单的数组,则删除此元素将花费 O(n) 时间,因为您必须将数组的剩余部分向左移动 1。我能想到的解决方案是使用类似于 STL 的set 的结构。实际上,它可以像二分查找一样在 O(logn) 中查找,并提供 O(logn) 时间的插入和删除。

步骤 2.3: 与步骤 2.2 相同,这次遍历每个列结构。当我们删除一行时,我们正在从每一列中删除一个元素。但是,由于找到了地址(即行和列)和全局最大元素的值,我们知道要删除哪一行以及从每一列中删除的元素的值和地址。因此,对于每个列结构,我们从存储该列的排序版本的 STL 的类似集合的数据结构中找到并删除该元素。

性能

预处理:既然我们知道了存储排序的行和列的数据结构类型,我们可以说它需要 O(n2logn) 因为这些结构有 2n 个,我们在 O(logn) 时间内向每个结构插入 n 个元素。

第 1 步:有 2n 个已排序的结构,这意味着在 O(logn) 时间内访问它们的最大元素将具有组合 O(nlogn) 时间复杂度。

步骤 2.1: 虽然这取决于保存结果数据的数据结构,但假设它是一个数组,向其中添加一个元素需要 O(1) 时间。然而,这一步的总体复杂度为 O(logn),因为我们需要从它所属的已排序行和列结构中删除最大元素。

步骤 2.2: 有 n 个已排序的行结构,我们对其执行查找和删除操作,造成 O(nlogn) 负担。

步骤 2.3: 具有 O(nlogn) 成本,与步骤 2.2 相同。

考虑到步骤2.1、2.2、2.3进行了n次,算法的整体复杂度为O(n2logn)

实施

您可能会在下面找到我上面提到的算法的完整实现,它似乎正在运行。该算法的要点是相同的,由于实现的微小变化,例如定义负无穷大或使用额外的布尔向量来存储行或列是否被完全删除。(即我们不会费心查看它的排序结构在剩余矩阵中寻找最大元素时)

#include <set>
#include <vector>
#include <iostream>
#define NEGATIVE_INFINITY -1

using namespace std;

vector< set< pair<int, int> > > sortedRows;
vector< set< pair<int, int> > > sortedCols;
vector< int > result;
void solve(const vector< vector< int > > &matrix)
{
    int n = matrix.size();
    vector<bool> rowNotDeleted(n, true), colNotDeleted(n, true);

    // Preprocessing
    for(int i=0; i < n; ++i)
    {
        sortedRows.resize(n);
        sortedCols.resize(n);
        for(int j=0; j < n; ++j)
        {
            sortedRows[i].insert( make_pair(matrix[i][j], j) );
            sortedCols[j].insert( make_pair(matrix[i][j], i) );
        }
    }

    for(int k=0; k < n; ++k)
    {
        set< pair<int, int> >::reverse_iterator it;

        // STEP 1: Find max. element
        int maxVal = NEGATIVE_INFINITY, maxRow = -1, maxCol = -1;
        for(int i=0; i < n; ++i)
        {
            if(rowNotDeleted[i])
            {
                it = sortedRows[i].rbegin();
                if(it->first > maxVal)
                {
                    maxVal = it->first;
                    maxRow = i;
                    maxCol = it->second;
                }
            }
        }

        for(int i=0; i < n; ++i)
        {
            if(colNotDeleted[i])
            {
                it = sortedCols[i].rbegin();
                if(it->first > maxVal)
                {
                    maxVal = it->first;
                    maxRow = it->second;
                    maxCol = i;
                }
            }
        }

        // STEP 2.1: Add max. element to result.
        result.push_back(maxVal);
        /*
         * Due to my implementation, removing it from
         * relevant sorted data structures can be done
         * in steps 2.2 and 2.3.
         */
//        sortedRows[maxRow].erase( make_pair(maxVal, maxCol) );
//        sortedCols[maxCol].erase( make_pair(maxVal, maxRow) );
        rowNotDeleted[maxRow] = false;
        colNotDeleted[maxCol] = false;

        // STEP 2.2: Remove cells of deleted col from sorted row structures
        for(int i=0; i < n; ++i)
        {
            sortedRows[i].erase( make_pair(matrix[i][maxCol], maxCol) );
        }

        // STEP 2.3: Remove cells of deleted row from sorted col structures
        for(int i=0; i < n; ++i)
        {
            sortedCols[i].erase( make_pair(matrix[maxRow][i], maxRow) );
        }
    }
}

int main()
{
    int n;
    cin >> n;

    // Read matrix
    vector< vector< int > > matrix(n);
    for(int i=0; i < n; ++i)
    {
        matrix[i].resize(n);
        for(int j=0; j < n; ++j)
        {
            cin >> matrix[i][j];
        }
    }

    solve(matrix);

    // Output results
    cout << result[0];
    for(int i=1; i < n; ++i)
    {
        cout << " " << result[i];
    }
    cout << endl;

    return 0;
}

【讨论】:

    【解决方案2】:

    对元素进行排序后,您只需扫描即可,使用两个数组来跟踪已删除的行和列。在未经测试的 Python 中:

    def greedy_max_match(weight):
        n = len(weight)
        row_deleted = [False] * n
        col_deleted = [False] * n
        sorted_weights = sorted(((weight[i][j], i, j)
                                 for i in range(n) for j in range(n)),
                                reverse=True)
        for w, i, j in sorted_weights:
            if not row_deleted[i] and not col_deleted[j]:
                yield (i, j)
                row_deleted[i] = True
                col_deleted[j] = True
    

    【讨论】:

      猜你喜欢
      • 2019-10-15
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      • 1970-01-01
      • 2022-12-07
      • 1970-01-01
      • 2019-10-05
      • 1970-01-01
      相关资源
      最近更新 更多