没有不值得去解决的问题,也没有不值得去学习的技术!

Go 实现 Sqrt() 的三种方式:固定迭代、误差控制与标准库对比

在学习 Go 官方教程 A Tour of Go 时,遇到了一个非常经典的练习: 实现一个平方根函数 Sqrt(x)

1、在学习 Go 官方教程 A Tour of Go 时,遇到了一个非常经典的练习:
实现一个平方根函数 Sqrt(x)。 如图1

在学习 Go 官方教程 A Tour of Go 时,遇到了一个非常经典的练习:
实现一个平方根函数 Sqrt(x)

这个练习不仅涉及 Go 的循环与函数,还引入了一个非常重要的数值计算方法:
牛顿迭代法(Newton’s method)。

本文将通过三个逐步优化的示例,演示如何从一个简单版本,
逐步实现一个更精确、更健壮的平方根函数:

  1. 固定 10 次循环
  2. 使用误差控制停止
  3. 与标准库 math.Sqrt 进行结果对比

所有代码均按照以下目录组织:

go-tour/
├── sqrt-iterations/
├── sqrt-epsilon/
├── sqrt-iteration-count/

2、## 牛顿迭代法简介

平方根的目标是:

给定一个数 x,找到一个数 z,使得:

z² ≈ x

牛顿法通过不断修正 z,使其逐渐接近真实值。

每一步的更新公式如下:

z = z – (zz – x) / (2z)

其中:

  • z*z – x 表示当前误差
  • 2*z 表示函数 z² 的导数
  • 每次更新都会让结果更接近真实值

这个方法收敛速度非常快,
通常只需要几次迭代就可以达到很高精度。

3、示例 1:固定 10 次迭代,对应 sqrt-iterations/main.go

Go
package main

import (
	"fmt"
)

func Sqrt(x float64) float64 {
	z := 1.0 // 初始猜测值
	for i := 0; i < 10; i++ {
		z -= (z*z - x) / (2 * z)
		fmt.Println("iteration", i, "z =", z)
	}

	return z
}

func main() {
	fmt.Println("result:", Sqrt(2))
}

4、运行结果示例

可以观察到:

每一次循环后,z 的值都会快速逼近 √2。

通常在第 4~5 次迭代后,
结果已经非常接近真实值。

Go
package main

import (
	"fmt"
)

func Sqrt(x float64) float64 {
	z := 1.0 // 初始猜测值
	for i := 0; i < 10; i++ {
		z -= (z*z - x) / (2 * z)
		fmt.Println("iteration", i, "z =", z)
	}

	return z
}

func main() {
	fmt.Println("result:", Sqrt(2))
}

5、示例 2:使用 epsilon 控制收敛 + 对比 math.Sqrt 对应:sqrt-epsilon/main.go

Go
package main

import (
	"fmt"
	"math"
)

func Sqrt(x float64) float64 {
	z := 1.0              // 初始猜测值
	const epsilon = 1e-10 // 精度控制

	for {
		prev := z
		z -= (z*z - x) / (2 * z)

		// 判断变化是否足够小
		if math.Abs(z-prev) < epsilon {
			break
		}
	}

	return z
}

func main() {
	fmt.Println("My sqrt:", Sqrt(2))
	fmt.Println("Math sqrt:", math.Sqrt(2))
}

6、运行结果示例

为什么这样更好?

这种方式:

自动判断收敛
避免无意义计算
提高程序效率

为了验证我们的实现是否正确,
可以将结果与 Go 标准库中的 math.Sqrt 进行对比。
可以看到,两者几乎完全一致(完全相等)。

Plaintext

/app/go-tour/sqrt-epsilon # go run main.go
My sqrt: 1.4142135623730951
Math sqrt: 1.4142135623730951

7、示例 3:统计迭代次数(观察收敛效率) 对应:sqrt-iteration-count/main.go

Go
package main

import (
	"fmt"
	"math"
)

func Sqrt(x float64) float64 {
	z := 1.0              // 初始猜测值
	const epsilon = 1e-10 // 精度控制

	count := 0

	for {
		prev := z
		z -= (z*z - x) / (2 * z)

		count++

		// 判断变化是否足够小
		if math.Abs(z-prev) < epsilon {
			break
		}
	}

	fmt.Println("iterations:", count)

	return z
}

func main() {
	fmt.Println("result:", Sqrt(2))
}

8、运行结果示例

特点:

统计 count
输出 iterations

用途:

观察算法效率。

通常:

iterations: 5

只需 5 次就能得到高精度结果。

Plaintext

/app/go-tour/sqrt-iteration-count # go run main.go
iterations: 5
result: 1.4142135623730951

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系