通过将行和列乘以-1,使矩阵中的整数之和最大

时间:2012-12-20 21:53:11

标签: python algorithm numpy python-3.x

在整数上有一个大小为M的矩阵m, n,对它进行转换的好算法是什么,以便所有元素的总和最大?

允许的唯一操作是逐列或逐行乘以-1。可以执行所需数量的此类操作。

粗略,整体的想法:我想到的是将每个减号从一个这样的负数移动到值最小的正数,这样减号对影响最小总和。

我们举个例子:

import numpy as np

M = np.matrix([
        [2,2,2,2],
        [2,2,-2,2],
        [2,2,2,2],
        [2,2,2,1],
    ])

def invert_at(M, n, m):
    M[n,:] *= -1
    M[:,m] *= -1

我通过构建从负元素到最小数字的最短路径之一并且在那里的每个单元格invert_at来尝试这个。

首先包括开始和结束单元格:

invert_at(M, 1, 2)  # start
invert_at(M, 2, 2)
invert_at(M, 3, 2)
invert_at(M, 3, 3)  # end

我最终得到了:

[[ 2  2 -2 -2]
 [-2 -2 -2  2]
 [-2 -2  2  2]
 [ 2  2 -2 -1]]

哪种看起来很有趣。它将负数推到右下角的-1,但也推到其他一些区域。现在,如果我在开始和结束位置(即-1 * -1 = 1)再次反转,那么首先将开始和结束单元格省略,我最终得到:

[[ 2  2  2  2]
 [ 2  2 -2  2]
 [-2 -2 -2 -2]
 [-2 -2 -2 -1]]

看起来更好,考虑到我想要

[[ 2  2  2  2]
 [ 2  2  2  2]
 [ 2  2  2  2]
 [ 2  2  2 -1]]

将“减”推向矩阵的右半部分。

谈到“一半”,我也玩过很多关于使用矩阵分区的想法,但是我无法观察到任何可用的模式。

我尝试过的大多数事情都让我回到原来的矩阵,我们可以观察到的这种“雪崩效应”让我发疯了。

解决这个问题的好方法是什么?

3 个答案:

答案 0 :(得分:2)

n行或m列中的任何一行都可以翻转(-1)或不翻转(1)。

这意味着可能性总数为2 ^(n + m)。这意味着可以在指数时间内找到解决方案。对于小矩阵,您可以使用蛮力,搜索翻转和未翻转列和行的所有可能组合。

然而,您需要等到所有内容都应用完毕,否则您将陷入局部最低限度。

在这种特定情况下,M已经是最大和(27)

import numpy as np

def bit_iter(n):
    for i in xrange(2**(n)):
        bits = []
        for j in xrange(n):
            if i & (2**j) != 0:
                bits.append(1)
            else:
                bits.append(0)
        yield bits

def maximal_sum(M):
    Morig = M.copy()
    n, m = M.shape
    best = None
    for bits in bit_iter(n + m):
        nvec = bits[:n]
        mvec = bits[n:]
        assert(len(nvec) + len(mvec) == len(bits))
        M = Morig.copy()
        for i, v in enumerate(nvec):
            if v == 0:
                M[i, :] *= -1
        for i, v in enumerate(mvec):
            if v == 0:
                M[:, i] *= -1
        if best == None or np.sum(M) > np.sum(best):
            best = M
    return best

M = np.matrix([
    [2,2,2,2],
    [2,2,-2,2],
    [2,2,2,2],
    [2,2,2,1],
])
print maximal_sum(M)
M = np.matrix([
        [1,2],[3,-4]
    ])
print maximal_sum(M)
M = np.matrix([
        [2,-2,2,2],
        [2,2,2,2],
        [2,2,-2,2],
        [2,2,2,2],
        [2,2,2,1],
    ])
print maximal_sum(M)

给出:

[[ 2  2  2  2]
 [ 2  2 -2  2]
 [ 2  2  2  2]
 [ 2  2  2  1]]
[[-1  2]
 [ 3  4]]
[[ 2 -2  2  2]
 [ 2  2  2  2]
 [ 2  2 -2  2]
 [ 2  2  2  2]
 [ 2  2  2  1]]

答案 1 :(得分:1)

问题很可能是NP-hard作为pseudo-Boolean function(PB)优化的一个实例。

您可以使用布尔变量x_i表示“第i行被否定”这一事实,并且使用布尔变量y_j表示事实“第j列被否定”。

然后,每个矩阵元素的“翻转符号”可以描述为

 c(i, j) = 1 - 2*x_i - 2*y_j + 4*x_i*y_j .

因此,给定矩阵M,您的问题要求最大化PB函数

 f = sum A[i,j]*c(i, j) .

众所周知,PB函数的优化是NP难的,所以除非这类功能允许一个聪明的解决方案,否则智能强制(安德鲁的解决方案)似乎还有很长的路要走。

this blog post中非常类似的问题进行了很好的记录。

答案 2 :(得分:0)

我不确定您的问题是否有多项式时间解决方案。我不认为这样做,但我也看不出如何证明它是NP完全的。

可能有希望的一种方法是将其写为(非凸)二次程序:我们想要找到向量v和w,使得-1 <= v <= 1,-1 <= w <1。 = 1,并且v ^ TM w尽可能大。这是一种放松;我不要求v和w只有+/- 1个条目,但它与你的问题具有相同的最佳解决方案。你应该能够找到一个“合理的”凸二次松弛来解决这个问题,然后在它上面构建一个分支定界方案。