侧边栏壁纸
  • 累计撰写 54 篇文章
  • 累计创建 0 个标签
  • 累计收到 50 条评论

目 录CONTENT

文章目录

牛顿迭代法求平方根

求平方根

以下实例通过 Go 语言使用递归方法实现求平方根的代码:

package main

import (
        "fmt"
)

func sqrtRecursive(x, guess, prevGuess, epsilon float64) float64 {
        if diff := guess*guess - x; diff < epsilon && -diff < epsilon {
                return guess
        }

        newGuess := (guess + x/guess) / 2
        if newGuess == prevGuess {
                return guess
        }

        return sqrtRecursive(x, newGuess, guess, epsilon)
}

func sqrt(x float64) float64 {
        return sqrtRecursive(x, 1.0, 0.0, 1e-9)
}

func main() {
        x := 25.0
        result := sqrt(x)
        fmt.Printf("%.2f 的平方根为 %.6f\n", x, result)
}

以上实例中,sqrtRecursive 函数使用递归方式实现平方根的计算。

sqrtRecursive 函数接受四个参数:

  • x 表示待求平方根的数
  • guess 表示当前猜测的平方根值
  • prevGuess 表示上一次的猜测值
  • epsilon 表示精度要求(即接近平方根的程度)

递归的终止条件是当前猜测的平方根与上一次猜测的平方根非常接近,差值小于给定的精度 epsilon。

在 sqrt 函数中,我们调用 sqrtRecursive 来计算平方根,并传入初始值和精度要求,然后在 main 函数中,我们调用 sqrt 函数来求解平方根,并将结果打印出来。

执行以上代码输出结果为:

25.00 的平方根为 5.000000

Grok3解释如下:

这段代码是用 Go 语言实现的牛顿法(Newton’s Method)来计算一个数的平方根。我们来逐步分析代码的逻辑和功能,并解释其输出。


代码结构与功能

1. sqrtRecursive 函数

func sqrtRecursive(x, guess, prevGuess, epsilon float64) float64 {
    if diff := guess*guess - x; diff < epsilon && -diff < epsilon {
        return guess
    }

    newGuess := (guess + x/guess) / 2
    if newGuess == prevGuess {
        return guess
    }

    return sqrtRecursive(x, newGuess, guess, epsilon)
}
  • 参数说明

    • x:要计算平方根的数(目标值)。
    • guess:当前猜测的平方根值。
    • prevGuess:上一次的猜测值,用于检测是否收敛。
    • epsilon:误差范围,用于判断猜测值是否足够接近真实平方根。
  • 逻辑说明

    • 首先计算当前猜测值 guess 的平方与目标值 x 的差值 diff
    • 如果差值 diff 的绝对值小于 epsilon,说明当前猜测值已经足够接近真实平方根,返回 guess
    • 否则,使用牛顿法公式 newGuess := (guess + x/guess) / 2 计算一个新的猜测值。
    • 如果新猜测值 newGuess 与上一次猜测值 prevGuess 相等,说明已经收敛,返回当前猜测值 guess
    • 如果未收敛,递归调用 sqrtRecursive,传入新的猜测值和其他参数。
  • 牛顿法原理
    牛顿法通过迭代公式 x_{n+1} = (x_n + S/x_n)/2 逐步逼近平方根,其中 S 是目标值(即 x)。每次迭代都会使猜测值更接近真实平方根。

2. sqrt 函数

func sqrt(x float64) float64 {
    return sqrtRecursive(x, 1.0, 0.0, 1e-9)
}
  • 这个函数是 sqrtRecursive 的入口函数。
  • 它以初始猜测值 1.0、初始上一次猜测值 0.0 和误差范围 1e-9(即 10⁻⁹,精度非常高)调用递归函数。
  • x 是要计算平方根的输入值。

3. main 函数

