lambert_W_function_complex.pl 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # License: GPLv3
  4. # Date: 26 December 2016
  5. # https://github.com/trizen
  6. # Implementation of the Lambert-W function in complex numbers.
  7. # Example: x^x = 100
  8. # x = exp(lambert_w(log(100)))
  9. # x =~ 3.59728502354042
  10. # See also:
  11. # https://en.wikipedia.org/wiki/Lambert_W_function
  12. use 5.010;
  13. use strict;
  14. use warnings;
  15. use Math::MPC;
  16. use Math::MPFR;
  17. my $PREC = 128; # can be tweaked
  18. my $ROUND = Math::MPC::MPC_RNDNN();
  19. sub lambert_w {
  20. my ($c) = @_;
  21. if (ref($c) ne 'Math::MPC') {
  22. my $n = Math::MPC::Rmpc_init2($PREC);
  23. Math::MPC::Rmpc_set_str($n, "$c", 10, $ROUND);
  24. $c = $n;
  25. }
  26. my $p = Math::MPFR::Rmpfr_init2($PREC);
  27. Math::MPFR::Rmpfr_ui_pow_ui($p, 10, int($PREC / 4), $ROUND);
  28. Math::MPFR::Rmpfr_ui_div($p, 1, $p, $ROUND);
  29. my $x = Math::MPC::Rmpc_init2($PREC);
  30. Math::MPC::Rmpc_set($x, $c, $ROUND);
  31. Math::MPC::Rmpc_sqrt($x, $x, $ROUND);
  32. Math::MPC::Rmpc_add_ui($x, $x, 1, $ROUND);
  33. my $y = Math::MPC::Rmpc_init2($PREC);
  34. Math::MPC::Rmpc_set_ui($y, 0, $ROUND);
  35. my $tmp = Math::MPC::Rmpc_init2($PREC);
  36. my $abs = Math::MPFR::Rmpfr_init2($PREC);
  37. my $count = 0;
  38. while (1) {
  39. Math::MPC::Rmpc_sub($tmp, $x, $y, $ROUND);
  40. Math::MPC::Rmpc_abs($abs, $tmp, $ROUND);
  41. Math::MPFR::Rmpfr_cmp($abs, $p) <= 0 and last;
  42. Math::MPC::Rmpc_set($y, $x, $ROUND);
  43. Math::MPC::Rmpc_log($tmp, $x, $ROUND);
  44. Math::MPC::Rmpc_add_ui($tmp, $tmp, 1, $ROUND);
  45. Math::MPC::Rmpc_add($x, $x, $c, $ROUND);
  46. Math::MPC::Rmpc_div($x, $x, $tmp, $ROUND);
  47. last if ++$count > $PREC;
  48. }
  49. Math::MPC::Rmpc_log($x, $x, $ROUND);
  50. $x;
  51. }
  52. say lambert_w(100); # 3.385630140290050184888244364529726867493
  53. say lambert_w(-100); # 3.205380786307449372155918213968303847481 + 2.482590531815923582117041287234452276982i
  54. say lambert_w(-0.5); # -0.7940236323446893679630153219005898091005 + 0.770111750510379109681313077405028929402i