Math-DyckWords

 view release on metacpan or  search on metacpan

lib/Math/DyckWords.pm  view on Meta::CPAN

#######################################################################
# $Id: DyckWords.pm,v 1.2 2010/04/14 03:41:06 mmertel Exp $

=head1 NAME

Math::DyckWords - Perl module for generating Dyck words. Dyck words
are named after the mathematician Walther von Dyck.

=head1 SYNOPSIS

  use Math::DyckWords qw( dyck_words_by_lex
                          dyck_words_by_position
                          dyck_words_by_swap
                          ranking
                          unranking
                          catalan_number );

  @words = dyck_words_by_lex( 4 );
  @words = dyck_words_by_position( 4 );
  @words = dyck_words_by_swap( 4 );
  $rank  = ranking( '01010101' );
  $word  = unranking( 3, 2 );

=head1 DESCRIPTION

Dyck words are even numbered string of X's and Y's, or 0's and 1's,
or any other binary alphabet for that matter, such that no initial
segment has more Y's or 1's.  The following are the Dyck words of
length 2n where n = 3:

    000111
    010011
    010101
    001101
    001011

Another common use of Dyck words is in dealing with the balanced
parenthesis problem. Substituting the left and right parentheses
for the 0's an 1's listed above we have:

    ((()))
    ()(())
    ()()()
    (())()
    (()())

There is also a relationship between Dyck words and Catalan numbers.
Catalan numbers have many applications in combinatorics and consists
of a sequence of ever increasing integers following the formula:

    (2n)!/(n!(n+1)!)

The first few numbers in the Catalan sequence are:

    1, 1, 2, 5, 14, 132, 429, 1430, 4862, 16796, 58786, 208012

The relationship between Dyck words and the Catalan sequence can
be easily seen as the nth Catalan number is equal to the number of
permutations, or unique Dyck words of length 2n. For example,
the 3rd Catalan number, using a zero index, is 5. This is the same
number of Dyck words of length 2n where n = 3.

The algorithms in this module are based on those presented in the
scholarly paper "Generating and ranking of Dyck words" by Zoltan Kasa
available on-line at http://arxiv4.library.cornell.edu/pdf/1002.2625,
and the provide three different Dyck word generators - lexigraphical,
positional, and one that generates Dyck words by swapping characters.

=head1 EXPORT

None by default.

=cut

package Math::DyckWords;

use 5.006;
use strict;
use warnings;

use Carp;
use Data::Dumper;
use Math::BigInt;
use Exporter;

our $VERSION = '0.03';
our @ISA = qw( Exporter );
our @EXPORT_OK = qw( dyck_words_by_lex
                     dyck_words_by_position
                     dyck_words_by_swap
                     ranking
                     unranking
                     catalan_number );

=head1 FUNCTIONS

=over

=item dyck_words_by_lex( $n )

This algorithm returns a list of all Dyck words of length 2n in ascending
lexicographic order, i.e. 000111, 001011, 001101, 010011, 010101

=back

=cut

my @words;

lib/Math/DyckWords.pm  view on Meta::CPAN


    my @c = ( 2 );
    my $n = scalar @b;
    for( my $j = 2; $j <= $n; $j++ ) {
	    $c[ $j - 1 ] = $b[ $j - 2 ] + 1 > $j * 2
	                 ? $b[ $j - 2 ] + 1
                 : $j * 2;
    }
    my $nr = 1;
    for( my $i = 1; $i <= $n - 1; $i++ ) {
	    for( my $j = $c[ $i - 1]; $j <= $b[ $i - 1] - 1; $j++ ) {
	        $nr = $nr + monotonic_path_count( $n, $n - $i, $n + $i - $j );
	    }
    }
    return $nr;
}

=over

=item unranking( $n, $r )

This function returns the rank $r Dyck word of length $n.

=back

=cut

sub unranking( $$ ) {
    my ( $n, $nr ) = @_;

    # initialize the dyck word to all '0'
    my @b = ( '0' x ( $n * 2 ) );

    $nr--;

    for( my $i = 1; $i <= $n; $i++ ) {
	    $b[ $i ] = $b[ $i - 1 ] + 1 > $i * 2
                 ? $b[ $i - 1 ] + 1
                 : $i * 2;

	    my $j  = $n + $i - $b[ $i ];
	    my $np = monotonic_path_count( $n, $n - $i, $j );

	    while( $nr >= $np && ( $b[ $i ] < $n + $i ) ) {
	        $nr      = $nr - $np;
	        $b[ $i ] = $b[ $i ] + 1;
	        $j       = $j - 1;
            $np      = monotonic_path_count( $n, $n - $i, $j );
	    }
    }
    # discard the zeroth element of the list of positions
    shift @b;

    return translate_positions( @b );
}

=over

=item catalan_number( $n )

Using the formula - (2n)!/(n!(n+1)!) - this function returns the
corresponding number $n from the Catalan sequence.

=back

=cut

sub catalan_number( $ ) {
    my $x = shift;

    my $X = Math::BigInt->new( $x );
    my $Y = $X->copy;
    my $Z = $X->copy;

    return $X->bmul( 2 )->bfac->bdiv(
        $Y->bfac->bmul( $Z->badd( 1 )->bfac )
    );
}

1;



( run in 2.049 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )