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

时间:2016-07-26 14:35:13

标签: matlab matrix combinations permutation

让我说我有这个矩阵A:[3 x 4]

Rank

我想要置换每列中的元素,但它们无法更改为其他列,因此library(sqldf) sqldf("select b.*, max(a.YearAmt) Year from df1 a join df1 b on a.rowid < b.rowid and a.Rank = '' group by b.rowid having b.Rank != ''") 需要始终成为第一列的一部分。所以例如我想要:

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

所以在一个矩阵中我希望得到所有可能的排列,在这种情况下,有3种不同的选择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 . . . . ,因为我只需要每个时间一3x3x3x3=81行向量答案,那81次。

问题的另一种方式(对于我来说同样的结局),如果我有4列向量,那就是:

81x4

与我之前的例子相比,每个列向量可以有不同的行数。然后就像我有4个盒子,A,B C,D和我只能把一个元素放在A中,b放在B中,依此类推;所以我希望得到所有排列的答案[1x4]成为 a=[1;2;3] b=[4;5;6] c=[7;8;9] d=[10;11;12;13] 行,在这种情况下,我会有[A B C D]不同的行。所以,我被误解(我的错),就是我不想要所有不同的[3x4]矩阵答案,只需要[1x4]行。

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

[1x4]

Matlab中的函数3x3x3x4=108无法做到这一点,因为我不想置换所有矩阵(顺便说一下,这已经是一个太大的矩阵了)。

那么你有什么想法我能做到这一点还是有一个可以做到这一点的功能?当然,我也可能有不同大小的矩阵。谢谢

5 个答案:

答案 0 :(得分:1)

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

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中的排列,并且该排列用作列的索引。

希望有所帮助

答案 1 :(得分:1)

基本上你想得到1:3的4倍排列的所有组合。 您可以使用神经网络工具箱(like @brainkz did)或permn from the File Exchange使用combvec生成这些内容。

之后,这是管理索引,应用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);

答案 2 :(得分:1)

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

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)

答案 3 :(得分:0)

这有点复杂,但无需任何额外的工具箱即可运行:

你基本上想要一个b元素&#39;真值表&#39;如果你将它应用于每个元素,你可以像这样生成(adapted from here):

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

现在您需要通过添加列号乘以总行数将真值表转换为线性索引:

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

现在您不是将此真值表应用于您实际想要将其应用于每个排列的每个元素。有6个排列,因此b变为6。诀窍是然后创建一个6 - by - 1单元格数组,其中每个元素都有[1,2,3]的不同排列,然后将真值表的想法应用于:

[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)

答案 4 :(得分:0)

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

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个排列;如果您希望在第三维中连接所有可能的排列(即3x4x1296矩阵),您可以使用for循环执行此操作,或者只是调整上面和矢量;例如,如果你想创建一个3x4x2矩阵,保持第三维的前两个排列:

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个排列;只需适当调整(例如,在第三维中复制ColumnOffsets正确的次数)