use List::Util 'min'; use POSIX 'ceil'; sub num_ways { my ($N, $S, $T) = @_; return 1 if $S == 0 and $T == 0; return 0 if $N < 1 or $S < 1 or $T < 1; my $min = (2*$T-$S+$S**2)/(2*$S); ## more correctly, ceil() of this my $max = min( $T-(($S-1)*$S/2), $N ); my $sum = 0; for my $K ($min .. $max) { $sum += num_ways( $K-1, $S-1, $T-$K ); } return $sum; } use Memoize; memoize 'num_ways'; print num_ways(100, 10, 667), $/; #### sub num_ways { my ($N, $S, $T, $callback, @so_far) = @_; return $callback->(@so_far) if $S == 0 and $T == 0; return if $N < 1 or $S < 1 or $T < 1; my $min = (2*$T-$S+$S**2)/(2*$S); my $max = min( $T-(($S-1)*$S/2), $N ); for my $K ($min .. $max) { num_ways( $K-1, $S-1, $T-$K, $callback, (@so_far, $K) ); } } num_ways( 100, 10, 667, sub { print "@_\n" } );