Code covered by the BSD License  

Highlights from
Next Combination/Permutation

image thumbnail
from Next Combination/Permutation by Matt Fig
Produces one comb/perm at a time. Both with/without repetition.

nextperm(N,K)
function C = nextperm(N,K)
%NEXTPERM Loop through permutations without replacement.
% NEXTPERM(N,K), when first called, returns a function handle.  This  
% function handle, when called, returns the next permutation without 
% replacement of K elements taken from the set 1:N. This can be useful
% when the number of such permutations is too large to hold in memory at
% once.  If the number of such permutations is not too large, use 
% COMBINATOR(N,K,'p') (on the FEX) instead.
%
% The number of permutations without replacement is:  N!/(N-K)!
%   where N >= 1, N >= K >= 0
%
% Examples:
%
%     % To use each permutation one at a time, put it in a loop.
%     N = 4;  % Length of the set.
%     K = 3;  % Number of samples taken for each sampling.
%     H = nextperm(N,K);
%     for ii = 1:((prod(1:N)/(prod(1:(N-K)))))
%         A = H();
%         % Do stuff with A: use it as an index, etc.
%     end
%
%
%     % To build all of the permutations, do this (See note below):
%     ROWS = (prod(1:N)/(prod(1:(N-K))));
%     C = ones(ROWS,K);
%     for ii = 1:ROWS
%         C(ii,:) = H();
%     end
%     %Note this is a lot slower than using combinator(N,K,'p')
%
% The function handle will cycle through when the final permutation is
% returned.
%
% See also,  nchoosek, perms, combinator, npermutek (both on the FEX)
%
% Author:   Matt Fig
% Contact:  popkenai@yahoo.com
% Date: 6/9/2009
% Reference:  http://mathworld.wolfram.com/BallPicking.html 

% Arg checking.
if K>N 
    error('K must be less than or equal to N.')
end

if isempty(N) || K == 0
   C = [];  
   return
elseif numel(N)~=1 || N<=0 || ~isreal(N) || floor(N) ~= N 
    error('N should be one real, positive integer. See help.')
elseif numel(K)~=1 || K<0 || ~isreal(K) || floor(K) ~= K
    error('K should be one real non-negative integer. See help.')
end

CNT = 0;  % Initializations for the nested function.
G = [];
TMP = [];
op = 1; % These are the variables which are passed to subfunc.
blk = 1;  % Keeps track of the permutation blocks in the subfunc.
idx = 1:N/2; % Index vectors for subfunc.
idx2 = idx;
WV = 1:K;  % Initial WV.
lim = 0;  % Sets the limit for working index.
inc = K;  % Controls which element of WV is being worked on.
cnt = 1;  % Keeps track of which block we are in.  See loop below.
BC = prod(1:N)/(prod(1:(N-K))); % Number of permutations.
L = prod(1:K);   % Size of the blocks.
CNT = 0;  % Counter for blocks.
Floop = BC - prod(1:K);  % Length of blocks.
A2 = (N-K+1):N;  % Seed for the final block.
B2 = 1:K;
ii = 1;

if K==1
    C = @nestfunc2;  % Return argument is a func handle.
else
    C = @nestfunc;
