MATLAB:高效生成多指数的大整数矩阵

时间:2015-02-02 11:12:07

标签: matlab matrix vectorization

设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)。

如果您对这个奇怪的矩阵的起源感到好奇,请告诉我!

3 个答案:

答案 0 :(得分:2)

使用nchoosekdiff

的解决方案

以下解决方案基于 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)来获取矩阵。

使用nchoosekaccumarray / histc

的解决方案

这种方法基于以下思想:所有 k-multicombinations 与我们正在寻找的矩阵之间存在双射。多重组合给出了应该添加条目的位置。例如,多组合[1,1,1,1,3]将映射到[4,0,1],因为有四个1和一个3。可以使用accumarrayhistc转换。这是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,:)

我认为第一行可以非常有效地创建候选列表,但遗憾的是我不知道如何在此之后以有效的方式减少列表。

所以第二行有效,但可以明智地使用改进性能。