通过在python中排列元素来最小化矩阵中的列总和

时间:2017-07-07 13:22:40

标签: python algorithm matrix

我有以下矩阵:

([2, 5, 5, 10]
 [7, 1, 4, 1]
 [1, 3, 3, 9])

如果对列进行求和,则结果为:

[10, 9, 12, 20]

我的目标是确定对不同行中的元素进行排序的最佳方法,以便最小化列总和中的最大元素。

例如,一种可能性是:

([2, 5, 5, 10]
 [7, 1, 4, 1]
 [1, 9, 3, 3])

如果对列进行求和,则结果为:

[10, 15, 12, 14]

这是比第一个更好的解决方案。

最简单的方法是检查所有可能的排列,但随着矩阵的增长,这种方法在python中变得非常慢。

有任何想法以更快的方式做到这一点吗?

3 个答案:

答案 0 :(得分:5)

这是一个想法:

  1. 选择具有最小和最大总和的2列。请注意他们的区别, d
  2. 检查两列中的元素。找到差值绝对值 d' 的行,以便 d' < d d' > 0.
  3. 交换该行中的元素。
  4. 重复步骤1-3,直到第2步不再可能。
  5. 示例: 给定

    ([2, 5, 5, 10]
     [7, 1, 4, 1]
     [1, 3, 3, 9])
    

    我们挑选最小和最大总和的2列。这里我们有第1列最小和第3列,最大总和。 对于这两列,它们的总和 d 之差为11。

    ([5, 10]
     [1, 1]
     [3, 9])
    

    现在我们找到了最大的差异 d' ,以便 d' < d d' > 0,即9 - 3 = 6。 我们现在交换该行中的元素。所以我们有

    ([2, 5, 5, 10]
     [7, 1, 4, 1]
     [1, 9, 3, 3])
    

    此矩阵的列总和为[10, 15, 12, 14]

    再一次重复上述过程,然后您将得到以下结果:

    ([5, 2, 5, 10]
     [7, 1, 4, 1]
     [1, 9, 3, 3])
    

    此结果矩阵的总和为[13, 12, 12, 14]。此时,步骤2不再可能。所以我们完成了。

答案 1 :(得分:2)

首先让你强化你的要求

"Can I produce a matrix that minimizes the difference between the max sum and the min sum of each column in my matrix" 

这很好,因为:

  1. 它将满足您的原始要求,因此解决此问题将解决您的问题
  2. 有了这个要求,很容易在每次迭代中显示次优性,这样我们就可以说服自己贪婪的方法会起作用。
  3. 要实现贪婪的解决方案,只需保持垫子的运行总和,并为每一行将当前行中的最低值插入最高总和列。这可确保色谱柱尽可能均匀堆积。

    这会为每行m行和n行添加2mlogm个插页,因此应该O(n*m + n*2*mlogm) O(nmlogm)运行。

    output_mat = []
    
    input_mat = [
         [2, 5, 5, 10],
         [7, 1, 4, 1],
         [1, 3, 3, 9],
    ]
    
    row_size = len(input_mat[0])
    running_sum = [0] * row_size
    
    for row in input_mat:
        sorted_idx = [
            x[0] for x in 
            sorted(enumerate(row), key=lambda x: x[1])
        ]
    
        sum_sorted_idx = [
             x[0] for x in 
             sorted(enumerate(running_sum), key=lambda x: x[1], reverse=True)
        ]
    
        new_val_row = [None] * row_size
        for col_idx,val_idx in zip(sum_sorted_idx, sorted_idx):
            new_val_row[col_idx] = row[val_idx]
            running_sum[col_idx] += row[val_idx]
    
        output_mat.append(new_val_row)
    
    for x in output_mat:
        print ">> %s" % x
    print(running_sum)
    

    输出:

    >> [2, 5, 5, 10]
    >> [7, 1, 4, 1]
    >> [3, 9, 3, 1]
    [12, 15, 12, 12]
    

