0xe3aad

0xe3aad

Newton's Method for Square Root

Newton's Iteration Method#

image

  • As shown in the figure, for a curve $y=f(x)$, draw a tangent line at $f(x_n)$ intersecting the x-axis at point $x_{n+1}$, then draw a tangent line at $f(x_{n+1})$ intersecting the x-axis at point $x_{n+2}$, and so on.
  • In this process, the intersection point $x_{n+m}$ will infinitely approach the zero point of the curve, which means obtaining the solution to the equation $f(x) = 0$.

Finding the Square Root#

Approach#

  • It is to find the zero point of the function $f(x) = x^2 - n$
  • The derivative $f^{'}(x) = 2x$
  • The equation of the tangent line at point $(x_n, x_n^2-n)$ is $y - x_n^2 + n = 2x_n (x-x_n)$, which is $y = 2x_nx - x_n^2 - n$
  • Therefore, the intersection point $x_{n+1}$ of the tangent line and the x-axis is xn2n2xn\frac {x_n^2 - n} {2x_n}
  • Repeat the iteration until a satisfactory value is obtained

Code Implementation#

package main

import (
	"fmt"
	"math"
)

func Sqrt(x float64) float64 {
	z := 1.0
	for math.Abs(z * z - x) > 1e-12 {
		z -= (z * z - x) / (2 * z)
	}
	return z
}

func main() {
	fmt.Println(Sqrt(2))
}

Finding the k-th Root#

Approach#

  • xn+1=xnf(xn)f(xn)x_{n+1} = x_{n} - \frac {f(x_n)} {f^{'}(x_n)}
  • xn+1=xnxn(1nxnk)kx_{n+1} = x_n - \frac {x_n(1 - nx_n^{-k})} k

Code Implementation#

package main

import (
	"fmt"
	"math"
)

func getRoot(x, k float64) float64 {
	z := 1.0
	for math.Abs(math.Pow(z, k) - x) > 1e-9 {
		z -= z * (1 - x * math.Pow(z, -k)) / k
	}
	return z
}

func main() {
	fmt.Println(getRoot(27, 3))
}

Implementation and Testing#

// sqrt.go
package sqrt

import "errors"

const EPSILON = 1e-8

var ErrNegativeSqrt = errors.New("cannot Sqrt negative number")

type Float interface {
	float64 | float32
}

func abs[T Float](x T) T {
	if x < 0 {
		return -x
	}
	return x
}

func SqrtWithNR[T Float](num T) (T, error) {
	if num < 0 {
		return 0, ErrNegativeSqrt
	}
	z := T(1.0)
	for abs(z*z-num) > EPSILON {
		z -= (z*z - num) / (2 * z)
	}
	return z, nil
}

func SqrtWithBS[T Float](num T) (T, error) {
	if num < 0 {
		return 0, ErrNegativeSqrt
	}
	l, r := T(0.0), num
	for abs(l*l-num) > EPSILON {
		m := (l + r) / 2
		if m*m > num {
			r = m
		} else {
			l = m
		}
	}
	return l, nil
}
// sqrt_test.go
package sqrt_test

import (
	"math"
	"sqrt"
	"testing"
)

func TestSqrtWithNR(t *testing.T) {
	for i := 0; i < 10; i++ {
		num := float64(i)
		if z, err := sqrt.SqrtWithNR(num); err != nil {
			t.Error(err)
		} else if math.Abs(z*z-num) > sqrt.EPSILON {
			t.Errorf("%f != %f", z*z, num)
		}
	}
}

func TestSqrtWithBS(t *testing.T) {
	for i := 0; i < 10; i++ {
		num := float64(i)
		if z, err := sqrt.SqrtWithBS(num); err != nil {
			t.Error(err)
		} else if math.Abs(z*z-num) > sqrt.EPSILON {
			t.Errorf("%f != %f", z*z, num)
		}
	}
}

func BenchmarkSqrtWithNR(b *testing.B) {
	for i := 0; i < b.N; i++ {
		sqrt.SqrtWithNR(float64(i))
	}
}

func BenchmarkSqrtWithBS(b *testing.B) {
	for i := 0; i < b.N; i++ {
		sqrt.SqrtWithBS(float64(i))
	}
}

Test Results#

image

It can be seen that Newton's method performs better than binary search! 🥰

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.