使用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
基准测试:
如果您的numVars 较低且maxDegree 较高,则对上述代码进行基准测试可以得出diff 解决方案更快。如果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,:);