Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

permutation algorithm

by Basilides (Friar)
on Jul 11, 2002 at 15:36 UTC ( [id://181047]=perlquestion: print w/replies, xml ) Need Help??

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

not strictly a perl-specific query, i'm afraid, but can anyone help me with an algorithm:

i've got a variable length array of numbers, and i need to mark which combinations of them add up to zero

eg say the array (which is zero-based) is

-25, 14, 50, 20, -7, -8, -10

then elements 0, 2, 4, 5 & 6 add up to zero, but no other combinations do (except the same thing in a different order, which i don't want to count again).

how do i structure my loops so that it tests every possible permutation?

Replies are listed 'Best First'.
Re: permuation algorithm
by japhy (Canon) on Jul 11, 2002 at 15:52 UTC
    Take a binary approach:
    my @n = (-25, 14, 50, 20, -7, -8, -10); my $max = 2**@n; for (my $i = 0; $i < $max; ++$i) { my $sum = 0; $sum += $n[$_] for grep $i & 2**$_, 0 .. $#n; if ($sum == 0) { my ($bits, @used) = $i; while ($bits) { my $high_bit = int(log($bits)/log(2)); push @used, $n[$high_bit]; $bits &= ~(2**$high_bit); } print "[@used] = 0\n"; } }
    Another approach to the end is thus:
    my ($bits, $j, @used) = ($i, 0); while ($bits) { push @used, $n[$j] if $bits & 1; $bits >>= 1, ++$j; }
    which could also be written as a for loop:
    for ( my ($bits, $j, @used) = ($i, 0); $bits; $bits >>= 1, ++$j ) { push @used, $n[$j] if $bits & 1; }

    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

Re: permuation algorithm
by Abigail-II (Bishop) on Jul 11, 2002 at 16:21 UTC
    Use backtracking, aka the Perl regex machine. Give your set of number as command line arguments to the following program:
    #!/usr/bin/perl use strict; use warnings 'all'; use re 'eval'; use vars '%seen'; my $regex = <<'--'; (?{ local $x = 0 }) (?{ local @x = (-1) x @ARGV }) -- my $i = 0; foreach my $number (@ARGV) { $regex .= "(?:(?{ local \$x = \$x + $number; local \$x [$i] = $i } +)|)\n"; $i ++ } $regex .= <<'--'; (?(?{ $x }) fail | ) (?(?{ grep {$_ >= 0} @x }) | fail) (?{ local $str = join " + " => @ARGV [grep {$_ >= 0} @x] }) (?(?{ $seen {$str} ++ }) fail | ) (?(?{ print "$str = 0\n" }) fail | ) -- "" =~ /$regex/x;

    Abigail

      ....eagerly awaiting the tutorial.


      Anyone know of an abbottoire going cheap?

Re: permuation algorithm
by FoxtrotUniform (Prior) on Jul 11, 2002 at 15:40 UTC

    You're looking for combinations, not permutations, and Knuth recently wrote a rather complete paper on the subject (about halfway down the page).

    --
    The hell with paco, vote for Erudil!
    :wq

Re: permuation algorithm
by broquaint (Abbot) on Jul 11, 2002 at 15:42 UTC
    I'm no expert on the matter of permutations but there are plenty of resources on The Monastery about the subject of permutations and there's even a module on CPAN under the name of Algorithm::Permute which might do the job for you, or failing that the source should be a good reference.
    HTH

    _________
    broquaint

Re: permutation algorithm
by runrig (Abbot) on Jul 11, 2002 at 20:33 UTC
    There's a node on this sort of thing here from a monk who is gone but not forgotten. All you have to do is add up the numbers for every iteration of the iterator, and, if you want, skip any combinations which contain duplicates.
Re: permutation algorithm
by fglock (Vicar) on Jul 11, 2002 at 22:50 UTC

    my $0.02;

    use strict; my @list = (1,2,3); my @result = (); for my $elem (@list) { push @result, [ @$_, $elem ] for @{ [ @result ] }; push @result, [ $elem ]; } use Data::Dumper; print Dumper @result;

    Got:

    $VAR1 = [ 1 ]; $VAR2 = [ 1, 2 ]; $VAR3 = [ 2 ]; $VAR4 = [ 1, 3 ]; $VAR5 = [ 1, 2, 3 ]; $VAR6 = [ 2, 3 ]; $VAR7 = [ 3 ];
Re: permutation algorithm
by fglock (Vicar) on Jul 12, 2002 at 13:01 UTC

    Here is the loop structure to your problem:

    use strict; my @list = (-25, 14, 50, 20, -7, -8, -10); my @result = (); my $sum; for my $elem (@list) { for (@{[@result]}) { push @result, [ @$_, $elem ]; $sum = eval join '+' => @{$result[-1]}; print join(',', @{$result[-1]}), "\n" unless $sum; } push @result, [ $elem ]; print $elem, "\n" unless $elem; }

    Result:

    -25,50,-7,-8,-10

    You will need some additional work in order to obtain array indexes instead of values.

    How it works:

  • In first pass, it will ignore the inner for and it will push [ -25 ] into @result. Result is [ [-25] ]
  • In second pass, it will push 14 into a copy of what it already has, and then it will push [ 14 ]. Result is: [ [-25], [-25,14], [14] ]
  • In second pass, it will push 50 into a copy of what it already has, and then it will push [ 50 ]. Result is: [ [-25], [-25,14], [14], [-25,50], [-25,14,50], [14,50], [50] ]
  • While it does that, it will print whatever combinations that sum zero.
  • That's it!

      That's a memory hungry program! For 10 elements, you will create 1024 arrays, 20 elements give you 1048576 arrays, and 30 elements give you 1073741824 arrays.

      The problem as given is in NP (and probably not in P), but that means it's in P-SPACE. Your solution however uses exponential memory.

      Abigail

        Mmmm, I was trying to solve the problem without a recursion.

        You are right. I am building the tree, instead of traversing it. We'd better use a recursion for this.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://181047]
Approved by lemming
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (5)
As of 2024-04-19 06:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found