func main() {
    x := 25.0
    result := sqrt(x)
    fmt.Printf("%.2f 的平方根为 %.6f\n", x, result)
}
  • 定义一个变量 x = 25.0,表示要计算平方根的数。
  • 调用 sqrt 函数计算 x 的平方根,并将结果存储在 result 中。
  • 使用 fmt.Printf 格式化输出结果,保留 x 两位小数,保留结果 result 六位小数。

代码运行过程(以 x = 25.0 为例)

  1. 初始调用:sqrtRecursive(25.0, 1.0, 0.0, 1e-9)

    • guess = 1.0guess*guess = 1.0,与 25.0 差距很大,不满足误差条件。
    • 计算新猜测值:newGuess = (1.0 + 25.0/1.0)/2 = 13.0
    • 递归调用:sqrtRecursive(25.0, 13.0, 1.0, 1e-9)
  2. 第二次调用:sqrtRecursive(25.0, 13.0, 1.0, 1e-9)

    • guess = 13.0guess*guess = 169.0,与 25.0 差距仍然较大。
    • 计算新猜测值:newGuess = (13.0 + 25.0/13.0)/2 ≈ 7.4615
    • 递归调用:sqrtRecursive(25.0, 7.4615, 13.0, 1e-9)
  3. 第三次调用:sqrtRecursive(25.0, 7.4615, 13.0, 1e-9)

    • guess ≈ 7.4615guess*guess ≈ 55.67,仍不满足误差条件。
    • 计算新猜测值:newGuess = (7.4615 + 25.0/7.4615)/2 ≈ 5.4060
    • 递归调用:sqrtRecursive(25.0, 5.4060, 7.4615, 1e-9)
  4. 第四次调用:sqrtRecursive(25.0, 5.4060, 7.4615, 1e-9)

    • guess ≈ 5.4060guess*guess ≈ 29.22,仍不满足误差条件。
    • 计算新猜测值:newGuess = (5.4060 + 25.0/5.4060)/2 ≈ 5.0148
    • 递归调用:sqrtRecursive(25.0, 5.0148, 5.4060, 1e-9)
  5. 第五次调用:sqrtRecursive(25.0, 5.0148, 5.4060, 1e-9)

    • guess ≈ 5.0148guess*guess ≈ 25.148,差值较小,但仍不满足 epsilon = 1e-9
    • 计算新猜测值:newGuess = (5.0148 + 25.0/5.0148)/2 ≈ 5.0001
    • 递归调用:sqrtRecursive(25.0, 5.0001, 5.0148, 1e-9)
  6. 第六次调用:sqrtRecursive(25.0, 5.0001, 5.0148, 1e-9)

    • guess ≈ 5.0001guess*guess ≈ 25.0005,差值非常小。
    • 计算新猜测值:newGuess ≈ 5.0000
    • 继续递归,直到差值小于 1e-9,最终返回 guess 约为 5.0000

输出结果

运行代码后,输出如下:

25.00 的平方根为 5.000000
  • x = 25.0 的平方根是 5.0,代码通过牛顿法迭代计算得到了非常接近 5.0 的结果。
  • 由于误差范围 epsilon = 1e-9,结果精度非常高,输出保留 6 位小数后显示为 5.000000

总结

  1. 这段代码使用牛顿法通过递归迭代的方式计算平方根,逻辑清晰且高效。
  2. 牛顿法每次迭代都会使猜测值更接近真实平方根,通常只需几次迭代就能达到很高的精度。
  3. 通过设置 epsilon = 1e-9,代码保证了结果的高精度。
  4. 输出结果表明对于输入 25.0,程序正确计算出平方根为 5.000000

如果你有其他问题或想测试不同的输入值,可以进一步探讨!

newGuess := (guess + x/guess) / 2 为什么除2

在牛顿法(Newton’s Method)中,计算平方根的迭代公式是:

newGuess = (guess + x/guess) / 2

这里的 / 2(除以 2)是为了取 guessx/guess 的平均值。让我们详细解释为什么需要除以 2,以及这个公式的数学和直观原理。


1. 牛顿法的数学原理

牛顿法是一种求解方程根的迭代方法。对于求平方根的问题,我们的目标是找到一个数 guess,使得 guess² = x,即求解方程:

