如何获得矩阵中元素的所有可能组合,但不允许在列之间交换元素?

How to get all the possible combinations of elements in a matrix, but don't allow exchange of elements inbetween columns?

假设我有这个矩阵 A:[3 x 4]

      1     4     7    10
      2     5     8    11
      3     6     9    12

我想置换每列中的元素,但它们不能更改为不同的列,因此 1 2 3 需要始终是第一列的一部分。例如我想要:

      3     4     8    10
      1     5     7    11
      2     6     9    12
      3     4     8    11
      1     6     7    10
      2     5     9    12
      1     6     9    11
      .     .     .     .

所以在一个矩阵中我想要所有可能的排列,在这种情况下,有 3 个不同的选择 3x3x3x3=81possibilities.So 我的结果矩阵应该是 81x4,因为我每次只需要一个[1x4]线向量答案,那81次。

另一种解决问题的方法是(对我来说也是一样),如果我有 4 列向量:

     a=[1;2;3]
     b=[4;5;6]
     c=[7;8;9]
     d=[10;11;12;13]

与我之前的例子相比,每个列向量可以有不同的行数。然后就像我有 4 个盒子,A,B C,D,我只能将 a 的一个元素放在 A 中,b 放在 B 中,依此类推;所以我想得到所有可能的排列,答案 [A B C D][1x4] 行,在这种情况下,我会有 3x3x3x4=108 不同的行。所以我被误解的地方(我的错)是我不想要所有不同的 [3x4] 矩阵答案,而只想要 [1x4] 行。

所以在这种情况下答案是:

     1 4 7 10
and  1 4 7 11
and  1 4 7 12
and  1 4 7 13
and  2 4 8 10
and  ...
until there are the 108 combinations

Matlab 中的函数 perms 不能这样做,因为我不想置换所有矩阵(顺便说一句,这已经是一个太大的矩阵了)。

那么你知道我该怎么做吗?或者是否有一个功能可以做到这一点?当然,我也可以有不同大小的矩阵。谢谢

这有点复杂,但不需要任何额外的工具箱就可以工作:

你基本上想要一个 b 元素 'truth table' 如果你将它应用到每个元素,你可以像这样生成 (adapted from here):

[b, n] = size(A)
truthtable = dec2base(0:power(b,n)-1, b) - '0'

现在您需要通过将列数乘以总行数将事实 table 转换为线性索引:

idx = bsxfun(@plus, b*(0:n-1)+1, truthtable)

现在您不是将这个真理 table 应用到每个元素,而是将它应用到每个排列。有 6 个排列,因此 b 变为 6。诀窍是创建一个 6-by-1 元胞数组,其中每个元素都有不同的 [1,2,3] 排列,然后将真理 table 的想法应用到那个:

