forked from trizen/perl-scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
solve_sequence.pl
71 lines (52 loc) · 1.55 KB
/
solve_sequence.pl
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
#!/usr/bin/perl
# Encode a sequence of n numbers into a polynomial of, at most, degree n-1.
# The polynomial will generate the given sequence of numbers, starting with index 0.
# See also:
# https://yewtu.be/watch?v=4AuV93LOPcE
# https://en.wikipedia.org/wiki/Polynomial_interpolation
use 5.014;
use warnings;
use experimental qw(signatures);
use Math::Polynomial;
use Math::AnyNum qw(:overload :all);
use List::Util qw(all);
sub binary_product (@arr) {
while ($#arr > 0) {
push @arr, shift(@arr)->mul(shift(@arr));
}
$arr[0];
}
sub poly_binomial ($n, $k) {
my @terms;
foreach my $i (0 .. $k - 1) {
push @terms, $n;
$n = $n->sub_const(1);
}
@terms || return Math::Polynomial->new(1);
binary_product(@terms)->div_const(factorial($k));
}
sub array_differences (@arr) {
my @result;
foreach my $i (1 .. $#arr) {
CORE::push(@result, $arr[$i] - $arr[$i - 1]);
}
@result;
}
sub solve_seq (@arr) {
my $poly = Math::Polynomial->new();
my $x = Math::Polynomial->new(0, 1);
for (my $k = 0 ; ; ++$k) {
$poly += poly_binomial($x, $k)->mul_const($arr[0]);
@arr = array_differences(@arr);
last if all { $_ == 0 } @arr;
}
$poly;
}
if (@ARGV) {
my @terms = (map { Math::AnyNum->new($_) } grep { /[0-9]/ } map { split(' ') } map { split(/\s*,\s*/) } @ARGV);
say solve_seq(@terms);
}
else {
say solve_seq(map { $_**2 } 0 .. 20); # (x^2)
say solve_seq(map { faulhaber_sum($_, 2) } 0 .. 20); # (1/3 x^3 + 1/2 x^2 + 1/6 x)
}