f(guess) = guess² - x = 0

牛顿法的迭代公式为:

guess_{n+1} = guess_n - f(guess_n) / f'(guess_n)

其中:

  • f(guess) = guess² - x
  • f'(guess) = 2 * guessf(guess) 的导数)

代入后:

guess_{n+1} = guess_n - (guess_n² - x) / (2 * guess_n)
            = (guess_n² + x) / (2 * guess_n)
            = (guess_n + x / guess_n) / 2

因此,newGuess = (guess + x/guess) / 2 中的 / 2 是从导数 2 * guess 中得到的。这是数学推导的结果,确保每次迭代都更接近真实平方根。


2. 直观解释:取平均值

除了数学推导,我们也可以从直观的角度理解为什么要除以 2。

  • guess 是当前对平方根的猜测值。
  • 如果 guessx 的平方根,那么 guess² = x,即 x/guess = guess
  • 如果 guess 不是精确的平方根:
    • guess 偏小时,guess² < x,因此 x/guess > guess
    • guess 偏大时,guess² > x,因此 x/guess < guess

也就是说,guessx/guess 总是位于真实平方根的两侧(一个偏小,一个偏大)。取两者的平均值 (guess + x/guess) / 2,可以得到一个更接近真实平方根的新猜测值。

  • 除以 2 就是取平均值的操作,确保新猜测值是当前猜测值和 x/guess 的中间值,从而逐步逼近真实平方根。

3. 示例说明

x = 25 为例,假设初始猜测值 guess = 1.0

  • guess = 1.0x/guess = 25.0/1.0 = 25.0
  • 此时 guess = 1.0 远小于真实平方根 5.0,而 x/guess = 25.0 远大于 5.0
  • 取平均值:(1.0 + 25.0) / 2 = 26.0 / 2 = 13.0,新猜测值 13.01.0 更接近 5.0

第二次迭代:

  • guess = 13.0x/guess = 25.0/13.0 ≈ 1.923
  • 此时 guess = 13.0 大于 5.0,而 x/guess ≈ 1.923 小于 5.0
  • 取平均值:(13.0 + 1.923) / 2 ≈ 14.923 / 2 ≈ 7.4615,新猜测值 7.461513.0 更接近 5.0

可以看到,每次取平均值(除以 2)都能让新猜测值更接近真实平方根。如果不除以 2,直接用 guess + x/guess,结果会是两者的和,而不是平均值,无法保证逐步收敛。


4. 不除以 2 会怎样?

如果不除以 2,迭代公式变为 newGuess = guess + x/guess,那么:

  • 对于 x = 25guess = 1.0
    • newGuess = 1.0 + 25.0/1.0 = 26.0,比真实值 5.0 更远。
    • 第二次:newGuess = 26.0 + 25.0/26.0 ≈ 26.9615,仍然远离真实值。
  • 结果不会收敛到平方根,而是不断变大或偏离目标。

因此,除以 2 是确保收敛的关键步骤,通过取平均值平衡 guessx/guess 的偏差。


总结

  • / 2(除以 2)来源于牛顿法数学推导中的导数项,确保迭代公式正确。
  • 直观上看,除以 2 是取 guessx/guess 的平均值,使得新猜测值更接近真实平方根。
  • 如果不除以 2,迭代结果无法收敛,无法正确计算平方根。

这就是牛顿法公式中除以 2 的原因。

Gemini-2.5-pro解释如下:

Of course. This is a great Go program that demonstrates how to calculate a square root using a classic numerical method.

Here is a detailed breakdown of the code, explaining what it does, how it works, and the algorithm behind it.

Overall Summary

This Go program calculates the square root of a number (25.0 in this case) using a recursive implementation of the Babylonian method (also known as Heron’s method, which is a specific case of Newton’s method). It finds the answer by starting with an initial guess and repeatedly refining that guess until it is acceptably close to the actual square root.


Code Breakdown

Let’s look at each function individually.

1. main function

