Golfing Newton's Method

3

1

I have the following code which I've written to emulate Newton's method for deriving the square root of a number:

r=(x,y)=>
  (z=((y=y||x)*y-x)/(2*y)>1e-15)?
r(x,y-(y*y-x)/(2*y)):y

Where r(37) would result in 6.08276253029822 which is great given the limitations of my browser.

I'd like to golf this code further seeing there's some repetition here, not to mention, I'd also like to reduce my error limit (the 1e-15). This is the furthest I've gotten. Can anyone help? If possible I'd love to eliminate the need of the error limit without resulting in a "maximum stack call size exceeded" error...

This is for a blog post I'm working on JavaScript and alternatives to native methods. I'll give a special shout out on the blog post to the persons who can help me out.

Thanks in advance.

WallyWest

Posted 2017-10-05T20:54:30.527

Reputation: 6 949

1Which is more important, golfing it or reducing the error? – mbomb007 – 2017-10-05T20:58:11.840

@mbomb007 reducing the error is one thing... But If day golfing it is more important. I'm curious to see the answer from both fronts though. I've tried this on various browsers and found that my error was in the 1e-16 range... – WallyWest – 2017-10-05T21:07:25.237

Okay, why the down vote? – WallyWest – 2017-10-05T21:08:31.907

8Idk. Questions about golfing are ON-TOPIC. Someone voted to close, and I think they are wrong. – mbomb007 – 2017-10-05T21:11:31.893

Without a spec of a problem to golf, it's hard to know what improvements are valid. I'm voting to close as unclear until there's a solid spec. – xnor – 2017-10-05T21:30:55.367

1@xnor, this is a tips question, not a challenge. – Shaggy – 2017-10-05T21:49:45.810

I never mentioned this as a challenge, @xnor. I'm asking for help to golf... – WallyWest – 2017-10-05T21:50:55.033

5@WallyWest Right, but I can't give help to golf without knowing the challenge. Does anything stop you from just writing sqrt(n)? What accuracy is required? The code has a 1e-15 in it -- whether a byte could be saved as a 1e-9 depends on how precise the output must be. "I'd also like to reduce my error limit (the 1e-15)" further confuses it. – xnor – 2017-10-05T21:56:26.473

2@WallyWest Also, it's not clear from the text that you are looking primarily for golfing help. Asking about eliminating the error limit and saying you'll use it for an explanation in a blog rather sounds like seeking programming advice separate from shortening code. – xnor – 2017-10-05T22:01:10.277

@xnor, the blog post I'm writing is about alternatives to native methods, so, no Math.sqrt(x) is out of the question. I'd prefer to retain as much accuracy as possible, so 1e-9 won't work for me either... – WallyWest – 2017-10-05T22:01:24.530

@WallyWest It seems to me that the hypothetical task for this golf is so vague it would be closed, which means I can't know if a suggestion I post would be valid. – xnor – 2017-10-05T22:08:04.103

1Using a fixed epsilon and an absolute difference test guarantees bugginess. Consider that if x is somewhere near the upper bound of a double then 1 ulp relative to the square root is about 1E140. – Peter Taylor – 2017-10-05T22:13:15.983

@PeterTaylor Can you give an example of X near that upper bound level? I'm not sure what classes as a double in this case...? – WallyWest – 2017-10-05T23:30:13.083

1E308. Number.MAX_VALUE is the actual upper bound. – Peter Taylor – 2017-10-06T06:31:05.613

Answers

9

Golfed only, -23 bytes

ES6 supports default parameters.

Default function parameters allow formal parameters to be initialized with default values if no value or undefined is passed.

So, we can replace y=y||x with just r=>(x,y=x).

(y*y-x)/(2*y) can be rewritten as y/2-x/2/y. And since this expression appears twice in the code, we can assign it to a new variable z.

We get:

r=(x,y=x)=>(z=y/2-x/2/y)>1e-15?r(x,y-z):y

As suggested by ETHproductions in the comments, we now can save 2 more bytes by multiplying z by 2:

r=(x,y=x)=>(z=y-x/y)>2e-15?r(x,y-z/2):y

r=(x,y=x)=>(z=y-x/y)>2e-15?r(x,y-z/2):y

console.log(r(37))

Alternate version, -26 bytes

To save more bytes, another approach is to just iterate enough times rather than using a test on the result.

That could be done that way:

r=(x,y=k=x)=>(k/=2)?r(x,y/2+x/2/y):y

r=(x,y=k=x)=>(k/=2)?r(x,y/2+x/2/y):y

console.log(r(37))

We start with k = x and divide it by 2 at each iteration. No matter the initial value of x, we will eventually reach 0 because of arithmetic underflow.

The good thing about this method is that the number of iterations doesn't heavily rely on the value of x. For instance, we'll get 1142 iterations for x = 1020 and 1009 iterations for x = 10 -20. In both cases, we iterate far too many times than necessary, without exceeding the default maximum number of iterations of common browsers.

Some more notes:

  • The error is negligible for x > 10 -215 and quickly grows below this bound.
  • For the same byte count and less iterations, we can divide by 9 instead. In that case, a safe bound is x > 10 -124.
  • Unlike Math.sqrt(), this method does not return NaN for negative numbers or ±Infinity (and neither does the original code).

Arnauld

Posted 2017-10-05T20:54:30.527

Reputation: 111 334

2And then you can do r=(x,y=x)=>(z=y-x/y)>2e-15?r(x,y-z/2):y to save another two bytes, and perhaps adjust the 2e-15 to get more precision – ETHproductions – 2017-10-05T21:39:48.550

1@ETHproductions Thanks! I thought about such an optimization for a second but overlooked it. – Arnauld – 2017-10-05T21:44:39.260

1@Arnauld Nice abuse of arithmetic underflow... I had considered it in some of my previous attempts but wasn't sure on the implementation. This is perfect... Thanks! – WallyWest – 2017-10-05T22:10:52.883

1@WallyWest Glad it helped! Some quick notes: 1) Performance-wise, the alternate version is, of course, very inefficient. 2) Unlike Math.sqrt(), these methods do not return NaN for negative numbers or +/-Infinity. – Arnauld – 2017-10-05T22:51:42.413

1

This is almost similar to "@Arnauld Alternate version" and has same bytes with that, but this can work in x < 1e-215

r=(x,y=k=9)=>k?r(x,(y+x/y)/2,k/=2):y

sapics

Posted 2017-10-05T20:54:30.527

Reputation: 119