lanczos_approximation.pl 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  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 Math::AnyNum qw(:overload pi real imag);
  8. use experimental qw(signatures lexical_subs);
  9. sub gamma($z) {
  10. my $epsilon = 0.0000001;
  11. my sub withinepsilon($x) {
  12. abs($x - abs($x)) <= $epsilon;
  13. }
  14. state $p = [
  15. 676.5203681218851, -1259.1392167224028,
  16. 771.32342877765313, -176.61502916214059,
  17. 12.507343278686905, -0.13857109526572012,
  18. 9.9843695780195716e-6, 1.5056327351493116e-7,
  19. ];
  20. my $result;
  21. if (real($z) < 0.5) {
  22. $result = (pi / (sin(pi * $z) * gamma(1 - $z)));
  23. }
  24. else {
  25. $z -= 1;
  26. my $x = 0.99999999999980993;
  27. while (my ($i, $pval) = each @$p) {
  28. $x += $pval / ($z + $i + 1);
  29. }
  30. my $t = ($z + @$p - 0.5);
  31. $result = (sqrt(pi * 2) * $t**($z + 0.5) * exp(-$t) * $x);
  32. }
  33. withinepsilon(imag($result)) ? real($result) : $result;
  34. }
  35. foreach my $i (0.5, 4, 5, 6, 30, 40, 50) {
  36. printf("gamma(%3s) =~ %s\n", $i, gamma($i));
  37. }
  38. __END__
  39. gamma(0.5) =~ 1.77245385090551659496855986697771284175944211142
  40. gamma( 4) =~ 6.00000000000000628999184513591742545418327380194
  41. gamma( 5) =~ 24.0000000000000308599507225303222574058679398028
  42. gamma( 6) =~ 120.000000000000178632999163000072600390777175518
  43. gamma( 30) =~ 8841761993739669928012342097034.15093049782426111
  44. gamma( 40) =~ 20397882081197200259694400837033107505429486392
  45. gamma( 50) =~ 6.08281864034254395430563164837656389765153447987e62