func main() {
        x := 25.0
        result := sqrt(x)
        fmt.Printf("%.2f 的平方根为 %.6f\n", x, result)
}
  • x := 25.0: This line declares a floating-point variable x and initializes it with the value 25.0. This is the number we want to find the square root of.
  • result := sqrt(x): This calls the sqrt function to perform the calculation and stores the returned value in the result variable.
  • fmt.Printf(...): This line prints the final output to the console.
    • %.2f: Formats the value of x as a float with 2 decimal places.
    • %.6f: Formats the result as a float with 6 decimal places.
    • 的平方根为: This is Chinese for “the square root of … is”.
    • Expected Output: 25.00 的平方根为 5.000000

2. sqrt function

func sqrt(x float64) float64 {
        return sqrtRecursive(x, 1.0, 0.0, 1e-9)
}

This is a “wrapper” or “helper” function. Its purpose is to provide a clean and simple interface for the user. Instead of having to provide multiple arguments (like an initial guess), you can just call sqrt(x).

  • It calls the main recursive function sqrtRecursive with sensible starting values:
    • x: The number we want the square root of.
    • guess = 1.0: The initial guess for the square root. 1.0 is a common and safe starting point.
    • prevGuess = 0.0: The “previous guess,” initialized to a value that won’t be equal to the first guess.
    • epsilon = 1e-9: This is the desired precision (0.000000001). It’s a very small number that defines how close our guess’s square needs to be to x before we consider the job done.

3. sqrtRecursive function

This is the core of the algorithm where the magic happens.

func sqrtRecursive(x, guess, prevGuess, epsilon float64) float64 {
        // Condition 1: Check if the guess is good enough
        if diff := guess*guess - x; diff < epsilon && -diff < epsilon {
                return guess
        }

        // Calculate a new, better guess
        newGuess := (guess + x/guess) / 2

        // Condition 2: Check if the guess has stopped changing
        if newGuess == prevGuess {
                return guess
        }

        // Recursive step: Call itself with the new guess
        return sqrtRecursive(x, newGuess, guess, epsilon)
}

Let’s analyze its parts:

  1. Termination Condition 1 (Success):

    if diff := guess*guess - x; diff < epsilon && -diff < epsilon {
            return guess
    }
    
    • This checks if our current guess is “good enough.”
    • diff := guess*guess - x: It calculates the difference between the square of our guess and the actual number x. If the guess is perfect, diff will be 0.
    • diff < epsilon && -diff < epsilon: This is a clever way to check if the absolute value of diff is less than our tolerance epsilon. It’s equivalent to math.Abs(diff) < epsilon. If the difference is this small, we consider our guess to be the correct answer and return it.
  2. The Algorithm’s Core:

    newGuess := (guess + x/guess) / 2
    
    • This is the Babylonian method formula. It takes the average of the current guess and x / guess.
    • This formula has the excellent property of converging very quickly towards the true square root. For example, if guess is too small, x/guess will be too large, and their average will be closer to the real answer.
  3. Termination Condition 2 (Stability):

    if newGuess == prevGuess {
            return guess
    }
    
    • This is a safeguard. Due to the limits of floating-point precision, the newGuess might eventually stop changing and become equal to the prevGuess. If this happens, further recursion is pointless, so we stop and return the current guess. This prevents an infinite loop.
  4. The Recursive Step:

    return sqrtRecursive(x, newGuess, guess, epsilon)
    
    • If neither of the termination conditions is met, the function calls itself.
    • The newGuess becomes the guess for the next iteration.
    • The current guess becomes the prevGuess for the next iteration.
    • This process repeats, with each call getting closer to the final answer.

Alternative: Iterative Approach

While the recursive solution is elegant, this kind of problem is more commonly and often more efficiently solved in Go using a simple for loop. This avoids the overhead of function calls and the (very small) risk of a stack overflow.

Here is the same logic implemented iteratively:

