确定是不是可以通过翻转 1 矩阵的行和列来达到给定二进制矩阵的算法

Posted

技术标签:

【中文标题】确定是不是可以通过翻转 1 矩阵的行和列来达到给定二进制矩阵的算法【英文标题】:Algorithm that determines if a given binary matrix can be reached by flipping rows and columns of a matrix of ones确定是否可以通过翻转 1 矩阵的行和列来达到给定二进制矩阵的算法 【发布时间】:2018-02-27 12:11:23 【问题描述】:

我需要帮助找到一种算法,该算法尽可能有效地检查是否可以通过翻转矩阵的行和列来达到给定的二进制矩阵。 每当你翻转一行或一列时,所有的 0 都变成 1,所有的 1 都变成 0

确定是否可以通过翻转一个矩阵的行和列来达到给定二进制矩阵的算法

例如,可以通过翻转第二行然后翻转第二列来达到这个矩阵:

+---+---+---+
| 1 | 0 | 1 |
+---+---+---+
| 0 | 1 | 0 |
+---+---+---+
| 1 | 0 | 1 |
+---+---+---+

但是这个矩阵不可​​能是你做的任何翻转

+---+---+---+
| 1 | 0 | 0 |
+---+---+---+
| 0 | 1 | 0 |
+---+---+---+
| 0 | 0 | 1 |
+---+---+---+

【问题讨论】:

矩阵的最大尺寸是多少? 你能举例说明一下翻转到底是做什么的吗? 这似乎是一个难题,需要进行一些测试。你有没有写过一个蛮力算法来用简单的例子来试试这个?可能有一个数学解决方案(即通过计算行列式),但您需要能够找到工作矩阵和非工作矩阵之间的区别。如果这不可能,那你就迷路了,因为这是一个终止问题,本质上是无法解决的。 @trincot:翻转将行或列上的所有 0 更改为 1,反之亦然。第一个示例在翻转中间行和中间列后给出全 1。 【参考方案1】:

这可以测试如下:

取目标矩阵的第一列。所有其他列应该与第一列相同,或者应该相反(翻转)。如果且仅当情况如此,则目标矩阵可以通过从具有所有 1 值的初始矩阵的行/列翻转来达到。

当然,您也可以使用行进行测试,但是使用行或列来执行测试就足够了。

要进行哪些翻转?

如果上述测试是肯定的,您还可以查看可以执行哪些翻转以到达目标矩阵:

在目标矩阵的第一行,识别值为 0 的单元格:这些是您需要在初始矩阵中翻转的列。

在目标矩阵的第一列中,识别具有 与目标矩阵左上角的值不同的值的单元格(因此这已经排除了第一个值):那些是您需要在初始矩阵中翻转的行。

翻转的顺序并不重要。显然,这只给出了一种解决方案。一般情况下可以有不止一个。

实施

这是一个简单的 javascript sn-p,它执行验证并提供一个列和行列表以供交换(如果可能):

function getFlips(matrix) 
    // Verification
    for (let i = 1; i < matrix.length; i++) 
        let flip = matrix[i][0] ^ matrix[0][0]; // XOR operation
        for (let j = 0; j < matrix[0].length; j++) 
            if (matrix[i][j] ^ flip != matrix[0][j]) return false; // Not possible
        
    
    // If we get here, it is possible: determine which rows/columns to flip
    let flips =  rows: [], columns: [] ;
    for (let j = 0; j < matrix[0].length; j++) 
        if (matrix[0][j] == 0) flips.columns.push(j+1);
    
    for (let i = 1; i < matrix.length; i++) 
        if (matrix[i][0] != matrix[0][0]) flips.rows.push(i+1);
    
    return flips;



// I/O management
inp.oninput = function () 
    // Convert input to matrix of numbers
    let matrix = inp.value.split('\n').map(row => Array.from(row, Number));
    // Perform algorithm
    let flips = getFlips(matrix);
    // Output the result in human readable format
    out.textContent = flips 
        ? 'Number(s) of the column(s) to flip: ' 
            + (flips.columns.length ? flips.columns : 'none') + '\n' +
          'Number(s) of the row(s) to flip: ' 
            + (flips.rows.length ? flips.rows : 'none')
        : 'Not possible';
;

inp.oninput();
Enter the values of the matrix:<br>
<textarea id="inp" rows="4" cols="4">101
010
101</textarea><br>
Solution:
<pre id="out"></pre>

【讨论】:

【参考方案2】:

考虑到我们有一个 N × N(方)矩阵,我们可以使用动态规划在 N 步内到达目标矩阵。

假设我们已经通过对初始矩阵的一些翻转来匹配左上 K 乘 K 子矩阵。然后,通过翻转第 K+1 行或/和 K+1 列,我们可以将子矩阵 K+1 与 K+1 匹配。在每个步骤中,我们可以有 4 个选项之一:

翻转第 K+1 行,翻转第 K+1 列 翻转第 K+1 行,不翻转第 K+1 列 不翻转第 K+1 行,翻转第 K+1 列 不翻转第K+1行,不翻转K+1列

对于每个选项,我们检查大小为 K+1 的子矩阵是否与目标矩阵的相同区域匹配。如果匹配,则进入下一步(K+2),否则,我们停止搜索并得出两个原始矩阵不匹配的结论。我们继续这个过程,直到我们匹配大小为 N 的子矩阵。

因此,每次,我们都会继续下一行和下一列,尽量避免翻转之前的行/列,从而保持已经匹配的子矩阵不变。

流程是这样的: 1. 我们通过翻转第一行/列来匹配左上角的 1x1 子矩阵 2. 我们通过翻转第二行/列来匹配左上角的 2x2 子矩阵 3. 我们通过翻转第三行/列来匹配左上角的 3x3 子矩阵 ... N. 我们通过翻转第 N 行/列来匹配左上 NxN 子矩阵

【讨论】:

以上是关于确定是不是可以通过翻转 1 矩阵的行和列来达到给定二进制矩阵的算法的主要内容,如果未能解决你的问题,请参考以下文章

给定索引,获取三角矩阵的行和列

检查给定行和列总和是不是只有 2 行的二进制矩阵

绘制二维矩阵的行和列

matlab 交换矩阵的行和列

matlab怎样通过for循环语句找出两矩阵相同的行和列?

打印 2 个数组的行和列的总和