设d和p为两个整数。我需要生成一个整数的矩阵A,有d列和N = nchoosek(d + p,p)行。请注意,nchoosek(d + p,p)随着d和p的增加而迅速增加,因此我可以快速生成A非常重要。 A的行是所有具有从0到p的分量的多索引,使得分量的总和小于或等于p。这意味着,如果d = 3且p = 3,则A是[N = nchoosek(3 + 3,3)= 20x3]矩阵,具有以下结构:
A=[0 0 0;
1 0 0;
0 1 0;
0 0 1;
2 0 0;
1 1 0;
1 0 1;
0 2 0;
0 1 1;
0 0 2;
3 0 0;
2 1 0;
2 0 1;
1 2 0;
1 1 1;
1 0 2;
0 3 0;
0 2 1;
0 1 2;
0 0 3]
完全遵循我使用的行排序并不是必不可少的,尽管它会让我的生活变得更轻松(对于那些感兴趣的人,它被称为分级词典排序,它在这里描述: http://en.wikipedia.org/wiki/Monomial_order)。
如果您对这个奇怪的矩阵的起源感到好奇,请告诉我!
答案 0 :(得分:2)
nchoosek
和diff
以下解决方案基于 Mark Dickinson 的this clever answer。
function degrees = monomialDegrees(numVars, maxDegree)
if numVars==1
degrees = (0:maxDegree).';
return;
end
degrees = cell(maxDegree+1,1);
k = numVars;
for n = 0:maxDegree
dividers = flipud(nchoosek(1:(n+k-1), k-1));
degrees{n+1} = [dividers(:,1), diff(dividers,1,2), (n+k)-dividers(:,end)]-1;
end
degrees = cell2mat(degrees);
您可以致电monomialDegrees(d,p)
来获取矩阵。
nchoosek
和accumarray
/ histc
这种方法基于以下思想:所有 k-multicombinations 与我们正在寻找的矩阵之间存在双射。多重组合给出了应该添加条目的位置。例如,多组合[1,1,1,1,3]
将映射到[4,0,1]
,因为有四个1
和一个3
。可以使用accumarray
或histc
转换。这是accumarray
- 方法:
function degrees = monomialDegrees(numVars, maxDegree)
if numVars==1
degrees = (0:maxDegree).';
return;
end
degrees = cell(maxDegree+1,1);
degrees{1} = zeros(1,numVars);
for n = 1:maxDegree
pos = nmultichoosek(1:numVars, n);
degrees{n+1} = accumarray([reshape((1:size(pos,1)).'*ones(1,n),[],1),pos(:)],1);
end
degrees = cell2mat(degrees);
这里是使用histc
的替代方案:
function degrees = monomialDegrees(numVars, maxDegree)
if numVars==1
degrees = (0:maxDegree).';
return;
end
degrees = cell(maxDegree+1,1);
degrees(1:2) = {zeros(1,numVars); eye(numVars);};
for n = 2:maxDegree
pos = nmultichoosek(1:numVars, n);
degrees{n+1} = histc(pos.',1:numVars).';
end
degrees = cell2mat(degrees(1:maxDegree+1));
两者都使用以下函数来生成多重组合:
function combs = nmultichoosek(values, k)
if numel(values)==1
n = values;
combs = nchoosek(n+k-1,k);
else
n = numel(values);
combs = bsxfun(@minus, nchoosek(1:n+k-1,k), 0:k-1);
combs = reshape(values(combs),[],k);
end
对上述代码进行基准测试,如果diff
低且numVars
高,则maxDegree
- 解决方案会更快。如果numVars
高于maxDegree
,则histc
解决方案会更快。
这是dec2base
的 Dennis' 方法的替代方法,它对最大基数有限制。它仍然比上述解决方案慢很多。
function degrees = monomialDegrees(numVars, maxDegree)
Cs = cell(1,numVars);
[Cs{:}] = ndgrid(0:maxDegree);
degrees = reshape(cat(maxDegree+1, Cs{:}),(maxDegree+1)^numVars,[]);
degrees = degrees(sum(degrees,2)<=maxDegree,:);
答案 1 :(得分:1)
我会这样解决:
ncols=d;
colsum=p;
base=(0:colsum)';
v=@(dm)permute(base,[dm:-1:1]);
M=bsxfun(@plus,base,v(2));
for idx=3:ncols
M=bsxfun(@plus,M,v(idx));
end
L=M<=colsum;
A=cell(1,ncols);
[A{:}]=ind2sub(size(L),find(L));
a=cell2mat(A);
%subtract 1 because 1 based indexing but base starts at 0
a=a-1+min(base);
它建立一个包含总和的p维矩阵。此代码的效率取决于sum(L(:))/numel(L)
,此商指示您创建的矩阵实际用于解决方案的程度。如果你的输入变低,可能会有更好的解决方案。
答案 2 :(得分:1)
这是一种非常简单的方法:
L = dec2base(0:4^3-1,4);
idx=sum(num2str(L)-'0',2)<=3;
L(idx,:)
我认为第一行可以非常有效地创建候选列表,但遗憾的是我不知道如何在此之后以有效的方式减少列表。
所以第二行有效,但可以明智地使用改进性能。