查找具有已知行/列和和最大像元值的矩阵的可能解

时间:2019-03-21 08:57:18

标签: arrays algorithm linear-programming subset-sum

我正在尝试找到矩阵的解,在该矩阵中我知道行和列的总和以及一个单元格可以具有的最大值。我想找到在约束范围内的可能解决方案。我已经尝试过各种方法,例如构造一个包含所有单元格值的数组,并从每个单元格中依次选择,但是无论如何,我总是遇到一个问题,即我用完了一个单元格的值。 我还尝试了一种递归算法,但是我只能设法获得第一个结果,否则无法获得任何解决方案。我想我必须使用回溯算法来做到这一点?不确定...

任何帮助或指点将不胜感激。

行总和A,B,C,列总和X,Y,Z以及每个?的最大值众所周知。所有值都是正整数。

    C1 | C2 | C3 
-----------------
R1 | ? | ?  | ? | A
-----------------
R2 | ? | ?  | ? | B
-----------------
R3 | ? | ?  | ? | C
-----------------
     X | Y | Z

2 个答案:

答案 0 :(得分:4)

如果您听说过linear programming(LP)及其“堂兄弟”(ILP,MILP),那可能是一个帮助您高效解决问题的好方法。
线性程序由一组变量(矩阵未知数),约束(最大值,行和列之和)和一个目标函数(此处没有)组成,以最小化或最大化。

我们将x [i] [j]称为您要查找的值。 带有以下数据:

NxM矩阵的尺寸
max_val[i][j]变量x[i][j]的最大值
row_val[i]i上的值之和
col_val[j]j上的值之和

那么可以解决您的问题的线性程序是:

// declare variables
int x[N][M] // or eventually float x[N][M] 
// declare constaints
for all i in 1 .. N, j in 1 .. M, x[i][j] <= max_val[i][j]
for all i in 1 .. N, sum[j in 1 .. M](x[i][j]) == row_val[i]
for all j in 1 .. M, sum[i in 1 .. N](x[i][j]) == col_val[j]
// here the objective function is useless, but you still will need one
// for instance, let's minimize the sum of all variables (which is constant, but as I said, the objective function does not have to be useful)
minimize sum[i in 1 .. N](sum[j in 1 .. M](x[i][j]))
// you could also be more explicit about the uselessness of the objective function
// minimize 0

诸如gurobiCplex之类的解决方案(但是有更多的解决方案,例如参见here)可以非常快地解决这类问题,特别是如果您的解决方案不能解决的话需要为整数,但可以为浮点数(这使问题容易得多)。它还具有不仅执行速度更快,而且编码更快,更简单的优点。它们具有几种常用编程语言的API,以简化其使用。
例如,您可以合理地期望在不到一分钟的时间内解决此类问题,整数情况下有数十万个变量,实数情况下有数百万个。

编辑: 为了回应评论,这是OPL中的一段代码(Cplex和其他LP解算器使用的语言),可以解决您的问题。我们考虑一个3x3的案例。

// declare your problem input
int row_val[1..3] = [7, 11, 8];
int col_val[1..3] = [14, 6, 6];
int max_val[1..3][1..3] = [[10, 10, 10], [10, 10, 10], [10, 10, 10]];

// declare your decision variables
dvar int x[1..3][1..3];

// objective function
minimize 0;

// constraints
subject to {
    forall(i in 1..3, j in 1..3) x[i][j] <= max_val[i][j];
    forall(i in 1..3) sum(j in 1..3) x[i][j] == row_val[i];
    forall(j in 1..3) sum(i in 1..3) x[i][j] == col_val[j];
}

LP解算器的概念是,您仅描述要解决的问题,然后解算器将为您解决问题。必须根据一组特定的规则来描述问题。在当前情况下(整数线性编程或ILP),变量必须全部为整数,并且对于决策变量,约束和目标函数必须为线性等式(或不等式)。
然后求解器将充当黑匣子。它将分析问题,并运行可以解决问题的算法,并进行大量优化,然后输出解决方案。

答案 1 :(得分:2)

正如您在评论中所写的那样,您想要一个自己的解决方案,这是一些准则:

使用Backtrack algorithm查找解决方案。您的值空间由3 * 3 = 9个独立值组成,每个值都在1maxval[i][j]之间。您的约束条件将是行和列的总和(所有条件都必须匹配)

使用所有1来初始化您的空间,然后递增它们,直到它们到达maxval。仅在覆盖该条件的每个值之后评估条件(特别是,在3个值之后,您可以评估第一行,在6行第二行之后,在7行第一列之后,在8行第二列之后,在9行之后第三列,然后第三栏)

如果在所有条件都通过的情况下达到第9位,那么您将找到解决方案。否则,请尝试从1maxval的值,如果都不匹配,请后退。如果第一个值被迭代,则没有解决方案。

仅此而已。


更多高级回溯:

您的移动值只是左上角的2 * 2 = 4个值。计算第三列,条件是该列必须在1maxval之间。 定义[1][1]元素后,您需要使用列总和来计算[2][2]索引,并通过行总和来验证其值(反之亦然)。相同的处理规则适用于上述情况:遍历所有可能的值,如果没有匹配,则退后一步,仅在可以应用规则时检查规则。

这是一种更快的方法,因为您有5个绑定变量(底部和右侧行),并且只有4个未绑定。这些是您特定规则的优化。但是,实现起来有点复杂。

PS:使用1是因为您有正整数。如果您使用非负整数,则需要以0开头。