lanczos_approximation.pl 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. #!/usr/bin/perl
  2. # Algorithm from Wikipedia:
  3. # https://en.wikipedia.org/wiki/Lanczos_approximation#Simple_implementation
  4. use 5.020;
  5. use strict;
  6. use warnings;
  7. use lib qw(../lib);
  8. use Math::GComplex qw(:overload real imag);
  9. use experimental qw(signatures lexical_subs);
  10. my $pi = log(-1) * -i;
  11. sub gamma($z) {
  12. my $epsilon = 0.0000001;
  13. my sub withinepsilon($x) {
  14. abs($x - abs($x)) <= $epsilon;
  15. }
  16. state $p = [
  17. 676.5203681218851, -1259.1392167224028,
  18. 771.32342877765313, -176.61502916214059,
  19. 12.507343278686905, -0.13857109526572012,
  20. 9.9843695780195716e-6, 1.5056327351493116e-7,
  21. ];
  22. my $result;
  23. if (real($z) < 0.5) {
  24. $result = ($pi / (sin($pi * $z) * gamma(1 - $z)));
  25. }
  26. else {
  27. $z -= 1;
  28. my $x = 0.99999999999980993;
  29. while (my ($i, $pval) = each @$p) {
  30. $x += $pval / ($z + $i + 1);
  31. }
  32. my $t = ($z + @$p - 0.5);
  33. $result = (sqrt($pi * 2) * $t**($z + 0.5) * exp(-$t) * $x);
  34. }
  35. withinepsilon(imag($result)) ? real($result) : $result;
  36. }
  37. foreach my $i (0.5, 4, 5, 6, 30, 40, 50) {
  38. printf("gamma(%3s) =~ %s\n", real($i), gamma($i));
  39. }
  40. __END__
  41. gamma(0.5) =~ 1.77245385090552
  42. gamma( 4) =~ 6
  43. gamma( 5) =~ 24
  44. gamma( 6) =~ 120
  45. gamma( 30) =~ 8.84176199373977e+30
  46. gamma( 40) =~ 2.0397882081197e+46
  47. gamma( 50) =~ 6.08281864034252e+62