この質問は、頻繁にポップアップ表示されます(たとえば、 here または here を参照)。だから私はそれを一般的な形で提示し、将来の参考に役立つかもしれない答えを提供すると思った。
サイズが異なる可能性のある任意の数の
n
ベクトルが与えられた場合、それらのベクトル(デカルト積)から取得した要素のすべての組み合わせを行が記述するn
- column行列を生成します。
例えば、
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
関数はほとんど答えを提供しますが、警告が1つあります。それを呼び出すには、n
出力変数を明示的に定義する必要があります。 n
は任意なので、最良の方法は コンマ区切りリスト (n
cellsを使用してセル配列から生成)を使用して出力として使用することです。次に、結果のn
行列は、目的のn
- column行列に連結されます。
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
これは、わずかに異なる順序でマトリックスを返します。
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
問題のマトリックスが必要な場合は、 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の答えとは異なるコードを使用していることがわかります。どちらが全体的に効率的かは言えません。
行が以前のセル配列に似ている行列がある場合は、次を使用できます。
vectors = [1 2;3 6;10 20];
vectors = num2cell(vectors,2);
combs = sortrows(combvec(vectors{:}).')
提案された2つのソリューションのベンチマークを実行しました。ベンチマークコードは timeit
function に基づいており、この投稿の最後に含まれています。
私は2つのケースを考えます:サイズn
の3つのベクトル、サイズn/10
、n
およびn*10
の3つのベクトル(両方とも同じ数の組み合わせを与えます) 。 n
は最大240
まで変化します(ラップトップコンピューターで仮想メモリの使用を避けるためにこの値を選択します)。
結果を次の図に示します。 ndgrid
ベースのソリューションは、一貫してcombvec
よりも時間がかかるようです。 combvec
にかかる時間は、サイズが異なる場合でも規則的に少しずつ変化することに注意することも興味深いです。
ベンチマークコード
ndgrid
ベースのソリューションの関数:
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
ソリューションの関数:
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が受け入れたnotよりも優れています解決。
与えられた例では、1,000回の実行後、このソリューションは平均0.00065935秒でマシンを使用しましたが、受け入れられたソリューションは0.00012877秒でした。大きなベクトルの場合、@ Luis Mendoのベンチマークポストに従って、このソリューションは一貫して受け入れられている答えよりも低速です。それにもかかわらず、私はあなたがそれについて何か有用なものを見つけるかもしれないことを期待してそれを投稿することにしました:
コード:
_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
は、後で簡単にアクセスできるようにすべてのベクトルを連結します(これはすべてのベクトルを行として入力したことを前提としています。v 'は列ベクトルに対して機能します)。
nchoosek
は、要素の総数n=length(v)
からL(end)
要素を選択するすべての方法を取得します。 ここに必要な組み合わせよりも多くの組み合わせがあります。
_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)
には2つの要素しかないため、J(:,1)>2
の行をすべて破棄する必要があります。同様に、J(:,2)<3
、J(:,2)>5
など... L
およびrepmat
を使用して、J
の各要素を判断できます。が適切な範囲にある場合、any
を使用して、不良要素がある行を破棄します。
最後に、これらはv
からの実際の値ではなく、単なるインデックスです。 V(J)
は、目的のマトリックスを返します。