我有一个方形矩阵,由1个元素组成 或0.第i行切换切换所有第i行元素(1 变为0,反之亦然)和第j列切换切换所有 第j列元素。我有另一个方阵 相似的大小。我想将初始矩阵更改为 使用最小切换次数的最终矩阵。例如
|0 0 1|
|1 1 1|
|1 0 1|
到
|1 1 1|
|1 1 0|
|1 0 0|
需要切换第一行和最后一行 列。
这个算法的正确算法是什么?
答案 0 :(得分:10)
一般来说,问题不会有解决方案。为了看到这一点,请注意,将矩阵A变换为矩阵B相当于将矩阵A-B(使用二进制算法计算,使0-1 = 1)变换为零矩阵。查看矩阵A - B,并应用列切换(如果需要),以便第一行变为全0或全1。此时,您已完成列切换 - 如果您切换一列,则必须将它们全部切换以使第一行正确。如果此时甚至一行是0和1的混合,则问题无法解决。如果现在每一行都是0或全1,则可以通过切换适当的行来达到零矩阵来解决问题。
要获得最小值,请将第一行转换为0和1时所需的切换次数进行比较。在OP的示例中,候选者将切换第3列和第1行或切换第1列和第2列以及第2行和第3行。事实上,您可以通过查看第一个解决方案并查看切换次数是否更小或如果大于N,则大于N,而不是切换相反的行和列。
答案 1 :(得分:4)
并非总是可行。如果你从一个偶数个1的2x2矩阵开始,你就永远无法得到奇数为1的最终矩阵。
答案 2 :(得分:3)
<强>算法强>
简化“尝试将A转换为B”到“尝试将M转换为0”的问题,其中M = A x或B.现在所有必须切换的位置都有1个。
考虑M中的任意位置。它受到恰好一列切换和恰好一行切换的影响。如果它的初始值是V,则列切换的存在是C,并且行切换的存在是R,那么最终值F是V x或C x或R.这是一个非常简单的关系,它使问题变得微不足道解决。
注意,对于每个位置,R = F x或V x或C = 0 x或V x或C = V x或C.如果我们设置C然后我们强制R的值,反之亦然。这太棒了,因为这意味着如果我设置任何行切换的值,那么我将强制列的所有切换。这些列切换中的任何一个都将强制行的所有切换。如果结果是0矩阵,那么我们有一个解决方案。我们只需要尝试两种情况!
<强>伪代码强>
function solve(Matrix M) as bool possible, bool[] rowToggles, bool[] colToggles:
For var b in {true, false}
colToggles = array from c in M.colRange select b xor Matrix(0, c)
rowToggles = array from r in M.rowRange select colToggles[0] xor M(r, 0)
if none from c in M.colRange, r in M.rowRange
where colToggle[c] xor rowToggle[r] xor M(r, c) != 0 then
return true, rowToggles, colToggles
end if
next var
return false, null, null
end function
<强>分析强>
分析是微不足道的。我们尝试两种情况,我们在其中运行一行,然后是一列,然后是所有单元格。因此,如果有r行和c列,意味着矩阵的大小为n = c * r,则时间复杂度为O(2 *(c + r + c * r))= O(c * r)= O( N)。我们使用的唯一空间是存储输出所需的内容= O(c + r)。
因此,算法在矩阵的大小中采用线性时间线性,并使用输出大小的线性空间。由于显而易见的原因,它是渐近最优的。
答案 3 :(得分:1)
算法:
假设我们有矩阵m = [[1,0],[0,1]]。
m: 1 0
0 1
我们生成所有行号和列号的列表,
像这样:['r0', 'r1', 'c0', 'c1']
现在我们蛮力,也就是检查,每一个可能的步骤组合
例如,我们从一步解决方案开始,
ksubsets = [['r0'], ['r1'], ['c0'], ['c1']]
如果没有元素是解决方案,那么继续进行两步解决方案,
ksubsets = [['r0', 'r1'], ['r0', 'c0'], ['r0', 'c1'], ['r1', 'c0'], ['r1', 'c1'], ['c0', 'c1']]
等...
ksubsets元素(组合)是要在矩阵中应用的切换步骤列表。
Python实现(在2.5版本上测试)
# Recursive definition (+ is the join of sets)
# S = {a1, a2, a3, ..., aN}
#
# ksubsets(S, k) = {
# {{a1}+ksubsets({a2,...,aN}, k-1)} +
# {{a2}+ksubsets({a3,...,aN}, k-1)} +
# {{a3}+ksubsets({a4,...,aN}, k-1)} +
# ... }
# example: ksubsets([1,2,3], 2) = [[1, 2], [1, 3], [2, 3]]
def ksubsets(s, k):
if k == 1: return [[e] for e in s]
ksubs = []
ss = s[:]
for e in s:
if len(ss) < k: break
ss.remove(e)
for x in ksubsets(ss,k-1):
l = [e]
l.extend(x)
ksubs.append(l)
return ksubs
def toggle_row(m, r):
for i in range(len(m[r])):
m[r][i] = m[r][i] ^ 1
def toggle_col(m, i):
for row in m:
row[i] = row[i] ^ 1
def toggle_matrix(m, combos):
# example of combos, ['r0', 'r1', 'c3', 'c4']
# 'r0' toggle row 0, 'c3' toggle column 3, etc.
import copy
k = copy.deepcopy(m)
for combo in combos:
if combo[0] == 'r':
toggle_row(k, int(combo[1:]))
else:
toggle_col(k, int(combo[1:]))
return k
def conversion_steps(sM, tM):
# Brute force algorithm.
# Returns the minimum list of steps to convert sM into tM.
rows = len(sM)
cols = len(sM[0])
combos = ['r'+str(i) for i in range(rows)] + \
['c'+str(i) for i in range(cols)]
for n in range(0, rows + cols -1):
for combo in ksubsets(combos, n +1):
if toggle_matrix(sM, combo) == tM:
return combo
return []
示例:
m: 0 0 0
0 0 0
0 0 0
k: 1 1 0
1 1 0
0 0 1
>>> m = [[0,0,0],[0,0,0],[0,0,0]]
>>> k = [[1,1,0],[1,1,0],[0,0,1]]
>>> conversion_steps(m, k)
['r0', 'r1', 'c2']
>>>
答案 4 :(得分:0)
如果只能切换行而不是列,那么只有一个矩阵子集可以转换为最终结果。如果是这种情况,那将非常简单:
for every row, i:
if matrix1[i] == matrix2[i]
continue;
else
toggle matrix1[i];
if matrix1[i] == matrix2[i]
continue
else
die("cannot make similar");
答案 5 :(得分:0)
这是状态空间搜索问题。您正在搜索从起始状态到目标状态的最佳路径。在这种特殊情况下,“最佳”被定义为“最小操作次数”。
状态空间是一组二进制矩阵,可以通过行和列切换操作从起始位置生成。
假设目标位于状态空间(在某些情况下不是有效的假设:请参阅Henrik的答案),我会尝试抛出经典的启发式搜索(可能是A *,因为它是关于该品种中最好的)该问题的算法,看看发生了什么。
第一个,最明显的启发式是“正确元素的数量”。
任何体面的人工智能教科书都会讨论搜索和A *算法。
您可以将矩阵表示为非负整数,矩阵中的每个单元格对应于整数中的一位。在支持64位长无符号整数的系统上,这使您可以使用最多8x8的任何内容。然后,您可以对该数字使用异或运算来实现行和列切换操作。
注意:原始总状态空间大小为2 ^(N ^ 2),其中N是行(或列)的数量。对于4x4矩阵,这是2 ^ 16 = 65536种可能的状态。
答案 6 :(得分:0)
不要将其视为矩阵问题,而是从每个数组中取出9位,将每个数组加载为2字节大小类型(16位,这可能是数组的源头),然后在两者之间做一次异或。
(根据您的CPU类型,位顺序会有所不同)
第一个数组将变为:0000000001111101 第二个数组将变为:0000000111110101
单个XOR会产生输出。不需要循环。如果你还想做的话,你所要做的就是将结果“解包”回一个数组。你可以读取这些位,而不是诉诸于此,但是.i
答案 7 :(得分:0)
我认为没有必要使用蛮力。
这个问题可以用一个群体来改写。具有2个元素的场上的矩阵构成了关于加法的交换组。
如前所述,A是否可以切换到B的问题等同于看AB是否可以切换为0.注意,行i的切换是通过在行i和0中仅添加一个矩阵来完成的。否则,虽然列j的切换是通过添加一个矩阵,只有列j中的一个,否则为零。
这意味着当且仅当A-B包含在由切换矩阵生成的子组中时,A-B才能切换到零矩阵。
由于加法是可交换的,因此首先进行列的切换,我们可以先将Marius的方法应用于列,然后应用于行。
特别是列的切换必须使任何行全部为全部或全部为零。有两种可能性:
切换列,使第一行中的每1个变为零。如果在此之后有一行出现了1和0,则没有解决方案。否则对行应用相同的方法(见下文)。
切换列,使第一行中的每个0变为1.如果在此之后出现一行,其中出现了1和0,则没有解决方案。否则对行应用相同的方法(见下文)。
由于在每行中只包含1或0的意义上成功切换了列,因此有两种可能性:
切换行,使第一列中的每1个变为零。
切换行,使第一行中的每个0都变为零。
当然,在行的步骤中,我们采取的可能性导致更少的切换,即我们计算第一列中的那些,然后决定如何切换。
总共只需考虑2个案例,即如何切换列;对于行步骤,可以通过计数来确定切换,以最小化第二步中的切换次数。