我有一个大小为m-by-n的二进制矩阵。下面给出的是一个样本二进制矩阵(实矩阵要大得多):
1010001
1011011
1111000
0100100
给定p = m * n,我有2 ^ p个可能的矩阵配置。我想得到一些满足某些规则的模式。例如:
如何在不按顺序检查所有2 ^ p组合的情况下严格地满足这些约束条件?
在我的情况下,p可以是2400这样的数字,给出大约2.96476e + 722种可能的组合。
答案 0 :(得分:3)
不是迭代所有2 ^ p组合,而是可以生成这样的二进制矩阵的一种方法是基于给定的约束执行重复的行和列操作。作为一个例子,我将发布一些代码,这些代码将根据您在上面列出的三个约束生成矩阵:
首先从初始化一些参数开始:
nRows = 10; % Row size of matrix
nColumns = 10; % Column size of matrix
minZeroes = 5; % Constraint 1 (for columns)
minRowSum = 5; % Constraint 2 (for rows)
minLengthOnes = 3; % Constraint 3 (for columns)
接下来,创建一些函数来生成与上面的约束1和3匹配的列向量:
function vector = make_column
vector = [false(minZeroes,1); true(nRows-minZeroes,1)]; % Create vector
[vector,maxLength] = randomize_column(vector); % Randomize order
while maxLength < minLengthOnes, % Loop while constraint 3 is not met
[vector,maxLength] = randomize_column(vector); % Randomize order
end
end
function [vector,maxLength] = randomize_column(vector)
vector = vector(randperm(nRows)); % Randomize order
edges = diff([false; vector; false]); % Find rising and falling edges
maxLength = max(find(edges == -1)-find(edges == 1)); % Find longest
% sequence of ones
end
函数 make_column 将首先创建一个逻辑列向量,其中最少数量为0个元素,其余元素设置为1(使用函数TRUE和FALSE) 。该向量将对其元素进行随机重新排序,直到它包含一个大于或等于所需最小长度的序列。这是使用 randomize_column 函数完成的。使用RANDPERM函数随机重新排序向量以生成随机索引顺序。使用DIFF功能检测序列在0和1之间切换的边缘。然后使用边的索引来查找最长序列的长度(使用FIND和MAX)。
通过上述两个函数,我们现在可以生成一个至少满足约束条件1和3的初始二进制矩阵:
binMat = false(nRows,nColumns); % Initialize matrix
for iColumn = 1:nColumns,
binMat(:,iColumn) = make_column; % Create each column
end
当然,现在我们必须确保满足约束2。我们可以使用SUM函数对每行求和:
rowSum = sum(binMat,2);
如果 rowSum 的任何元素小于我们想要的最小行总和,我们将不得不调整一些列值来进行补偿。您可以通过多种方式修改列值。我在这里举一个例子:
while any(rowSum < minRowSum), % Loop while constraint 2 is not met
[minValue,rowIndex] = min(rowSum); % Find row with lowest sum
zeroIndex = find(~binMat(rowIndex,:)); % Find zeroes in that row
randIndex = round(1+rand.*(numel(zeroIndex)-1));
columnIndex = zeroIndex(randIndex); % Choose a zero at random
column = binMat(:,columnIndex);
while ~column(rowIndex), % Loop until zero changes to one
column = make_column; % Make new column vector
end
binMat(:,columnIndex) = column; % Update binary matrix
rowSum = sum(binMat,2); % Update row sum vector
end
此代码将循环,直到所有行总和大于或等于我们想要的最小总和。首先,使用MIN找到具有最小总和( rowIndex )的行的索引。接下来,找到该行中零的索引,并随机选择其中一个作为要修改的列的索引( columnIndex )。使用 make_column ,连续生成一个新的列向量,直到给定行中的0变为1.然后更新二进制矩阵中的该列并计算新的行总和。
对于相对较小的10×10二进制矩阵和给定的约束,上述代码通常在不超过几秒的时间内完成。随着更多限制,事情当然会变得更加复杂。根据您选择约束的方式,可能没有可能的解决方案(例如,将 minRowSum 设置为6将导致上述代码从不收敛到解决方案)。< / p>
希望这会给你一个起点,开始使用矢量化操作生成你想要的各种矩阵。
答案 1 :(得分:1)
如果您有足够的约束,可以尝试探索所有可能的矩阵:
// Explore all possibilities starting at POSITION (0..P-1)
explore(int position)
{
// Check if one or more constraints can't be verified anymore with
// all values currently set.
invalid = ...;
if (invalid) return;
// Do we have a solution?
if (position >= p)
{
// print the matrix
return;
}
// Set one more value and continue exploring
for (int value=0;value<2;value++)
{ matrix[position] = value; explore(position+1); }
}
如果约束的数量很少,这种方法将花费太多时间。
在这种情况下,对于您提供的约束类型,simulated annealing可能是一个很好的解决方案。 在满足所有约束条件时,必须设计一个高能量函数。那将是这样的:
答案 2 :(得分:1)
如果所有约束都与列相关(如问题中的情况),则可以找到所有可能的有效列,并检查矩阵中的每列是否在此集合中。 (即,当您单独考虑每个列时,可以减少很多可能性。)
答案 3 :(得分:0)
我可能会离开这里,但我记得用一些遗传算法做过类似的事情。
答案 4 :(得分:0)
检查伪布尔约束(也称为0-1整数编程)。
答案 5 :(得分:0)
如果您的约束集足够复杂,这几乎是不可能的。您可以尝试使用随机优化器,如模拟退火,粒子群优化或遗传算法来找到可行的解决方案。
但是,如果您可以针对此类问题生成一个(非随机)解决方案,那么通常您可以通过对现有解决方案进行随机排列来生成其他解决方案。