生成一个matrix,其中包含从n个向量中获取的元素的所有组合
这个问题经常以某种forms出现(例如参见这里或这里 )。 所以我想我会以一般forms呈现,并提供一个可供未来参考的答案。
给定任意数量的可能不同大小的vector,生成一个
n
列matrix,其行描述了从这些vector(笛卡尔乘积)中取出的所有元素的组合。
例如,
vectors = { [1 2], [3 6 9], [10 20] }
应该给
combs = [ 1 3 10 1 3 20 1 6 10 1 6 20 1 9 10 1 9 20 2 3 10 2 3 20 2 6 10 2 6 20 2 9 10 2 9 20 ]
ndgrid
函数几乎给出了答案,但有一个警告: n
输出variables必须显式定义来调用它。 由于n
是任意的,所以最好的办法是用逗号分隔的列表 (由n
单元格产生的单元格arrays)作为输出。 得到的n
matrix然后连接成所需的n
列matrix:
vectors = { [1 2], [3 6 9], [10 20] }; %// input data: cell array of vectors n = numel(vectors); %// number of vectors combs = cell(1,n); %// pre-define to generate comma-separated list [combs{end:-1:1}] = ndgrid(vectors{end:-1:1}); %// the reverse order in these two %// comma-separated lists is needed to produce the rows of the result matrix in %// lexicographical order combs = cat(n+1, combs{:}); %// concat the n n-dim arrays along dimension n+1 combs = reshape(combs,[],n); %// reshape to obtain desired matrix
一点点简单…如果你有neural network工具箱,你可以简单地使用combvec
:
vectors = {[1 2], [3 6 9], [10 20]}; combs = combvec(vectors{:}).' % Use cells as arguments
它以一个稍微不同的顺序返回一个matrix:
combs = 1 3 10 2 3 10 1 6 10 2 6 10 1 9 10 2 9 10 1 3 20 2 3 20 1 6 20 2 6 20 1 9 20 2 9 20
如果你想要问题中的matrix,你可以使用sortrows
:
combs = sortrows(combvec(vectors{:}).') % Or equivalently as per @LuisMendo in the comments: % combs = fliplr(combvec(vectors{end:-1:1}).')
这使
combs = 1 3 10 1 3 20 1 6 10 1 6 20 1 9 10 1 9 20 2 3 10 2 3 20 2 6 10 2 6 20 2 9 10 2 9 20
如果您查看combvec
的内部(在命令窗口中键入edit combvec
),您会看到它使用与@ LuisMendo的答案不同的代码。 我不能说整体效率更高。
如果碰巧有一个matrix的行类似于早期的单元arrays,你可以使用:
vectors = [1 2;3 6;10 20]; vectors = num2cell(vectors,2); combs = sortrows(combvec(vectors{:}).')
我已经在两个build议的解决scheme上做了一些基准testing。 基准代码基于timeit
函数 ,并包含在这篇文章的末尾。
我考虑两种情况:三个大小为n
向量和三个大小分别为n/10
, n
和n*10
向量(两种情况给出了相同数量的组合)。 n
是最多变化到240
(我select这个值,以避免在我的笔记本电脑中使用虚拟内存)。
结果在下图中给出。 基于ndgrid
的解决scheme看起来比combvec
持续花费的时间combvec
。 另外值得注意的是,在不同大小的情况下, combvec
的时间会稍有不同。
基准代码
基于ndgrid
的解决scheme的function:
function combs = f1(vectors) n = numel(vectors); %// number of vectors combs = cell(1,n); %// pre-define to generate comma-separated list [combs{end:-1:1}] = ndgrid(vectors{end:-1:1}); %// the reverse order in these two %// comma-separated lists is needed to produce the rows of the result matrix in %// lexicographical order combs = cat(n+1, combs{:}); %// concat the n n-dim arrays along dimension n+1 combs = reshape(combs,[],n);
combvec
解决scheme的function:
function combs = f2(vectors) combs = combvec(vectors{:}).';
通过调用这些函数的timeit
来测量时间的脚本:
nn = 20:20:240; t1 = []; t2 = []; for n = nn; %//vectors = {1:n, 1:n, 1:n}; vectors = {1:n/10, 1:n, 1:n*10}; t = timeit(@() f1(vectors)); t1 = [t1; t]; t = timeit(@() f2(vectors)); t2 = [t2; t]; end
这是一个自己动手的方法,使我喜欢,使用nchoosek
,虽然它不比@Luis Mendo公认的解决scheme好。
对于给定的例子,在1000次运行之后,这个解决scheme平均使用我的机器0.00065935秒,而接受的解决scheme为0.00012877秒。 对于更大的载体,在@Luis Mendo的基准testing后,这个解决scheme一直比接受的答案慢。 不过,我决定发布它,希望也许你会发现一些有用的东西:
码:
tic; v = {[1 2], [3 6 9], [10 20]}; L = [0 cumsum(cellfun(@length,v))]; V = cell2mat(v); J = nchoosek(1:L(end),length(v)); J(any(J>repmat(L(2:end),[size(J,1) 1]),2) | ... any(J<=repmat(L(1:end-1),[size(J,1) 1]),2),:) = []; V(J) toc
给
ans = 1 3 10 1 3 20 1 6 10 1 6 20 1 9 10 1 9 20 2 3 10 2 3 20 2 6 10 2 6 20 2 9 10 2 9 20 Elapsed time is 0.018434 seconds.
说明:
L
使用cellfun
获取每个向量的长度。 虽然cellfun
基本上是一个循环,但在这里考虑到你的向量数量必须相对较低,这个问题甚至是可行的。
V
将所有的vector连接在一起,以便以后访问(假设你将所有的vector作为行input,v'对列vector起作用)。
nchoosek
可以从元素总数L(end)
selectn=length(v)
元素。 这里会有更多的组合比我们需要的更多。
J = 1 2 3 1 2 4 1 2 5 1 2 6 1 2 7 1 3 4 1 3 5 1 3 6 1 3 7 1 4 5 1 4 6 1 4 7 1 5 6 1 5 7 1 6 7 2 3 4 2 3 5 2 3 6 2 3 7 2 4 5 2 4 6 2 4 7 2 5 6 2 5 7 2 6 7 3 4 5 3 4 6 3 4 7 3 5 6 3 5 7 3 6 7 4 5 6 4 5 7 4 6 7 5 6 7
由于v(1)
中只有两个元素,我们需要抛出J(:,1)>2
任何行。 同样的,当J(:,2)<3
, J(:,2)>5
等…使用L
和repmat
我们可以确定J
每个元素是否在适当的范围内,然后使用any
丢弃行有任何不好的因素。
最后,这些不是v
的实际值,只是指数。 V(J)
将返回所需的matrix。