P(m,n) = {
1 if m or n = 1,
P(m,m) if m < n,
1 + P(m,m-1) if m = n > 1,
P(m,n-1) + P(m-n,n) if m > n > 1
}
####
sub partition {
my ( $m, $n ) = @_;
die "bad inputs" if $m <= 0 or $n <= 0;
print "P($m,$n) = ";
if ( $m == 1 or $n == 1 ) {
say 1;
return 1;
}
if ( $m < $n ) {
say "P($m,$m)";
return partition( $m, $m );
}
if ( $m == $n ) {
say "1 + P($m,", $m - 1, ")";
return 1 + partition( $m, $m - 1 );
}
if ( $m > $n ) {
say "P($m,", $n - 1, ") + P(", $m - $n, ",$n)";
return partition( $m, $n - 1 ) + partition( $m - $n, $n );
}
die "impossible!";
}
MAIN: {
my ( $m, $n ) = @ARGV;
$n ||= $m;
say partition( $m, $n );
}
####
sub P {
# Returns strings!
my ( $m, $n ) = @_;
if ( $m == 1 or $n == 1 ) {
return 1;
}
if ( $m < $n ) {
return "P($m,$m)";
}
if ( $m == $n ) {
return sprintf( "1 + P(%i,%i)", $m, $m - 1 );
}
if ( $m > $n ) {
return sprintf( "P(%i,%i) + P(%i,%i)", $m, $n - 1, $m - $n, $n );
}
die "impossible!";
}
MAIN: {
local $_ = "P($m,$n)";
print;
while ( /\D/ ) {
# Put ints up front
1 while s/^(.+\)) \+ (\d+)/$2 + $1/;
# Add the ints
1 while s/(\d+) \+ (\d+)/$1 + $2/eg;
# Evaluate *one* level at a time
s/(P\(\d+,\d+\))/$1/eeg;
printf( "\n = %s", $_ );
}
}
####
$ partition.pl 7
P(7,7)
= 1 + P(7,6)
= 1 + P(7,5) + P(1,6)
= 1 + P(7,4) + P(2,5) + 1
= 2 + P(7,3) + P(3,4) + P(2,2)
= 2 + P(7,2) + P(4,3) + P(3,3) + 1 + P(2,1)
= 3 + P(7,1) + P(5,2) + P(4,2) + P(1,3) + 1 + P(3,2) + 1
= 5 + 1 + P(5,1) + P(3,2) + P(4,1) + P(2,2) + 1 + P(3,1) + P(1,2)
= 7 + 1 + P(3,1) + P(1,2) + 1 + 1 + P(2,1) + 1 + 1
= 12 + 1 + 1 + 1
= 15