二分法、梯度下降法、牛顿法求解根号(Python)

1. 梯度下降法(Gradient descent)

若要求根号下2,即要求解 x^2 - 2 = 0 的根, 也就是函数 L = ( x 2 − 2 ) 2 L=\left(x^{2}-2\right)^{2} L=(x22)2取极小值时 x 的取值。这个也就对应机器学习中的损失函数。
要寻找损失函数的最低点就是找到曲线的最低点。在这里,我们使用了微积分里导数,通过求出函数导数的值,从而找到函数下降的方向或者是最低点(极值点)。
g ( x ) = d L / d x = 4 x 3 − 8 x = 4 x ( x 2 − 2 ) g(x)=d L / d x=4 x^{3}-8 x=4 x\left(x^{2}-2\right) g(x)=dL/dx=4x38x=4x(x22)
在这里插入图片描述
给x一个初始值,然后不断通过下式来更新x就可以逐渐逼近最优的x,这里的a代表步长,也就是学习率。
x n + 1 = x n − a g ( x n ) x_{n+1}=x_{n}-a g\left(x_{n}\right) xn+1=xnag(xn)

python

import random
import matplotlib.pyplot as plt
class Solutin():
    def gradient_descent(self,n):
        # 随机初始化
        x = float(random.randint(1, 100))
        # 学习率
        lr = 0.00001
        # 记录损失
        loss = []
        # 损失阈值
        while (abs(x ** 2 - n) > 0.0000000001):
            # x(n+1) = x(n) - lr * g(x(n))
            x = x - lr * 4 * x * (x ** 2 - n)
            # 记录损失
            loss.append((x ** 2 - n)**2)
        return loss,x

if __name__ == '__main__':
    n=100
    loss,a = Solutin().gradient_descent(n)
    print( a)

    # 画损失图
    x = range(len(loss))
    plt.plot(x, loss, color='b')
    plt.xlim(0,1000)
    plt.show()

损失变化图
在这里插入图片描述

2. 牛顿迭代法(Newton’s method)

它是牛顿在17世纪提出的一种在实数域和复数域上近似求解方程的方法。

多数方程不存在求根公式,因此求精确根非常困难,甚至不可解,从而寻找方程的近似根就显得特别重要。方法使用函数的泰勒级数的前面几项来寻找方程的根牛顿迭代法是求方程根的重要方法之一,其最大优点是在方程 的单根附近具有平方收敛,而且该法还可以用来求方程的重根、复根,此时线性收敛,但是可通过一些方法变成超线性收敛。另外该方法广泛用于计算机编程中。

把 f(x )在点x_0 的某邻域内展开成泰勒级数

f ( x ) = f ( x 0 ) + f ′ ( x 0 ) ( x − x 0 ) + f ′ ′ ( x 0 ) ( x − x 0 ) 2 2 ! + ⋯ + f ( n ) ( x 0 ) ( x − x 0 ) n n ! + R n ( x ) f(x)=f\left(x_{0}\right)+f^{\prime}\left(x_{0}\right)\left(x-x_{0}\right)+\frac{f^{\prime \prime}\left(x_{0}\right)\left(x-x_{0}\right)^{2}}{2 !}+\cdots+\frac{f^{(n)}\left(x_{0}\right)\left(x-x_{0}\right)^{n}}{n !}+R_{n}(x) f(x)=f(x0)+f(x0)(xx0)+2!f(x0)(xx0)2++n!f(n)(x0)(xx0)n+Rn(x)

取其线性部分(即泰勒展开的前两项),并令其等于0

0 = f ( x 0 ) + f ′ ( x 0 ) ( x − x 0 ) 0=f\left(x_{0}\right)+f^{\prime}\left(x_{0}\right)\left(x-x_{0}\right) 0=f(x0)+f(x0)(xx0)

以此作为非线性方程 的近似方程,则其解为 x 1 = x 0 − f ( x 0 ) f ′ ( x 0 ) x_{1}=x_{0}-\frac{f\left(x_{0}\right)}{f^{\prime}\left(x_{0}\right)} x1=x0f(x0)f(x0) 这样,得到牛顿迭代法的一个迭代关系式:
x n + 1 = x n − f ( x n ) f ′ ( x n ) x_{n+1}=x_{n}-\frac{f\left(x_{n}\right)}{f^{\prime}\left(x_{n}\right)} xn+1=xnf(xn)f(xn)

假设 f(x) = x^2 - a
可以得到
x n + 1 = x n − x n 2 − a 2 x n x_{n+1}=x_{n} - \frac{x_{n}^2-a}{2x_{n}} xn+1=xn2xnxn2a
x n + 1 = x n − x n − a / x n 2 x_{n+1}=x_{n} - \frac{x_{n}-a/x_{n}}{2} xn+1=xn2xna/xn
x n + 1 = x n + a / x n 2 x_{n+1}=\frac{x_{n}+a/x_{n}}{2} xn+1=2xn+a/xn

这种方法可以很有效地求出根号 a的近似值:首先随便猜一个近似值 x,然后不断令 x 等于 x 和 a/x 的平均数,迭代个六七次后 x 的值就已经相当精确了。

例如,我想求根号 2 等于多少。假如我猜测的结果为 4,虽然错的离谱,但你可以看到使用牛顿迭代法后这个值很快就趋近于根号 2 了:

( 4 + 2/ 4 ) / 2 = 2.25

( 2.25 + 2/ 2.25 ) / 2 = 1.56944…

( 1.56944…+ 2/1.56944…) / 2 = 1.42189…

( 1.42189…+ 2/1.42189…) / 2 = 1.41423…

….

这种算法的原理很简单,我们仅仅是不断用 (x, f(x)) 的切线来逼近方程的根。
在这里插入图片描述

python

class sqrt(object):
    def s(self,x):
        a = x
        while a * a > x:
            a = (a + x / a) / 2
            print(a)

if __name__ == '__main__':
    x = 169
    sqrt().s(x)
85.0
43.49411764705882
23.68985027605849
15.411853548944432
13.188719595702175
13.00135021013767
13.000000070110696
13.0

3. 二分法

一个数a的平方根小于等于a,使用二分法解决如下

class Solution3():
    def mySqrt(self, x):
        if x==0:
            return 0
        if x==1:
            return 1
        left = 1
        right = x
        while left<=right:
            mid = left+(right-left)//2
            if mid*mid==x:
                return mid
            elif mid*mid>x:
                right = mid - 0.0001
            else:
                left = mid + 0.0001
        return right

if __name__ == '__main__':
    print(Solution3().mySqrt(3))

猜你喜欢

转载自blog.csdn.net/weixin_41744192/article/details/123543067
今日推荐