sequence_polynomial_closed_form.pl 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 04 January 2019
  4. # https://github.com/trizen
  5. # Find a closed-form polynomial to a given sequence of numbers.
  6. # See also:
  7. # https://www.youtube.com/watch?v=gur16QsZ0r4
  8. # https://en.wikipedia.org/wiki/Polynomial_interpolation
  9. # https://en.wikipedia.org/wiki/Vandermonde_matrix
  10. use 5.020;
  11. use warnings;
  12. use Math::MatrixLUP;
  13. use Math::AnyNum qw(ipow sum);
  14. use List::Util qw(all);
  15. use experimental qw(signatures);
  16. sub find_poly_degree(@seq) {
  17. for (my $c = 1 ; ; ++$c) {
  18. @seq = map { $seq[$_ + 1] - $seq[$_] } 0 .. $#seq - 1;
  19. return $c if all { $_ == 0 } @seq;
  20. }
  21. }
  22. sub eval_poly ($S, $x) {
  23. sum(map { ($S->[$_] == 0) ? 0 : ($S->[$_] * ipow($x, $_)) } 0 .. $#{$S});
  24. }
  25. # An arbitrary sequence of numbers
  26. my @seq = (
  27. @ARGV
  28. ? (map { Math::AnyNum->new($_) } grep { /[0-9]/ } map { split(' ') } map { split(/\s*,\s*/) } @ARGV)
  29. : (0, 1, 17, 98, 354, 979, 2275, 4676)
  30. );
  31. # Find the lowest polygonal degree to express the sequence
  32. my $c = find_poly_degree(@seq);
  33. # Create a new cXc Vandermonde matrix
  34. my $A = Math::MatrixLUP->build($c, sub ($n, $k) { ipow($n, $k) });
  35. # Find the polygonal coefficients
  36. my $S = $A->solve([@seq[0 .. $c - 1]]);
  37. # Stringify the polynomial
  38. my $P = join(' + ', map { ($S->[$_] == 0) ? () : "($S->[$_] * x^$_)" } 0 .. $#{$S});
  39. if ($c == scalar(@seq)) {
  40. say "\n*** WARNING: the polynomial found may not be a closed-form to this sequence! ***\n";
  41. }
  42. say "Coefficients : [", join(', ', @$S), "]";
  43. say "Polynomial : $P";
  44. say "Next 5 terms : [", join(', ', map { eval_poly($S, $_) } scalar(@seq) .. scalar(@seq) + 4), "]";
  45. __END__
  46. Coefficients : [0, -1/30, 0, 1/3, 1/2, 1/5]
  47. Polynomial : (-1/30 * x^1) + (1/3 * x^3) + (1/2 * x^4) + (1/5 * x^5)
  48. Next 5 terms : [8772, 15333, 25333, 39974, 60710]