aboutsummaryrefslogtreecommitdiff
path: root/challenge-119/e-choroba/perl/ch-2.pl
blob: c3803842514e95c5a20011c15dd6ccb6a43c6fed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

sub seq_naive {
    my ($n) = @_;
    my $e = 0;
    while ($n--) {
        1 while ++$e =~ /[^123]|11/;
    }
    return $e
}

use PDL;
sub _of_length {
    my ($n) = @_;
    my $recurrence = pdl([[0, 1], [2, 2]]);
    my $m = $recurrence;
    $m x= $recurrence for 0 .. $n - 2;
    $m x= pdl([[1], [3]]);
    return $m->at(0, 0)
}

sub seq_matrix {
    my ($n) = @_;
    my $l = 1;
    my $predecessors = 0;
    my $o = 0;
    do {
        $o = _of_length($l++);
        $predecessors += $o;
    } while $predecessors < $n;

    my $element;
    if ($n < $predecessors - $o / 2) {
        $element = '3' x ($l - 2);
        $predecessors -= $o;
        until ($predecessors++ == $n) {
            1 while ++$element =~ /[^123]|11/;
        }
    } else {
        $element = '3' x ($l - 1);
        until ($predecessors-- == $n) {
            1 while --$element =~ /[^123]|11/;
        }

    }
    return $element
}

use Test2::V0 qw{ plan is };
plan(31);

is seq_naive(5),    13, 'Example 1 naive';
is seq_naive(10),   32, 'Example 2 naive';
is seq_naive(60), 2223, 'Example 3 naive';

is seq_matrix(5),    13, 'Example 1 matrix';
is seq_matrix(10),   32, 'Example 2 matrix';
is seq_matrix(60), 2223, 'Example 3 matrix';

my @inputs = (1 .. 20, 100, 250, 500, 750, 1000);
is seq_matrix($_), seq_naive($_), "same $_" for @inputs;

use Benchmark qw{ cmpthese };
cmpthese(-3, {
    naive  => sub { seq_naive($_) for @inputs },
    matrix => sub { seq_matrix($_) for @inputs },
});

=head1 Sequence without 1-on-1

=head2 Using a matrix

Let's call the sequence without 1-on-1 "Sw1".

Let's consider a sequence s[1], s[2], s[3], ... where each s[i] says how many
elements of length i exist in Sw1. This sequence can be computed from a matrix,
using

  | s[i]   |   | 0 1 |^i-2   |1|
  | s[i+1] | = | 2 2 |     x |3|

If we define

  s'[i] = s[1] + s[2] + ... + s[i-1]

then s'[i] tells us how many elements in the sequence Sw1 precedes the first
element of length i.

Calculating Sw1[n] can be a bit faster now: find the i such that

  s'[i] <= n <= s'[i+1]

If n is closer to s'[i] than s'[i+1], start with '3' x (i-1) and "increment" it
(n - s'[i]) times. Otherwise, start with '3' x i and "decrement" it (s'[i+1] -
n) times; where in/de-crement means finding the following or preceding number
in the Sw1 sequence.

Results like 222222 still take a lot of time, but results closer to the
increment of length are found much faster.

          Rate  naive matrix
 naive  1.17/s     --   -35%
 matrix 1.79/s    53%     --

=cut