答案 2 :(得分:1)

您希望自己的最优水平如何?

据我所知,您所陈述的问题是NP-complete。为了在多项式时间内找到次优解,存在几种试探法。例如,在Coffman和Yannakakis的论文《在矩阵的列内排列元素以最小化最大行和》中,他们提出了一种算法,其算法的复杂度为O(m ^ 2 n)(其中矩阵定义为mxn),该算法可以实现在最坏的情况下,性能比最佳性能大1.5-(0.5 / m)倍。请注意,它们的问题与您的问题相同(只需对矩阵进行转置)。由于论文是付费的,所以我不确定是否可以在这里复制他们的算法,但是请看一下您是否有兴趣(大多数大学将在学生自己的在线图书馆中免费提供它供学生使用)。

还存在其他算法(例如,来自Hsu的装配线机组调度问题的近似算法),也许还有一些我不熟悉的最新文献。

我想指出的是,文献中的解决方案在您的示例中给出了相当糟糕的结果,因此,只有在最坏的情况下寻找数学保证时,它才会变得有趣。

这里的重点是:如果您的目标是最小化列总数中的最大元素,那么问题有任何想法以更快的方式做到这一点的答案吗?< / strong>是:没有。问题是NP完全。最好的办法是尝试所有可能的方法,并希望您能及早找到最佳解决方案。

也就是说,这是在可以接受算法的复杂程度与在最坏情况下可以接受的最优程度之间的权衡。

有关该问题的一些见解: 通常,虽然输出显然不是最佳的,但很容易找到其中一对元素的排列不能改善目标的示例。例如,考虑矩阵

 2     1     0
 0     1     2
 2     1     0

最差的和是在第一列,但是,如果将2s中的任何一个与任何其他元素一起置换,则总和根本不会提高。例如,gbtimmon中的算法被卡在此矩阵中并返回[2,3,4]之和。 qwerty's answer也是如此。但是,一个简单的最佳解决方案是

 1     2     0
 0     1     2
 2     0     1

,但是为了从原始矩阵转换为最佳矩阵,您必须必须执行一开始不会改善的排列,例如,您可以从排列位置(1、2)和(1开始, 3)。

一个主意

我无法保证性能的一种可能性是尝试一种树状算法。将损失/成本/目标函数定义为列和起始矩阵A的总和的最大值。从矩阵A中考虑所有成对排列,从而导致或相等成本降低。例如,从

开始
 2     1     0
 0     1     2
 2     1     0

我们考虑所有导致成本小于或等于4的成对排列。在这种情况下,排除的唯一排列是:

 2     1     0
 2     1     0
 2     1     0

 2     1     0
 1     0     2
 2     1     0

和所有其他排列的成本均为4。

然后,我们考虑刚刚得到的所有矩阵的所有成对排列。如果您的矩阵是m x n,那么对于每个矩阵,都有n * n-choose-k(m,2)个成对排列。然后,我们从该树中切出所有具有最小成本的矩阵。在这种情况下,所有其他矩阵的成本均为4,因此我们不会削减任何成本。然后,我们对每个矩阵执行相同的操作。在我的示例中,这导致了最优解,在您的示例中,它给出了两个相等的最优解,即

 5    10     5     2
 7     1     4     1
 1     3     3     9

 5     2     5    10
 7     1     4     1
 1     9     3     3

显然是相同的解决方案,但要减去列的排列。两者的总和为{12,12,13,14}(未按列排序)。但是请注意,随着矩阵大小的增加,该算法 将会变得复杂,尤其是在有许多解决方案提供相同成本的情况下。如果两个成对的排列达到相同的成本,也将有很多冗余。尽管如此,它应该比尝试所有可能性更快,因为我们排除了“不良”可能性。

很抱歉没有在python中实现它来呈现整个代码,但是我认为该算法非常清晰和易于读者实现。 :)