solve_sequence.pl 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. #!/usr/bin/perl
  2. # Encode a sequence of n numbers into a polynomial of, at most, degree n-1.
  3. # The polynomial will generate the given sequence of numbers, starting with index 0.
  4. # See also:
  5. # https://yewtu.be/watch?v=4AuV93LOPcE
  6. # https://en.wikipedia.org/wiki/Polynomial_interpolation
  7. use 5.014;
  8. use warnings;
  9. use experimental qw(signatures);
  10. use Math::Polynomial;
  11. use Math::AnyNum qw(:overload :all);
  12. use List::Util qw(all);
  13. sub binary_product (@arr) {
  14. while ($#arr > 0) {
  15. push @arr, shift(@arr)->mul(shift(@arr));
  16. }
  17. $arr[0];
  18. }
  19. sub poly_binomial ($n, $k) {
  20. my @terms;
  21. foreach my $i (0 .. $k - 1) {
  22. push @terms, $n;
  23. $n = $n->sub_const(1);
  24. }
  25. @terms || return Math::Polynomial->new(1);
  26. binary_product(@terms)->div_const(factorial($k));
  27. }
  28. sub array_differences (@arr) {
  29. my @result;
  30. foreach my $i (1 .. $#arr) {
  31. CORE::push(@result, $arr[$i] - $arr[$i - 1]);
  32. }
  33. @result;
  34. }
  35. sub solve_seq (@arr) {
  36. my $poly = Math::Polynomial->new();
  37. my $x = Math::Polynomial->new(0, 1);
  38. for (my $k = 0 ; ; ++$k) {
  39. $poly += poly_binomial($x, $k)->mul_const($arr[0]);
  40. @arr = array_differences(@arr);
  41. last if all { $_ == 0 } @arr;
  42. }
  43. $poly;
  44. }
  45. if (@ARGV) {
  46. my @terms = (map { Math::AnyNum->new($_) } grep { /[0-9]/ } map { split(' ') } map { split(/\s*,\s*/) } @ARGV);
  47. say solve_seq(@terms);
  48. }
  49. else {
  50. say solve_seq(map { $_**2 } 0 .. 20); # (x^2)
  51. say solve_seq(map { faulhaber_sum($_, 2) } 0 .. 20); # (1/3 x^3 + 1/2 x^2 + 1/6 x)
  52. }