How to compute sum of binomial more efficiently?

前端 未结 3 940
太阳男子
太阳男子 2020-12-20 09:22

I must calculate an equation as follows:

where k1,k2 are given. I am using MATLAB to compute P. I think I have a correct implementation fo

3条回答
  •  南笙
    南笙 (楼主)
    2020-12-20 09:55

    You can vectorize all the process and it will make it super-fast without any need for mex.

    First the nchoosek function:

    function C = nCk(n,k)
    % use smaller k if available
    k(k>n/2) = n-k(k>n/2);
    k = k(:);
    kmat = ones(numel(k),1)*(1:max(n-k));
    kmat = kmat.*bsxfun(@le,kmat,(n-k));
    pw = bsxfun(@power,kmat,-1./(n-k));
    pw(kmat==0) = 1;
    kleft = ones(numel(k),1)*(min(k):n);
    kleft = kleft.*bsxfun(@gt,kleft,k);
    t = bsxfun(@times,kleft,prod(pw,2));
    t (kleft==0) = 1;
    C = prod(t,2);
    end
    

    Then beta and P computation:

    function P = binomial_coefficient(k1,k2,D)
    warning ('off','MATLAB:nchoosek:LargeCoefficient');
    i_ind = nonzeros(triu(ones(D,1)*(1:D)))-1;
    j_ind = nonzeros(tril(ones(D,1)*(1:D+1)).')-1;
    valid = ~(i_ind-j_ind>=k2 | j_ind>=k1);
    i_ind = i_ind(valid);
    j_ind = j_ind(valid);
    beta = @(ii,jj) nCk(k1,jj).*nCk(k2,ii-jj)./nCk((k1+k2),ii);
    b = beta(i_ind,j_ind);
    P = sum(b(:));
    end
    

    and execution time drops from 10.674 to 0.49696 seconds.

    EDIT:

    Taking the idea of @rahnema1, I managed to make this even faster, using a table for all unique nCk computations, so none of them will be done more than once. Using the same nCk function from above, this is how the new binomial_coefficient function will look:

    function P = binomial_coefficient(k1,k2,D)
    warning ('off','MATLAB:nchoosek:LargeCoefficient');
    i_ind = nonzeros(triu(ones(D,1)*(1:D)))-1;
    j_ind = nonzeros(tril(ones(D,1)*(1:D+1)).')-1;
    valid = ~(i_ind-j_ind>=k2 | j_ind>=k1);
    i_ind = i_ind(valid);
    j_ind = j_ind(valid);
    ni = numel(i_ind);
    all_n = repelem([k1; k2; k1+k2],ni); % all n's to be used in thier order
    all_k = [j_ind; i_ind-j_ind; i_ind]; % all k's to be used in thier order
    % get all unique sets of 'n' and 'k':
    sets_tbl = unique([all_n all_k],'rows');
    uq_n = unique(sets_tbl(:,1));
    nCk_tbl = zeros([max(all_n) max(all_k)+1]);
    % compute all the needed values of nCk:
    for s = 1:numel(uq_n)
        curret_n = uq_n(s);
        curret_k = sets_tbl(sets_tbl(:,1)==curret_n,2);
        nCk_tbl(curret_n,curret_k+1) = nCk(curret_n,curret_k).';
    end
    beta = @(ii,jj) nCk_tbl(k1,jj+1).*nCk_tbl(k2,ii-jj+1)./nCk_tbl((k1+k2),ii+1);
    b = beta(i_ind,j_ind);
    P = sum(b(:));
    end
    

    and now, when it takes only 0.01212 second to run, it's not just super-fast code, it's a flying-talking-super-fast code!

提交回复
热议问题