ibm1620 has asked for the wisdom of the Perl Monks concerning the following question:

I want to produce combinations of K characters taken from a set of N characters, without permutations, and "on demand", rather than all at once. Is there a CPAN module that might provide a class from which I could instantiate an object with the set of N characters and the value K, and provide a next_combination() method that would return the succeeding combination?
  • Comment on CPAN module to return successive combinations of n things taken k at a time

Replies are listed 'Best First'.
Re: CPAN module to return successive combinations of n things taken k at a time
by LanX (Saint) on Feb 26, 2015 at 04:14 UTC
      Actually, Charlie, I did look, but I ended up in a mathematics section that was completely over my head. Neither the word "permutations" nor "iterator" occurred to me. But thanks for the link.
        For the record:
        #!/usr/bin/env perl use strict; use warnings; use 5.010; use Algorithm::Combinatorics qw(combinations); my @data = qw(a b c d e); # scalar context gives an iterator my $iter = combinations(\@data, 3); while (my $p = $iter->next) { say (join '', @$p); }
        Produces:
        abc abd abe acd ace ade bcd bce bde cde
        As required.
        > Neither the word "permutations" nor "iterator" occurred to me.

        Dear Bill, please see my update.

        The results are even better. :)

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)

        PS: Je suis Charlie!

Re: CPAN module to return successive combinations of n things taken k at a time (NestedLoops)
by tye (Sage) on Feb 26, 2015 at 04:14 UTC

    Not obvious, but probably how I'd do it just because I wouldn't have to think about it. And I'm sure somebody else will soon point out a combinations module that has a version of 'pick' built in already.

    #!/usr/bin/perl -l use strict; use Algorithm::Loops 'NestedLoops'; sub pick { my( $k, @chars ) = @_; my $idx = NestedLoops( [ ( sub { [ ( @_ ? 1+$_ : 0 ) .. @chars-$k+@_ ] } ) x $k ] ); return sub { @chars[ $idx->() ] }; } @ARGV = ( 3, 'a'..'e' ) if ! @ARGV; my $iter = pick( @ARGV ); while( my @subset = $iter->() ) { print "@subset"; }

    Update: Oh, you didn't say whether or not you wanted "with replacement". It is slightly simpler with replacement:

    ( sub { [ ( @_ ? $_ : 0 ) .. $#chars ] } ) x $k

    Update: I decided to combine the two and support shortcuts on the command-line. Code available via the "Select" (or "Download") link.

    $ comb 10 10 | wc -l # with replacement 92378 $ comb -10 19 | wc -l # without replacement 92378

    - tye        

Re: CPAN module to return successive combinations of n things taken k at a time
by Athanasius (Cardinal) on Feb 26, 2015 at 04:29 UTC

    Hello ibm1620,

    Another option: Math::Combinatorics:

    #! perl use strict; use warnings; use Math::Combinatorics; my @n = qw(a e i o u); my $com = Math::Combinatorics->new ( count => 2, data => [@n], ); my $s = join(' ', @n); print '-' x (24 + length $s), "\n"; print "Combinations of 2 from: $s\n"; print '-' x (24 + length $s), "\n"; while (my @combo = $com->next_combination) { print join(' ', @combo), "\n"; }

    (Code adapted from the documentation.)

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: CPAN module to return successive combinations of n things taken k at a time
by atcroft (Abbot) on Feb 26, 2015 at 06:58 UTC
Re: CPAN module to return successive combinations of n things taken k at a time
by BrowserUk (Patriarch) on Feb 26, 2015 at 05:56 UTC

    Algorithm::Combinatorics:

    #! perl -slw use strict; use Algorithm::Combinatorics qw[ combinations combinations_with_repeti +tion ]; our $K //= 3; our $ALPHA //= join '', 'a'..'d'; my @alpha = split '', $ALPHA; print "$K from [@alpha] without repetition"; my $iter = combinations( \@alpha, $K ); print "@$_" while $_= $iter->next; print "$K from [@alpha] with repetition"; $iter = combinations_with_repetition( \@alpha, $K ); print "@$_" while $_= $iter->next; __END__ C:\test>1117899 3 from [a b c d] without repetition a b c a b d a c d b c d 3 from [a b c d] with repetition a a a a a b a a c a a d a b b a b c a b d a c c a c d a d d b b b b b c b b d b c c b c d b d d c c c c c d c d d d d d

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". I'm with torvalds on this
    In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked
Re: CPAN module to return successive combinations of n things taken k at a time
by hdb (Monsignor) on Feb 28, 2015 at 08:44 UTC

    Here is an explicit solution w/o using a module that uses a recursive procedure to calculate the next combination:

    use strict; use warnings; sub first { ( (1)x$_[1], (0)x($_[0]-$_[1]) ); } sub nextstep { my @p = @_; my $i = $#p; $i-- while $i>=0 and !$p[$i]; # find leading 1 return () if $i<0; # only 0s => nothing to do my @sub; if( $i==0 or not( @sub = nextstep( @p[0..($i-1)] ) ) ) { return () if $i==$#p; $p[$i] = 0; $p[$i+1] = 1; @sub = first( $i, scalar grep { $_ == 1 } @p[0..($i-1)] ); } return ( @sub, @p[$i..$#p] ); } my @data = 'a'..'e'; my @logical = first( scalar(@data), 3 ); while( @logical ) { print "@logical: ", @data[ grep { $logical[$_] } 0..$#logical ], "\n +"; @logical = nextstep( @logical ) }

    While I think it works, give the time it took me to get it right and the somewhat tricky logic, I have come to the conclusion, that using a module is the better alternative.