diff options
Diffstat (limited to 'challenge-117/polettix/perl/ch-2.pl')
| -rw-r--r-- | challenge-117/polettix/perl/ch-2.pl | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/challenge-117/polettix/perl/ch-2.pl b/challenge-117/polettix/perl/ch-2.pl new file mode 100644 index 0000000000..972a812b5f --- /dev/null +++ b/challenge-117/polettix/perl/ch-2.pl @@ -0,0 +1,148 @@ +#!/usr/bin/env perl +use 5.024; +use warnings; +use experimental qw< postderef signatures >; +no warnings qw< experimental::postderef experimental::signatures >; + +use constant THRESHOLD => 10; + +sub find_possible_paths ($N) { + my @solution = (['']); + for my $i (1 .. $N) { + my @new_iteration = []; + for my $j (0 .. $#solution) { + my $previous = $solution[$j]; + my $left = $new_iteration[$j]; + my $right = $new_iteration[$j + 1] = []; + for my $p ($previous->@*) { + push $left->@*, $p . 'L'; + push $right->@*, $p . 'R'; + } + for my $p ($left->@*) { + push $right->@*, $p . 'H'; + } + } + @solution = @new_iteration; + } + return $solution[-1]->@*; +} + +sub combinations_iterator ($k, @items) { + my @indexes = (0 .. ($k - 1)); + my $n = @items; + return sub { + return unless @indexes; + my (@combination, @remaining); + my $j = 0; + for my $i (0 .. ($n - 1)) { + if ($j < $k && $i == $indexes[$j]) { + push @combination, $items[$i]; + ++$j; + } + else { + push @remaining, $items[$i]; + } + } + for my $incc (reverse(-1, 0 .. ($k - 1))) { + if ($incc < 0) { + @indexes = (); # finished! + } + elsif ((my $v = $indexes[$incc]) < $incc - $k + $n) { + $indexes[$_] = ++$v for $incc .. ($k - 1); + last; + } + } + return \@combination; + } +} + +sub basic_case_iterator_longer ($N) { + my $N2 = 2 * $N; + my $cs; + return sub { + $cs //= combinations_iterator($N, 0 .. $N2 - 1); + CANDIDATE: + while (my $Ls = $cs->()) { + my @sequence = ('H') x $N2; + @sequence[$Ls->@*] = ('L') x $N; + my $count = 0; + for my $item (@sequence) { + $count += $item eq 'L' ? 1 : -1; + next CANDIDATE if $count < 0; + } + return join '', @sequence; + } + return; + }; +} + +sub basic_case_iterator ($N) { + --$N; + my $N2 = 2 * $N; + my $cs; + return sub { + $cs //= combinations_iterator($N, 0 .. $N2 - 1); + CANDIDATE: + while (my $Ls = $cs->()) { + my @sequence = ('H') x $N2; + @sequence[$Ls->@*] = ('L') x $N; + my $count = 1; # we will force starting with an L + for my $item (@sequence) { + $count += $item eq 'L' ? 1 : -1; + next CANDIDATE if $count < 0; + } + return join '', 'L', @sequence, 'H'; + } + return; + }; +} + +sub expand_with_Rs_iterator ($sequence) { + my $indexes; + my @parts; + my ($i, $n, $max); + return sub { + if (! $indexes) { # initialize + @parts = grep {length} split m{(LH)}mxs, $sequence; + $indexes = [grep {$parts[$_] eq 'LH'} 0 .. $#parts]; + $n = $indexes->@*; + $max = 0; + $max = ($max << 1) | 1 for 1 .. $n; + $i = 0; + return $sequence; + } + return if $i >= $max; + ++$i; + my @Rs = split m{}mxs, sprintf "%0${n}b", $i; + my @copy = @parts; + for my $j (0 .. $#Rs) { + next unless $Rs[$j]; + $copy[$indexes->[$j]] = 'R'; + } + return join '', @copy; + }; +} + +sub find_possible_paths_iterator ($N) { + my ($basic_it, $fit); + return sub { + $basic_it //= basic_case_iterator($N); + while ('necessary') { + $fit //= expand_with_Rs_iterator($basic_it->() // return); + if (my $item = $fit->()) { return $item } + $fit = undef; + } + }; +} + +my $n = shift // 2; +my $use_iterator = $ENV{USE_ITERATOR} ? 1 + : defined($ENV{USE_ITERATOR}) ? 0 + : $n > THRESHOLD; +if ($use_iterator) { + my $it = find_possible_paths_iterator($n); + while (my $c = $it->()) { say $c } +} +else { + say for find_possible_paths($n); +} |
