You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

103 lines
2.7KB

  1. /* Lambert W function.
  2. Was ~/C/LambertW.c written K M Briggs Keith dot Briggs at bt dot com 97 May 21.
  3. Revised KMB 97 Nov 20; 98 Feb 11, Nov 24, Dec 28; 99 Jan 13; 00 Feb 23; 01 Apr 09
  4. Computes Lambert W function, principal branch.
  5. See LambertW1.c for -1 branch.
  6. Returned value W(z) satisfies W(z)*exp(W(z))=z
  7. test data...
  8. W(1)= 0.5671432904097838730
  9. W(2)= 0.8526055020137254914
  10. W(20)=2.2050032780240599705
  11. To solve (a+b*R)*exp(-c*R)-d=0 for R, use
  12. R=-(b*W(-exp(-a*c/b)/b*d*c)+a*c)/b/c
  13. Test:
  14. gcc -DTESTW LambertW.c -o LambertW -lm && LambertW
  15. Library:
  16. gcc -O3 -c LambertW.c
  17. */
  18. #include <math.h>
  19. #include <stdio.h>
  20. double LambertW(const double z);
  21. const int dbgW=0;
  22. double LambertW(const double z) {
  23. int i;
  24. const double eps=4.0e-16, em1=0.3678794411714423215955237701614608;
  25. double p,e,t,w;
  26. if (dbgW) fprintf(stderr,"LambertW: z=%g\n",z);
  27. if (z<-em1 || isinf(z) || isnan(z)) {
  28. fprintf(stderr,"LambertW: bad argument %g, exiting.\n",z); exit(1);
  29. }
  30. if (0.0==z) return 0.0;
  31. if (z<-em1+1e-4) { // series near -em1 in sqrt(q)
  32. double q=z+em1,r=sqrt(q),q2=q*q,q3=q2*q;
  33. return
  34. -1.0
  35. +2.331643981597124203363536062168*r
  36. -1.812187885639363490240191647568*q
  37. +1.936631114492359755363277457668*r*q
  38. -2.353551201881614516821543561516*q2
  39. +3.066858901050631912893148922704*r*q2
  40. -4.175335600258177138854984177460*q3
  41. +5.858023729874774148815053846119*r*q3
  42. -8.401032217523977370984161688514*q3*q; // error approx 1e-16
  43. }
  44. /* initial approx for iteration... */
  45. if (z<1.0) { /* series near 0 */
  46. p=sqrt(2.0*(2.7182818284590452353602874713526625*z+1.0));
  47. w=-1.0+p*(1.0+p*(-0.333333333333333333333+p*0.152777777777777777777777));
  48. } else
  49. w=log(z); /* asymptotic */
  50. if (z>3.0) w-=log(w); /* useful? */
  51. for (i=0; i<10; i++) { /* Halley iteration */
  52. e=exp(w);
  53. t=w*e-z;
  54. p=w+1.0;
  55. t/=e*p-0.5*(p+1.0)*t/p;
  56. w-=t;
  57. if (fabs(t)<eps*(1.0+fabs(w))) return w; /* rel-abs error */
  58. }
  59. /* should never get here */
  60. fprintf(stderr,"LambertW: No convergence at z=%g, exiting.\n",z);
  61. exit(1);
  62. }
  63. #ifdef TESTW
  64. /* test program... */
  65. int main() {
  66. int i;
  67. double z,w,err;
  68. for (i=0; i<100; i++) {
  69. z=i/100.0-0.3678794411714423215955; w=LambertW(z);
  70. err=exp(w)-z/w;
  71. printf("W(%8.4f)=%22.16f, check: exp(W(z))-z/W(z)=%e\n",z,w,err);
  72. }
  73. for (i=0; i<100; i++) {
  74. z=i/1.0e-1-0.3; w=LambertW(z);
  75. err=exp(w)-z/w;
  76. printf("W(%8.4f)=%22.16f, check: exp(W(z))-z/W(z)=%e\n",z,w,err);
  77. }
  78. return 0;
  79. }
  80. #endif
  81. #ifdef INTW
  82. int main() {
  83. int i,n=1000;
  84. double w,z,s=0,err;
  85. for (i=1; i<=n; i++) {
  86. z=i/(double)n;
  87. w=LambertW(1/z)/(1+z);
  88. s+=w;
  89. printf("%8.4f %8.4f\n",z,w);
  90. }
  91. fprintf(stderr,"%8.4f\n",exp(s/n/log(2)));
  92. return 0;
  93. }
  94. #endif