AlgoC++第一课:求解根号2

前言

手写AI推出的全新面向AI算法的C++课程 Algo C++,链接。记录下个人学习笔记,仅供自己参考。
本次课程主要讲解求解根号2
课程大纲可看下面的思维导图

在这里插入图片描述

1.问题描述

使用梯度下降法和牛顿法实现求解根号x
x = ? \sqrt{x}=? x =?

2.梯度下降法

问题思考方式:

第一步:转化问题,将 x \scriptsize \sqrt{x} x 转化为 L ( t ) = ( t 2 − x ) 2 \scriptsize L(t)=(t^2-x)^2 L(t)=(t2x)2,当 L ( t ) = 0 \scriptsize L(t)=0 L(t)=0时, t \scriptsize t t就是计算得出的结果

第二步:寻找合适的解,找 t \scriptsize t t使得 L ( t ) = ( t 2 − x ) 2 = 0 \scriptsize L(t)=(t^2-x)^2 = 0 L(t)=(t2x)2=0

第三步:找到的 t \scriptsize t t,就是得出的结果

转化问题的目的是,求解 L ( t ) \scriptsize L(t) L(t)的极小值,也就是导数为0的点

补充知识点

  • L ( t ) \scriptsize L(t) L(t)为偶函数,关于y轴对称,只需要考虑 [ 0 , + ∞ ) \scriptsize [0,+∞) [0,+)

  • [ 0 , + ∞ ) \scriptsize [0,+∞) [0,+) L ( t ) \scriptsize L(t) L(t)为凹函数,参考自什么是凹函数和凸函数?

  • 如果凹函数在区间上的局部极小值存在则一定是该区间最小值。参考自函数凹凸性和极值

  • L ( t ) = 0 \scriptsize L(t)=0 L(t)=0时取最小值同时也是极小值

如何找 t \scriptsize t t,使得 L ( t ) = 0 \scriptsize L(t)=0 L(t)=0,注意 L ( t ) \scriptsize L(t) L(t)取0是 L \scriptsize L L函数的极小值

核心思想:

第一步:随机给定一个初始值 t 0 \scriptsize t_0 t0

第二步:在这个位置观察 t \scriptsize t t应该如何调整能够使得函数值变小,即 L ( t 1 ) < L ( t 0 ) \scriptsize L(t_1)<L(t_0) L(t1)<L(t0)

第三步:不停根据第二步做调整,直到 L ( t n ) \scriptsize L(t_n) L(tn)足够接近0为止(或调整量足够小)

在这里插入图片描述

如何调整使函数值变小

具体如何调整呢?在t的领域内对比 L ( t − Δ t ) \scriptsize L(t-\Delta t) L(tΔt) L ( t + Δ t ) \scriptsize L(t+\Delta t) L(t+Δt)

L ( t − Δ t ) < L ( t ) \scriptsize L(t-\Delta t)<L(t) L(tΔt)<L(t),则 t 1 = t − Δ t \scriptsize t1=t-\Delta t t1=tΔt

L ( t + Δ t ) < L ( t ) \scriptsize L(t+\Delta t)<L(t) L(t+Δt)<L(t),则 t 1 = t + Δ t \scriptsize t1=t+\Delta t t1=t+Δt

由于考虑的是 t \scriptsize t t的领域内,因此 Δ t \scriptsize \Delta t Δt是大于0的无穷小数

可以改写为

L ( t ) − L ( t − Δ t ) > 0 \scriptsize L(t)-L(t-\Delta t)>0 L(t)L(tΔt)>0,则 t 1 = t − Δ t \scriptsize t_1=t-\Delta t t1=tΔt

L ( t + Δ t ) − L ( t ) < 0 \scriptsize L(t+\Delta t)-L(t)<0 L(t+Δt)L(t)<0,则 t 1 = t + Δ t \scriptsize t_1=t+\Delta t t1=t+Δt