end

    function B = nestfunc2  
    % A handle to this function is passed back from the main func if K=1.    
        if WV > N
            B = 1;
            WV = 2;
            return
        end
       B = WV(1);
       WV = WV + 1;
    end

    function B = nestfunc
    % A handle to this function is passed back from the main func.
            if ii<=Floop
                if CNT == 0
                    for jj = 1:inc
                        WV(K + jj - inc) = lim + jj;
                    end
                    % This is the first combination.  We will permute it
                    % below for the rest of the calls in this block.
                    B = WV;
                    cnt = cnt + L;

                    if lim<(N-inc)
                        inc = 0;  % Reset this guy.
                    end

                    TMP = WV;  % This serves as seed for perm index.
                    inc = inc+1;  % Increment the counter.
                    lim = WV(K+1-inc);  % Limit for working index.
                    CNT = 1;
                    G = 1:K; % Seed for nextp subfunc
                    op = 1;
                    blk = 1;
                    idx = 1:N/2;
                    idx2 = idx;
                    ii = ii + 1; % "Loop" index.
                else
                    % Permute the seed.
                    [G,op,blk,idx,idx2] = nextp(G,op,blk,idx,idx2,K);
                    B = TMP(G);  % Index into current combination.
                    CNT = CNT + 1;

                    if CNT==(L) % Goes back to if for next seed (combin).
                        CNT = 0;
                    end
                    
                    ii = ii + 1;
                end
            else
                if ii == Floop+1  % We are at the last block
                    op = 1;  % Re-initialize for subfunc.
                    blk = 1;
                    idx = 1:N/2;
                    idx2 = idx;
                    B = A2;  % Seed for this last block.
                    cnt = cnt + 1;
                    ii = ii + 1;
                    return
                elseif ii == BC + 1  % Time to start over.
                    WV = 1:K;  % Seed for first block.
                    op = 1;  % Re-initialize for subfunc.
                    blk = 1;
                    idx = 1:N/2;
                    idx2 = idx;
                    inc = 1; % Reset the incrementer. 
                    lim = WV(K+1-inc); % And the lim.
                    cnt = L + 1;  % Reset block counter.
                    CNT = 1;
                    ii = 2;  % Reset "Loop" index.
                    B = WV;
                    TMP = WV;
                    B2 = 1:K;
                    G = 1:K;
                    return
                end
                % Permute seed the seed.
                [B2,op,blk,idx,idx2] = nextp(B2,op,blk,idx,idx2,K);
                B = A2(B2);
                cnt = cnt + 1;
                ii = ii + 1;
            end 
    end
end





function [x,op,blk,idx,idx2] = nextp(x,op,blk,idx,idx2,K) 
% Delivers one permutation at a time.  This is a modification of an
% algorithm attributed to H. F. Trotter.
% x = 1:4; 
% op = 1; 
% blk = 1;  
% idx = 1:length(x)/2; 
% idx2 = idx;  
% C(1,:) = x;
% for jj = 2:factorial(length(x))
%    [x,op,blk,idx,idx2] = nextp(x,op,blk,idx,idx2,length(x));
%    C(jj,:) = x;
% end

if op<K
    np = op + 1; % Index of where the 1 goes.
    x(op) = x(np);  % Here we are just doing the switcheroo on adjacents. 
    x(np) = 1;  % np is the current position of the 1.
    op = np;  % op is the old position of the 1, for next time.
    return
else
    x(K) = x(1);  % Here we are switching the endpoints of the vect.
    x(1) = 1;
    op = 1;  % Reset to the first position.
    blk = blk + 1;  % Keep track of this block. Reset every N*(N-1)th call.

    if blk<K % Not through with this block yet.
        return
    end

    blk = 1;  % If here, we need to mix internal elements of the vect.
    low = 2;  % Start with the second element.
    upp = K - 1;  % And the next to last element.

    while true  % We always break this loop.  
        % In here, on every N*(N-1)th call, we are mixing internal elems.
        % The number of times through the while loop is usually very small,
        % even for large N most calls will go through less than 3 times.  
        cur = idx(low);  % Holds the current index to switch.

        if cur==upp
            np = low;
            idx2(low) = idx2(low) + 1;
        else
            np = cur + 1;  % For next iter.
        end

        tmp = x(cur);  % Switcheroo.
        x(cur) = x(np);
        x(np) = tmp;
        idx(low) = np;

        if idx2(low)<upp
            break  % Out of while loop, we've mixed them enough.
        end

        idx2(low) = low;
        low = low + 1; % These two march towards each other.
        upp = upp - 1;
    end
end
end

Contact us at files@mathworks.com