Compute the Lambert W function

6

1

Your challenge is to compute the Lambert W function. The W of x is defined to be the real value(s) y such that

y = W(x) if x = y*(e^y)

where e = 2.718281828 is Euler's number.

Sometimes, y may not be real.

Examples

W(-1) = non-real
W(-0.1) = -0.11183, -3.57715
W(1) = 0.56714
W(2) = 0.85261

Here's a quick graph of what this function looks like.

enter image description here


Rules

Your goal is to take an input and output either nothing, 1 solution, or 2 solutions, out to 5 significant figs. You should expect float inputs within the reasonable range of -100..100.

This is code-golf, so shortest code wins.

Simply Beautiful Art

Posted 2017-11-23T02:19:07.660

Reputation: 2 140

1A note about Lambert W function: there is no solution for x < -1/e, two solutions for -1/e < x < 0, and one solution for x == -1/e or x >= 0. – JungHwan Min – 2017-11-23T05:58:34.103

15 places after the decimal, or 5 significant figures? Since W^-1(x) tends to negative infinity when x->0-, it is hard to get 5 places after the decimal. – Colera Su – 2017-11-23T08:04:09.377

1If one input should give two values, this is not a function – Luis Mendo – 2017-11-23T10:43:10.293

@Mr.Xcoder Sure, though the non-real output should be reasonable. – Simply Beautiful Art – 2017-11-23T12:50:07.837

@ColeraSu Fixed. – Simply Beautiful Art – 2017-11-23T12:51:13.283

@LuisMendo It's a multivalued 'function'.

– Simply Beautiful Art – 2017-11-23T12:54:56.977

2I think a nice way to handle multiple solutions is the output being a list of reals, containing 0, 1, or 2 values. Would this be allowed? – xnor – 2017-11-23T16:56:59.247

@xnor Sounds like a great idea. Editting. – Simply Beautiful Art – 2017-11-23T17:10:49.983

Answers

1

Ruby, 131 bytes

->x{y,z,e,l=1,-1,Math::E,->t{Math::log t};100.times{z=x<0&&x>-1/e ?l[x/z]:p;y=x<-1/e ?"non-real":x>e ?l[x/y]:x*e**-y};p y;z&&(p z)}

Try it online!

Basically applies fixed-point iteration 100 times, as partially explained in Lambert W function aproximation.

Simply Beautiful Art

Posted 2017-11-23T02:19:07.660

Reputation: 2 140

3Why non-competing? – MD XF – 2017-11-23T03:38:33.717

4

Marking a submission non-competing does not make it valid. If your submission is invalid, please edit it to make it valid or delete it altogether. Relevant meta post.

– JungHwan Min – 2017-11-23T04:51:43.397

Actually the only non-competing reason I have seen is language postdate the challenge. In other cases I often see they are deleted or competing. / Well, apply the fixed-point iteration is a valid way to compute the function, but only if you can prove that it is good enough (to your own requirement). – user202729 – 2017-11-23T09:31:01.983

1@JungHwanMin My submission is valid, I just marked it non-competing because, well, I didn't want to compete. I suppose that in itself is not a valid reason :P – Simply Beautiful Art – 2017-11-23T12:49:19.407

1

Octave, 36 34 bytes

@(x)(y=lambertw(-1:0,x))(~imag(y))

Try it online!

I solved this in 10 different ways without the builtin, but I couldn't get the solution for both the -1 branch and the 0-branch. I wasn't able to solve this without resorting to the builtin lambertw function. Even though it uses a builtin, it's far from straightforward.

lambertw will only give out one value by default, namely the W0(x), unless otherwise specified. The problem arises when we specifies that we want two values, both W0(x) and W-1(x). If this is the case, and x>=0, it will return one real and one complex result.

We therefore have to specify that we only want the results where the imaginary part of the result is zero.

Breakdown:

@(x)                              % Anonymous function that takes x as input
    (y=lambertw(-1:0,x))          % The lambertw function, with both the -1 and 0 branches
                        (~imag(y))  % Index, where the elements with imag(y)=0 is true

Stewie Griffin

Posted 2017-11-23T02:19:07.660

Reputation: 43 471

1

C, 101 + 2 (-lm) bytes

Returns how many real solutions, and store them in an array.

This one is very slow, since it uses a brute-force approach to find roots. It handles up to x = 9e9.

f(a,b,p,t,c,x)float*b,a,x;{for(p=a<0,c=0,x=-50;x<9;x+=2e-6,p=t)(t=x*exp(x)>a)^p?b[c++]=x:0;return c;}

Try it online!

C, 148 146 + 2 (-lm) bytes

This function returns how many real solutions, and store them in the given pointers.

Use binary search to find a solution. It handles up to about x = 108.

#define h(x)99;for(l=-1;x=(l+r)/2,fabs(l-r)>1e-6;)x*exp(x)>a?r=x:(l=x);
f(a,b,c,l,r)float*b,*c,a,l,r;{r=h(*b)r=-h(*c)return-1/exp(1)>a?0:a<0?2:1;}

Try it online!

Colera Su

Posted 2017-11-23T02:19:07.660

Reputation: 2 291

0

Wolfram Language (Mathematica), 28 bytes

x/.NSolve[#==E^x x,x,Reals]&

Try it online!

Returns x for no-solution.

This function should give all solutions because NSolve algorithm uses inverse functions, and there is a built-in inverse function of x = y*(e^y) called ProductLog.

Explanation

#==E^x x

Equation <input> = e^x * x

NSolve[ ... ,x,Reals]

Find solutions in the domain of real numbers.

x/.

Replace x with the solutions.

Alternative version, 35 bytes

N@ProductLog[{-1,0},#]~Cases~_Real&

Try it online!

JungHwan Min

Posted 2017-11-23T02:19:07.660

Reputation: 13 290