[SPOILER] Quiz #23 (and a solution that outputs in exactly the same order as the original :-)

yves <[email protected]>
Newsgroups gmane.comp.lang.perl.qotw.discuss
Message-ID <[email protected]>
I didnt see any mention of anything like two solutions I came up with
for Quiz #23 so i thought id send them through.

My first attempt used binary to generate the patterns and then
validated each appropriate bitstring. Basically we only(!?) need to
iterate the odd integers
0..2**$N-1 (where $N=2n) to find all the possibilitie and then walking
each ones bits to keep track of whether its got a valid pattern .
Obviously this isnt going to scale that well (it takes 13 seconds to
n=10) but it works nicely for smaller numbers. And also paints an
interesting picture. I used 0='(' and 1=')' which means that it
outputs the exact order of the one posted by MJD.

The second attempt uses a queue and perls regex engine to recurse over
the string and build all the possible substrings that can be built by
turning )( into (). I have a feeling that this search can be pruned
better to get better times but I havent worked out how.

For testing purposes and stuff I put the two solutions into their own
subroutines which return a list. I suppose in some respects this isnt
that smart as for larger N it would be a lot more memory efficient to
just print them out straight away. Which actually highlights a
strength in the _int version and weakness in the _rex version. The
latter needs to maintain a hash of previously seen solutions so is
unsuitable for larger N, wheras the _int solution has almost no memory
overhead (were it not building an @array)

Heres the code with output for n=0..4, the number in the parens is the
integer value the pattern represents.

Anyway, this is my first posting here, i hope I have done this right
in terms of ettiquette and this is interesting to you all.

demerphq

#!perl
use strict;
use warnings;

sub parens_permute_int {
    my $n=shift;
    return unless $n;
    my $verbose=shift;
    my $N=2*$n;
    my @paren=qw[ ( ) ];
    my $count=0;
    my @results;
    NUM:
    for(my $int=2**$n-1;$int<2**($N-1);$int+=2) {
        my @bit=split //,sprintf "%0*b",$N,$int;
        my $sum=0;
        BIT:
        for my $pos (0..$#bit) {
            my $digit=$bit[$pos];
            $sum+=$digit ? -1 : 1;
            next NUM if $sum<0          # too many close brackets
                     or $sum>@bit-$pos; # too many open brackets
        }
        if ($verbose) {
            push @results,sprintf "%4d(%4d) %s",++$count,$int,
                    join "",map $paren[$_],@bit;
        } else {
            push @results,join '',map($paren[$_],@bit);
        }
    }
    return @results
}

sub parens_permute_rex {
    my $n=shift;
    return unless $n;
    our (@pattern,%seen);
    local @pattern=("()" x $n);
    local %seen=(@pattern,1);
    my $cur=0;
    while ($cur<@pattern) {
        $pattern[$cur++]=~m/^\((.*?)\)(.*)\((.*?)\)$
          (??{my $str="($1($2)$3)";
              push @pattern,$str unless $seen{$str}++; 'a'})/x;
    }
    #print "$_:$seen{$_}\n" for reverse sort keys %seen;
    return @pattern;
}

unless (@ARGV) {
    warn "Testing.\n";
    for my $n (0..4) {
        print "For \$n=$n:\n";
        my @res1=parens_permute_int($n,1);
        my @res2=sort(parens_permute_rex($n));
        my $num=$#res1>=$#res2 ? $#res1 : $#res2;
        foreach my $i (0..$num) {
            print $res1[$i]=~/\Q$res2[$i]\E/ ? '= ' 
                   : '? ',$res1[$i]||'missing',"  # 
",$res2[$i]||'missing',"\n";
        }
        print "----\n";
    }
} else {
    my ($n,$ver)=@ARGV;
    if ($ver and $ver=~/int/) {
      parens_permute_int();
    } else {
      parens_permute_rex();
    }
}

__END__
Testing.
For $n=0:
----
For $n=1:
=    1(   1) ()  #  ()
----
For $n=2:
=    1(   3) (())  #  (())
=    2(   5) ()()  #  ()()
----
For $n=3:
=    1(   7) ((()))  #  ((()))
=    2(  11) (()())  #  (()())
=    3(  13) (())()  #  (())()
=    4(  19) ()(())  #  ()(())
=    5(  21) ()()()  #  ()()()
----
For $n=4:
=    1(  15) (((())))  #  (((())))
=    2(  23) ((()()))  #  ((()()))
=    3(  27) ((())())  #  ((())())
=    4(  29) ((()))()  #  ((()))()
=    5(  39) (()(()))  #  (()(()))
=    6(  43) (()()())  #  (()()())
=    7(  45) (()())()  #  (()())()
=    8(  51) (())(())  #  (())(())
=    9(  53) (())()()  #  (())()()
=   10(  71) ()((()))  #  ()((()))
=   11(  75) ()(()())  #  ()(()())
=   12(  77) ()(())()  #  ()(())()
=   13(  83) ()()(())  #  ()()(())
=   14(  85) ()()()()  #  ()()()()
----




-- 
First they ignore you, then they laugh at you, then they fight you,
then you win.
  +Gandhi
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.