Newton's Iteration Method#
- 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
- 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#
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))
}
Benchmark (vs Binary Search)#
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#
It can be seen that Newton's method performs better than binary search! 🥰