#!/usr/bin/perl -w use strict; sub genPermuTranspOrder { # @e is the original (unpermuted) list of elements. my @e= @_; # @o is the order we want to output the elements. # So @e[@o] is the permuted list. my @o= (0..$#e); # @p is the position of each element in @o, # that is, @o = (0..$#e)[@p] my @p= (0..$#e); # Note that it is also true that @p = (0..$#e)[@o]. # $d[$n] is the direction we are moving $e[$n]. my @d= (-1) x @e; # We return a code reference (closure) that each time # it is called will return the next permutation of @e. # Note that the first time this is called, it permutes # @e one step, so the calling code must output the # original @e before calling the iterator. return sub { # Start by assuming we'll move the last element of @e. my $n= $#e; my $j; for(;;) { # If we move this one, we'd set $p[$n] += $d[$n]. # So $j is the possible new value for $p[$n]. $j= $p[$n] + $d[$n]; # That is okay if $j is in range and # we aren't swapping with an element later in @e. last if 0 <= $j && $j <= $#e && $o[$j] < $n; # It wasn't okay. So move this element # in the other direction next time. $d[$n]= -$d[$n]; # And move to the left in @e. --$n; # If we are to the first element in @e # then we are done. return if $n < 1; } # $p[$n] is the (permuted) position of the $n'th # element ($e[$n]) we wish to swap in the direction # of $d[$n]. So we want to swap $o[ $p[$n] ] with # $o[ $p[$n]+$d[$n] ]. $j is already that second # offset so we'll set $i to be the first offset # and later we'll swap @o[$i,$j]. my $i= $p[$n]; # We need to keep @o = (0..$#e)[@p], so we need # swap corresponding elements of @p to match @o. # We should swap @p[@o[$i,$j]], but $o[$i] is $n. @p[ $n, $o[$j] ]= @p[ $o[$j], $n ]; @o[$j,$i]= @o[$i,$j]; # To confuse your instructor, the commented # line produces the permutations in a different # and strange "transposition order". # return @e[@p]; return @e[@o]; }; } # Take $ARGV[0] (default 5) letters from 'a' to permute my @e= (0,'a'..'z','A'..'Z')[1..($ARGV[0]||5)]; my $iter= genPermuTranspOrder( @e ); do { print "@e\n"; } while( @e= $iter->() );