合成后如下:
t 1 = t − L ( t + Δ t ) − L ( t ) ∣ L ( t + Δ t ) − L ( t ) ∣ Δ t t_{1}=t-\frac{L(t+\Delta t)-L(t)}{|L(t+\Delta t)-L(t)|} \Delta t t1=tL(t+Δt)L(t)L(t+Δt)L(t)Δt
在这里插入图片描述

t领域内

对式子进行变换
t 1 = t − L ( t + Δ t ) − L ( t ) ∣ L ( t + Δ t ) − L ( t ) ∣ Δ t (1) t_{1}=t-\frac{L(t+\Delta t)-L(t)}{|L(t+\Delta t)-L(t)|} \Delta t \tag1 t1=tL(t+Δt)L(t)L(t+Δt)L(t)Δt(1)

t 1 = t − L ( ( t + Δ t ) − L ( t ) Δ t ∣ L ( t + Δ t ) − L ( t ) Δ t ∣ Δ t = t − L ( t + Δ t ) − L ( t ) Δ t Δ t ∣ L ( t + Δ t ) − L ( t ) Δ t ∣ (2) t_{1}=t-\frac{\frac{L((t+\Delta t)-L(t)}{\Delta t}}{\left|\frac{L(t+\Delta t)-L(t)}{\Delta t}\right|} \Delta t=t-\frac{L(t+\Delta t)-L(t)}{\Delta t} \frac{\Delta t}{\left|\frac{L(t+\Delta t)-L(t)}{\Delta t}\right|} \tag2 t1=t ΔtL(t+Δt)L(t) ΔtL((t+Δt)L(t)Δt=tΔtL(t+Δt)L(t) ΔtL(t+Δt)L(t) Δt(2)

t 1 = t − α L ( t + Δ t ) − L ( t ) Δ t ( α → 0 + ) (3) t_{1}=t-\alpha \frac{L(t+\Delta t)-L(t)}{\Delta t} \quad\left(\alpha \rightarrow 0^{+}\right) \tag3 t1=tαΔtL(t+Δt)L(t)(α0+)(3)
得出迭代式, t 1 \scriptsize t_1 t1只需要取 t − α L ′ ( t ) \scriptsize t-\alpha L^{\prime}(t) tαL(t),就能够保证 L ( t 1 ) ≤ L ( t 0 ) \scriptsize L(t_1) \leq L(t_0) L(t1)L(t0),并使得 L \scriptsize L L逐渐下降,直到逼近0为止,注意这里的 α \scriptsize \alpha α必须取趋近于0的无穷小时,该行为才能恒成立, α \scriptsize \alpha α也被视作步长

然而真实世界是

t 1 = t − α L ( t + Δ t ) − L ( t ) Δ t ( α → 0 + ) t_{1}=t-\alpha \frac{L(t+\Delta t)-L(t)}{\Delta t} \quad\left(\alpha \rightarrow 0^{+}\right) t1=tαΔtL(t+Δt)L(t)(α0+)

  • 1.当 α \scriptsize \alpha α取无穷小时,虽然一定保证下降,但效率太慢
  • 2.日常设计的很多函数,可以运行使用相对大一些的步长,比如 α = 0.01 \scriptsize \alpha=0.01 α=0.01。原因在于虽然步长大了可能跳过合适位置,使得 L ( t 1 ) > L ( t 0 ) \scriptsize L(t_1)>L(t_0) L(t1)>L(t0),但是在下一个时刻,依旧可能跳回来使得 L ( t 2 ) < L ( t 1 ) \scriptsize L(t_2)<L(t_1) L(t2)<L(t1)
  • 3.大的步长不能保证一定收敛,但是大部分时候是可以很好的工作
  • 4.因此步长 α \scriptsize \alpha α,我们常称之为学习率,通常会给一个相对小的数字,但不会太小

代码如下

float x = 2;
float t = x / 2;
float L = (t * t - x) * (t * t - x);
float a = 0.01;

while(L > 1e-5){
    
    float delta = 2 * (t * t - x) * 2 * t;
    t = t - a * delta;
    L = (t * t - x) * (t * t - x);
}
cout << "result: " << t << endl;

第一步:初始化 t = x 2 t=\frac{x}{2} t=2x t \scriptsize t t也可以初始化为随机值,不过加入先验给一个更好的初始化,使得求解更迅速

第二步:计算 L ( t ) = ( t 2 − x ) 2 \scriptsize L(t)=(t^2-x)^2 L(t)=(t2x)2

第三步:若 L ( t ) > 1 e − 5 \scriptsize L(t)>1e-5 L(t)>1e5,则继续迭代,否则停止迭代(:delta足够小时也可以停止迭代)

第四步:计算导数 d L ( t ) d t = 2 ( t 2 − x ) ⋅ 2 t \scriptsize \frac{d L(t)}{d t}=2\left(t^{2}-x\right) \cdot 2 t dtdL(t)=2(t2x)2t

第五步:更新 t + = t − α ⋅ d L ( t ) d t \scriptsize t^{+}=t-\alpha \cdot \frac{d L(t)}{d t} t+=tαdtdL(t),这里 α \scriptsize \alpha α取0.01

第六步:继续执行第二步

从泰勒展开来理解梯度下降法

对于 f ( x ) \scriptsize f(x) f(x)的一阶泰勒展开,表示为:
f ( x ) = f ( x 0 ) + f ′ ( x 0 ) ( x − x 0 ) f(x)=f\left(x_{0}\right)+f^{\prime}\left(x_{0}\right)\left(x-x_{0}\right) f(x)=f(x0)+f(x0)(xx0)
对应的 L \scriptsize L L t 0 \scriptsize t_0 t0处的一阶泰勒展开近似表示为( t 1 \scriptsize t_1 t1 t 0 \scriptsize t_0 t0都是领域):
L ( t 1 ) = L ( t 0 ) + L ′ ( t 0 ) ( t 1 − t 0 ) L\left(t_{1}\right)=L\left(t_{0}\right)+L^{\prime}\left(t_{0}\right)\left(t_{1}-t_{0}\right) L(t1)=L(t0)+L(t0)(t1t0)
为了使得 L ( t 1 ) < L ( t 0 ) \scriptsize L(t_1)<L(t_0) L(t1)<L(t0)
L ( t 0 ) + L ′ ( t 0 ) ( t 1 − t 0 ) < L ( t 0 ) L ′ ( t 0 ) ( t 1 − t 0 ) < 0 L\left(t_{0}\right)+L^{\prime}\left(t_{0}\right)\left(t_{1}-t_{0}\right)<L\left(t_{0}\right) \\ L^{\prime}\left(t_{0}\right)\left(t_{1}-t_{0}\right)<0 L(t0)+L(t0)(t1t0)<L(t0)L(t0)(t1t0)<0

由于 t 0 \scriptsize t_0 t0 t 1 \scriptsize t_1 t1是领域,则 ∣ t 1 − t 0 ∣ = Δ t \scriptsize \left|t_{1}-t_{0}\right|=\Delta t t1t0=Δt,又 L ′ ( t 0 ) ( t 1 − t 0 ) < 0 \scriptsize L^{\prime}\left(t_{0}\right)\left(t_{1}-t_{0}\right)<0 L(t0)(t1t0)<0,则必有 L ′ ( t 0 ) \scriptsize L^{\prime}\left(t_{0}\right) L(t0) t 1 − t 0 \scriptsize t_1-t_0 t1t0符号相反


t 1 − t 0 = − L ′ ( t 0 ) ∣ L ′ ( t 0 ) ∣ Δ t t_{1}-t_{0}=-\frac{L^{\prime}\left(t_{0}\right)}{\left|L^{\prime}\left(t_{0}\right)\right|} \Delta t t1t0=L(t0)L(t0)Δt
则有
t 1 = t 0 − L ′ ( t 0 ) ∣ L ′ ( t 0 ) ∣ Δ t t 1 = t 0 − α L ′ ( t 0 ) ( α → 0 + ) t_{1}=t_{0}-\frac{L^{\prime}\left(t_{0}\right)}{\left|L^{\prime}\left(t_{0}\right)\right|} \Delta t \\ t_{1}=t_{0}-\alpha L^{\prime}\left(t_{0}\right) \quad\left(\alpha \rightarrow 0^{+}\right) t1=t0L(t0)L(t0)Δtt1=t0αL(t0)(α0+)
总结:

  • 1.梯度下降法是通过观察局部,决定如何调整的算法。如果函数具有多个极值,则可能陷入局部极值,此时初始点的选择直接影响收敛结果
  • 2.大的步长在一定程度上可能跨过局部极值,但也可能造成震荡导致不收敛
  • 3.步长的选择,需要根据函数的特性来找到合适取值,若导数特别大时,则步长取小,导数小时,步长取大。否则很容易造成收敛问题
  • 4.存在一类算法,可以在一定范围内搜索一个合适步长,使得每一次迭代更加稳定

3.牛顿法

问题思考方式:

第一步:转化问题,将 x \scriptsize \sqrt{x} x 转化为 L ( t ) = ( t 2 − x ) \scriptsize L(t)=(t^2-x) L(t)=(t2x),当 L ( t ) = 0 \scriptsize L(t)=0 L(t)=0时, t \scriptsize t t就是计算得出的结果

第二步:寻找合适的解,找 t \scriptsize t t使得 L ( t ) = ( t 2 − x ) = 0 \scriptsize L(t)=(t^2-x) = 0 L(t)=(t2x)=0

第三步:找到的 t \scriptsize t t,就是得出的结果

具体实现:

  • 考虑 L ( t ) \scriptsize L(t) L(t) t 0 \scriptsize t_0 t0处的切线与x轴交点作为 t 1 \scriptsize t_1 t1,不断逼近零点
  • 如下图所示,考虑以 o 2 ( t 0 , L ( t 0 ) ) \scriptsize o2(t_0,L(t_0)) o2(t0,L(t0))为原点,则切线可表示为 k = L ′ ( t 0 ) , b = 0 \scriptsize k=L^{\prime}\left(t_{0}\right),b=0 k=L(t0),b=0,而与x轴交点可表示为:

k = d ( o 2 , t 0 ) d ( t 1 , t 0 ) = L ( t 0 ) t 0 − t 1 = L ′ ( t 0 ) k=\frac{d\left(o 2, t_{0}\right)}{d\left(t_{1}, t_{0}\right)}=\frac{L\left(t_{0}\right)}{t_{0}-t_{1}}=L^{\prime}\left(t_{0}\right) k=d(t1,t0)d(o2,t0)=t0t1L(t0)=L(t0)

t 0 − t 1 = L ( t 0 ) L ′ ( t 0 ) t 1 = t 0 − L ( t 0 ) L ′ ( t 0 ) t_{0}-t_{1}=\frac{L\left(t_{0}\right)}{L^{\prime}\left(t_{0}\right)} \\ t_{1}=t_{0}-\frac{L\left(t_{0}\right)}{L^{\prime}\left(t_{0}\right)} t0t1=L(t0)L(t0)t1=t0L(t0)L(t0)

在这里插入图片描述

代码如下

float x = 2;
float t = x/2;
float L = t * t - x;

while(abs(L) > 1e-5){
    
    float dL = 2 * t;
    t = t - L / dL;
    L = t * t - x;
}
cout << "simple_nt1: " << t << endl;

第一步:随机给定一个初始值 t = x 2 t=\frac{x}{2} t=2x

第二步:计算 L ( t ) \scriptsize L(t) L(t) L ′ ( t ) \scriptsize L^{\prime}(t) L(t)

第三步:更新 t + = t − L ( t ) L ′ ( t ) \scriptsize t^{+}=t-\frac{L(t)}{L^{\prime}(t)} t+=tL(t)L(t)

第四步:不停根据规则2做调整,直到 L ( t ) \scriptsize L(t) L(t)足够接近0为止(或调整量足够小)

从泰勒展开来理解牛顿法

对于 f ( x ) \scriptsize f(x) f(x)的一阶泰勒展开,表示为:
f ( x ) = f ( x 0 ) + f ′ ( x 0 ) ( x − x 0 ) f(x)=f\left(x_{0}\right)+f^{\prime}\left(x_{0}\right)\left(x-x_{0}\right) f(x)=f(x0)+f(x0)(xx0)
对应的 L \scriptsize L L t 0 \scriptsize t_0 t0处的一阶泰勒展开近似表示为( t 1 \scriptsize t_1 t1 t 0 \scriptsize t_0 t0都是领域):
L ( t 1 ) = L ( t 0 ) + L ′ ( t 0 ) ( t 1 − t 0 ) L\left(t_{1}\right)=L\left(t_{0}\right)+L^{\prime}\left(t_{0}\right)\left(t_{1}-t_{0}\right) L(t1)=L(t0)+L(t0)(t1t0)
L ( t 1 ) = 0 \scriptsize L(t_1)=0 L(t1)=0
L ( t 0 ) + L ′ ( t 0 ) ( t 1 − t 0 ) = 0 t 1 − t 0 = − L ( t 0 ) L ′ ( t 0 ) t 1 = t 0 − L ( t 0 ) L ′ ( t 0 ) L\left(t_{0}\right)+L^{\prime}\left(t_{0}\right)\left(t_{1}-t_{0}\right)=0 \\ t_{1}-t_{0}=-\frac{L\left(t_{0}\right)}{L^{\prime}\left(t_{0}\right)} \\ t_{1}=t_{0}-\frac{L\left(t_{0}\right)}{L^{\prime}\left(t_{0}\right)} L(t0)+L(t0)(t1t0)=0t1t0=L(t0)L(t0)t1=t0L(t0)L(t0)
若函数二阶可导,则可考虑导函数零点时方程的根

根据 t + = t − L ( t ) L ′ ( t ) \small t^{+}=t-\frac{L(t)}{L^{\prime}(t)} t+=tL(t)L(t),可令 L = f ′ ( t ) \small L=f^{\prime}(t) L=f(t),得出 t + = t − f ′ ( t ) f ′ ′ ( t ) \small t^{+}=t-\frac{f^{\prime}(t)}{f^{\prime\prime}(t)} t+=tf′′(t)f(t)。则函数为 L ( t ) = ( t 2 − x ) 2 \small L(t)=(t^2-x)^2 L(t)=(t2x)2

代码如下

float x = 2;
float t = x / 2;
float L = (t * t - x) * (t * t - x);

while(L > 1e-5){
    
    float d1L = 2 * (t * t - x) * 2 * t;
    float d2L = 4 * t * 2 * t + 4 * (t * t - x);
    t = t - d1L / d2L;
    L = (t * t - x) * (t * t - x);
}
cout << "simple_nt2: " << t << endl;

第一步:随机给定一个初始值 t = x 2 \small t=\frac{x}{2} t=2x

第二步:计算 L ( t ) \small L(t) L(t) L ′ ( t ) \small L^{\prime}(t) L(t)

第三步:更新 t + = t − L ( t ) L ′ ( t ) \small t^{+}=t-\frac{L(t)}{L^{\prime}(t)} t+=tL(t)L(t)

第四步:不停根据规则2做调整,直到 L ( t ) \scriptsize L(t) L(t)足够接近0为止(或调整量足够小)

结语

本次课程主要通过求解根号2学习了梯度下降法和牛顿法等相关算法

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱听歌的周童鞋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值