[m,n] = size(A);
b = factorial(m);
permutations = reshape(perms(1:m)',[],1);
permCell = mat2cell(permutations,ones(b,1)*m,1);

truthtable = dec2base(0:power(b,n)-1, b) - '0';
expandedTT = cell2mat(permCell(truthtable + 1));
idx = bsxfun(@plus, m*(0:n-1), expandedTT);

A(idx)

你的问题似乎是一个非常有趣的脑筋急转弯。我建议如下:

in = [1,2,3;4,5,6;7,8,9;10,11,12]';
b = perms(1:3);
a = 1:size(b,1);
c = combvec(a,a,a,a);
for k = 1:length(c(1,:))
    out{k} = [in(b(c(1,k),:),1),in(b(c(2,k),:),2),in(b(c(3,k),:),3),in(b(c(4,k),:),4)];
end
%and if you want your result as an ordinary array:
out = vertcat(out{:});

b 是一个 6x3 数组,其中包含 [1,2,3] 所有可能的排列。 c4x1296 数组,包含 a = 1:6 中元素的所有可能组合。在 for 循环中,我们使用从 1 到 6 的数字来获得 b 中的排列,并且该排列用作列的索引。

希望对您有所帮助

另一个答案。相当具体只是为了演示这个概念,但可以很容易地进行调整。

A = [1,4,7,10;2,5,8,11;3,6,9,12];
P = perms(1:3)'
[X,Y,Z,W] = ndgrid(1:6,1:6,1:6,1:6);

您现在有 1296 个排列。如果您想访问第 400 个:

Permutation_within_column = [P(:,X(400)), P(:,Y(400)), P(:,Z(400)), P(:,W(400))];
ColumnOffset = repmat([0:3]*3,[3,1])
My_permutation = Permutation_within_column + ColumnOffset; % results in valid linear indices
A(My_permutation)

这种方法可以让你按需获得第400个排列;如果您希望将所有可能的排列连接到第 3 维(即 3x4x1296 矩​​阵),您可以使用 for 循环执行此操作,或者简单地调整上述内容并进行矢量化;例如,如果您想创建一个 3x4x2 矩阵,其中包含沿第 3 维的前两个排列:

Permutations_within_columns = reshape(P(:,X(1:2)),3,1,[]);
Permutations_within_columns = cat(2, Permutations_within_columns, reshape(P(:,Y(1:2)),3,1,[]));
Permutations_within_columns = cat(2, Permutations_within_columns, reshape(P(:,Z(1:2)),3,1,[]));
Permutations_within_columns = cat(2, Permutations_within_columns, reshape(P(:,W(1:2)),3,1,[]));
ColumnOffsets = repmat([0:3]*3,[3,1,2]);
My_permutations = Permutations_within_columns + ColumnOffsets;
A(My_permutations)

这种方法使您能够收集特定的子范围,如果可用内存是一个问题(即对于较大的矩阵)并且您更愿意执行操作,这可能很有用按块。如果内存不是问题,您可以根据需要在一个巨大的矩阵中一次获得所有 1296 个排列;适当调整(例如,在第 3 维中将 ColumnOffsets 复制正确的次数)

基本上您想要获得 1:3 排列的 4 倍的所有组合。 您可以使用神经网络工具箱中的 combvec 生成这些 (), or with permn from the File Exchange.

之后是管理索引、应用 sub2ind(使用正确的列索引)并重新排列直到一切都按您想要的顺序排列的问题。

a = [1     4     7    10
     2     5     8    11
     3     6     9    12];

siz = size(a);
perm1 = perms(1:siz(1));
Nperm1 = size(perm1,1); % = factorial(siz(1))
perm2 = permn(1:Nperm1, siz(2) );
Nperm2 = size(perm2,1);
permidx = reshape(perm1(perm2,:)', [Nperm2 siz(1), siz(2)]); % reshape unnecessary, easier for debugging

col_base_idx = 1:siz(2);
col_idx = col_base_idx(ones(Nperm2*siz(1) ,1),:);
lin_idx = reshape(sub2ind(size(a), permidx(:), col_idx(:)), [Nperm2*siz(1) siz(2)]);

result = a(lin_idx);

这避免了任何循环或单元格连接,而是使用直接索引。

每列排列,唯一行

同样的方法:

siz = size(a);
permidx = permn(1:siz(1), siz(2) );
Npermidx = size(permidx, 1);
col_base_idx = 1:siz(2);
col_idx = col_base_idx(ones(Npermidx, 1),:);
lin_idx = reshape(sub2ind(size(a), permidx(:), col_idx(:)), [Npermidx siz(2)]);

result = a(lin_idx);

这是另一个八度友好的解决方案:

function result = Tuples(A)
    [P,n]= size(A);
    M = reshape(repmat(1:P,  1, P ^(n-1)), repmat(P, 1, n));
    result = zeros(P^ n, n);
    for i = 1:n
        result(:, i) = A(reshape(permute(M, circshift((1:n)', i)), P ^ n, 1), i);
    end
end
%%%example
A = [...
      1     4     7    10;...
      2     5     8    11;...
      3     6     9    12];
result = Tuples(A)

更新: 问题更新为:给定 n 个不同长度的向量生成所有可能元组的列表,其第 i 个元素来自向量 i:

function result = Tuples( A)
    if exist('repelem') ==0
        repelem = @(v,n) repelems(v,[1:numel(v);n]);
    end
    n = numel(A);
    siz = [ cell2mat(cellfun(@numel, A , 'UniformOutput', false))];
    tot_prd = prod(siz);
    cum_prd=cumprod(siz);
    tot_cum = tot_prd ./ cum_prd;
    cum_siz = cum_prd ./ siz;
    result = zeros(tot_prd, n);
    for i = 1: n
        result(:, i) = repmat(repelem(A{i},repmat(tot_cum(i),1,siz(i))) ,1,cum_siz(i));
    end
end
%%%%example
a = {...
        [1;2;3],...
        [4;5;6],...
        [7;8;9],...
        [10;11;12;13]...
    };
result =Tuples(a)