func sqrtIterative(x float64) float64 {
    if x < 0 {
        return 0 // Or handle error appropriately
    }
    guess := 1.0
    epsilon := 1e-9
    for {
        newGuess := (guess + x/guess) / 2
        // Check if the guess has stabilized or is accurate enough
        if newGuess == guess || (guess*guess-x < epsilon && x-guess*guess < epsilon) {
            return newGuess
        }
        guess = newGuess
    }
}

newGuess := (guess + x/guess) / 2 为什么除2

Excellent question! That line is the core of the entire algorithm, and understanding why you divide by 2 is key to understanding how it works.

The short answer is: You are taking the average.

Let’s break down the logic in a few different ways, from simple intuition to a more formal explanation.

1. The Intuitive Explanation (Averaging)

Let’s say we want to find the square root of a number x. We’ll call the true square root s. So, s * s = x. This also means s = x / s.

Now, imagine we make a guess, which we’ll call g.

  • Case 1: Our guess g is too small. (i.e., g < s)
    If g is smaller than the true square root, then when you calculate x / g, the result will be larger than the true square root.
    Example: x = 25, true root s = 5. Let’s guess g = 4 (too small). Then x / g = 25 / 4 = 6.25 (too large).

  • Case 2: Our guess g is too large. (i.e., g > s)
    If g is larger than the true square root, then x / g will be smaller than the true square root.
    Example: x = 25, true root s = 5. Let’s guess g = 10 (too large). Then x / g = 25 / 10 = 2.5 (too small).

The Key Insight:
In both cases, the true square root s lies somewhere between our guess g and the value x / g.

Since the true answer is between these two numbers, a very good strategy for getting a better guess is to take their average. And how do you average two numbers? You add them together and divide by 2.

That’s exactly what the formula does:

newGuess := (guess + x/guess) / 2
//           ^^^^^^^^^^^^^^^^^   ^
//           Add the two numbers | Divide by 2 to get the average

This new guess (newGuess) will be much closer to the true square root than your original guess was. By repeating this process, you rapidly close in on the correct answer.

2. The Geometric Explanation (Making a Rectangle into a Square)

This is another great way to visualize it.

  1. Finding the square root of x is the same as finding the side length of a square with an area of x.
  2. Let’s start with a rectangle that has an area of x. We can choose any side length for our first guess, let’s call it g.
  3. To make the area x, the other side of the rectangle must have a length of x / g.
  4. Our goal is to make this rectangle a square. A square is just a special rectangle where the side lengths are equal.
  5. To make the two sides (g and x/g) more equal, we can create a new rectangle. For its side length, we’ll choose the average of the two old sides. This new side length is (g + x/g) / 2.
  6. This new rectangle (with sides of length newGuess) will be much “squarer” than the previous one.
  7. If you repeat this process, your rectangle will get closer and closer to being a perfect square, and its side length will get closer and closer to the true square root.

3. The Mathematical Explanation (Newton’s Method)

This algorithm is a specific application of a more general technique from calculus called Newton’s method, used for finding the roots of functions (i.e., where the function equals zero).

  1. We want to find g such that g² = x.

  2. We can rephrase this as finding the root of the function f(g) = g² - x. We are looking for the g where f(g) = 0.

  3. Newton’s method provides a formula to find a better approximation (g_next) from a current guess (g_current):
    g_next = g_current - f(g_current) / f'(g_current)
    (where f' is the derivative of the function f).

  4. Let’s apply it to our function:

    • f(g) = g² - x
    • The derivative f'(g) = 2g
  5. Now, substitute these into the Newton’s method formula:
    g_next = g - (g² - x) / (2g)

  6. Let’s simplify this with some algebra:
    g_next = (g * 2g) / 2g - (g² - x) / 2g
    g_next = (2g² - (g² - x)) / 2g
    g_next = (2g² - g² + x) / 2g
    g_next = (g² + x) / 2g

  7. And finally, splitting the fraction:
    g_next = g²/2g + x/2g = g/2 + x/(2g) = (g + x/g) / 2

This shows that the simple, intuitive formula of “taking the average” is actually a mathematically proven, highly efficient algorithm for finding square roots.

Preference

0

评论区