机器人中的数值优化|【二】最速下降法,可行牛顿法的python实现,以Rosenbrock function为例

news2024/11/28 19:27:05

机器人中的数值优化|【二】最优化方法:最速下降法,可行牛顿法的python实现,以Rosenbrock function为例

在上一节中提到了我们详细探讨了数值优化/最优化理论中的基本概念和性质,现在开始使用python对算法进行实现。上一节链接:
机器人中的数值优化|【一】数值优化基础

导入依赖

导入依赖库并定义常量,C_CONSTANT为步长超参数,取0~1之间,停机准则STOP_CONSTANT,意为停止门限,当梯度的无穷范数低于这个门限时我们认为精度已经达到需求,停止进行计算

import numpy as np 
import sympy as sp 
from sympy import hessian
from sympy import oo
import numpy as np
from scipy.linalg import solve_triangular
import matplotlib.pyplot as plt
from types import MethodType
import time

# 步长超参数,取0~1之间
C_CONSTANT = 0.9
# 停止门限,当梯度的无穷范数低于这个门限时我们认为精度已经达到需求,停止进行计算
STOP_CONSTANT = 1e-6

定义基类

首先我们定义一个基本的优化方法类ZOptimization,在类中对法各种方法进行抽象的实现

class ZOptimization(object):
    def  __init__(self, dimensions) -> None:
        self._optimizationName = 'z optimization'
        self._version = '0.0.0.1'
        self._dimensions = dimensions
        self._core = None
    
        
    def _getGradient(self, function, variables):
    	# sympy没有专门的求梯度函数,我们这里手动求偏导数再组装成向量
        gradientList = []
        for variable in variables:
            gradient_i = sp.diff(function, variable)
            gradientList.append(gradient_i)
        return sp.Matrix(gradientList)

    def _getHessian(self, function, variables):
    	# 直接调用自身hessian方法
        return hessian(function, variables)

    def _getProblemToSolve(self, dimensions):
        return None, None
    
    def _getDirectionFunction(self, gradient, hessian, variables, x_k):
        return None

    def _getConditionFunction(self, function, variables, direction, tau, x_k, gradient):
        return None

    def _update(self, x_k, tau, direction):
        return [x_k[i] + tau * direction[i] for i in range(len(x_k))]
        
    def search(self):
    	#初始化坐标点,这里默认坐标点在每个纬度上的值都为-2
        x_k = [-2] * self._dimensions
        iter = 0
        functionToSolve, variables = self._getProblemToSolve(self._dimensions)
        gradient_ = self._getGradient(functionToSolve, variables)
        hessian_ = self._getHessian(functionToSolve, variables)
        direction = self._getDirectionFunction(gradient_, hessian_, variables, x_k)
        gradientVector = gradient_.subs([(variables[i], x_k[i]) for i in range(len(variables))])
        gradientNorm = gradientVector.norm(oo)
        # 记录一下迭代过程用于作图
        x_k_list = []
        z_list = []
        while gradientNorm >= STOP_CONSTANT:
            tau = self._getConditionFunction(functionToSolve, variables, direction, 1.0, x_k, gradient_)
            x_k = self._update(x_k, tau, direction)
            direction = self._getDirectionFunction(gradient_, hessian_, variables, x_k)
            iter = iter + 1
            gradientVector = gradient_.subs([(variables[i], x_k[i]) for i in range(len(variables))])
            gradientNorm = gradientVector.norm(oo)
            print("norm {}".format(gradientNorm))
            print("Iter {}, x_k is {}, gradient is {}".format(iter, x_k, gradientNorm))
            x_k_list.append(x_k)
            z_list.append(functionToSolve.subs([(variables[i], x_k[i]) for i in range(len(variables))]))
        return x_k, functionToSolve.subs([(variables[i], x_k[i]) for i in range(len(variables))]), x_k_list, z_list

这里我们设计了三个抽象方法_getDirectionFunction, _getConditionFunction, _getProblemToSolve,用于根据不同的情况下为我们的求解器类分别配置不同的优化方向计算模块,优化步长条件计算模块和自定义的问题模块(即我们自定义一个函数和函数的变量)。
_getGradient用于计算梯度,_getHessian用于计算Hessian矩阵,_update用于更新座标点。

线性搜索

在优化问题中,我们常用的架构无非是:

  • 搜索下降方向
  • 搜索确定下降步长
  • 迭代确定新位置

在下降步长这里,最速下降法Steepest Gradient Descend和可行牛顿法Practical Newton’s Method我们都采用Armijo Condition作为判断,回顾机器人中的数值优化|【一】数值优化基础,我们有

Backtracking/Armijo line search

  1. 选择搜索方向: d = − ∇ f ( x k ) d=-\nabla f\left(x^k\right) d=f(xk)
  2. f ( x k + τ d ) > f ( x k ) + c ⋅ τ d T ∇ f ( x k ) f\left(x^k+\tau d\right)>f\left(x^k\right)+c \cdot \tau d^T \nabla f\left(x^k\right) f(xk+τd)>f(xk)+cτdTf(xk)时,重复 τ ← τ / 2 \tau \leftarrow \tau/2 ττ/2
  3. 迭代 x k + 1 = x k + τ d x^{k+1}=x^k+\tau d xk+1=xk+τd

因此,直接进行代码实现

#不等式左侧计算
def ArmijoConditionLeft(function, variables, direction, tau, x_k):
    return float(function.subs([(variables[i], x_k[i] + tau * direction[i]) for i in range(len(variables))]))

#不等式右侧计算
def ArmijoConditionRight(function, variables, direction, tau, x_k, gradient):
    gd = gradient.subs([(variables[i], x_k[i]) for i in range(len(variables))])
    dgd = direction.T * gd
    return float(function.subs([(variables[i], x_k[i]) for i in range(len(variables))])) + \
        float(C_CONSTANT * tau * dgd[0])

#不等式迭代
def ArmijoCondition(self, function, variables, direction, tau, x_k, gradient):
    while ArmijoConditionLeft(function, variables, direction, tau, x_k) > \
        ArmijoConditionRight(function, variables, direction, tau, x_k, gradient):
        tau = tau / 2.0
    return tau

搜索下降方向

最速下降法Steepest Gradient Descent Method

最速梯度下降的迭代形式如下所示
x k + 1 = x k − τ ∇ f ( x k ) x^{k+1}=x^k-\tau \nabla f\left(x^k\right) xk+1=xkτf(xk)
代码中则为

def lineSearchDirection(self, gradientVector, hessianMatrix, variables, x_k):
    return -gradientVector.subs([(variables[i], x_k[i]) for i in range(len(variables))])

可行牛顿法Practical Newton’s Method

首先初始化 x x x, x ← x 0 ∈ R n x \leftarrow x_0 \in \mathbb{R}^n xx0Rn
∣ ∣ ∇ f ( x ) ∣ ∣ > δ ||\nabla f(x)|| > \delta ∣∣∇f(x)∣∣>δ时,进行如下计算
d o do do
d ← − M − 1 ∇ f ( x ) d \leftarrow -M^{-1} \nabla f(x) dM1f(x)
t ← b a c k t r a c k i o n g l i n e s e a r c h t \leftarrow backtrackiong \quad line \quad search tbacktrackionglinesearch
x ← x + t d x \leftarrow x + td xx+td
e n d w h i l e end \quad while endwhile
r e t u r n return return
其中,M是一个接近Hessian阵的正定矩阵,以此来替代线性搜索中的求梯度和求Hessian阵。
如果函数为凸函数,则有
M = ∇ 2 f ( x ) + ϵ I , ϵ = min ⁡ ( 1 , ∥ ∇ f ( x ) ∥ ∞ ) / 10 \boldsymbol{M}=\nabla^2 f(\boldsymbol{x})+\epsilon \boldsymbol{I}, \epsilon=\min \left(1,\|\nabla f(\boldsymbol{x})\|_{\infty}\right) / 10 M=2f(x)+ϵI,ϵ=min(1,∥∇f(x))/10
因为M是正定的,因此可以使用Cholesky factorization
M d = − ∇ f ( x ) , M = L L T \boldsymbol{M} \boldsymbol{d}=-\nabla f(\boldsymbol{x}), \boldsymbol{M}=\boldsymbol{L} \boldsymbol{L}^{\mathrm{T}} Md=f(x),M=LLT
如果函数是非凸的,那么我们通过如下计算M
Bunch-Kaufman Factorization:
M d = − ∇ f ( x ) , M = L B L T \boldsymbol{M} \boldsymbol{d}=-\nabla f(\boldsymbol{x}), \boldsymbol{M}=\boldsymbol{L} \boldsymbol{B} \boldsymbol{L}^{\mathrm{T}} Md=f(x),M=LBLT
代码实现为

def practicalNewtonDirection(self, gradientVector, hessianMatrix, variables, x_k):
    gradientVectorValue = gradientVector.subs([(variables[i], x_k[i]) for i in range(len(variables))])
    epsilon = min(1, gradientVectorValue.norm(oo))/10.0
    M = hessianMatrix.subs([(variables[i], x_k[i]) for i in range(len(variables))]) + epsilon * sp.eye(len(variables))
    M = np.matrix(M.tolist()).astype(np.float64)
    gradientVectorValue = np.matrix(gradientVectorValue.tolist()).astype(np.float64)
    direction = cholesky_solve(M, -gradientVectorValue)
    direction = sp.Matrix(direction.tolist())
    return direction

# cholesky分解,使用cholesky分解是因为通过将正定矩阵进行LU分解
# 使用LU分解后的矩阵进行线性方程求解的速度比对原M矩阵求逆矩阵的速度要快很多
def cholesky_solve(A, b):
    L = np.linalg.cholesky(A)
    y = solve_triangular(L, b, lower=True, check_finite=False)
    x = solve_triangular(L.T, y, lower=False, check_finite=False)
    return x

注意可行牛顿法在这里使用了cholesky分解,使用cholesky分解是因为通过将正定矩阵进行LU分解,使用LU分解后的矩阵进行线性方程求解的速度比对原M矩阵求逆矩阵的速度要快很多,但是值得注意的是cholesky分解的时间复杂度仍然为 O ( N 3 ) O(N^3) O(N3)

问题定义

Rosenbrock function定义
如图所示的一个2N维优化问题,我们可以把优化问题定义为如下所示

# 输入:维度 输出: 方程、变量列表
def initialProblem(self, dimensions):
    functionToSolve = 0
    variables = []
    for i in range(dimensions):
        variables.append(sp.Symbol('x'+str(i+1)))
    for i in range(int(dimensions/2)):
        functionToSolve = functionToSolve + 100 * ( variables[2*i] * variables[2*i] - variables[2*i+1] ) * \
             ( variables[2*i] * variables[2*i] - variables[2*i+1] ) + \
                ( variables[2*i] - 1 ) * ( variables[2*i] - 1 )
    return functionToSolve, variables

使用代理模式,动态添加求解器类对象的方法

在这里由于求解器类中的几个方法都是抽象方法,所以需要体我们手动区实现。但是每个类我们根据需要去修改太麻烦了,能不能更根据我们的需要,在抽象的求解器基类“小车”上为我们动态添加我们需要的“工具”呢?这里我想到了Java的设计模式,代理模式(也有可能是其他名称,笔者混淆了)。通过定义好接口的基类,在抽象的基类上添加具体实现了方法的代理来完成各种复杂功能的组装。
但是,python中并没有接口这样的概念,因此我们换一种方法,通过动态添加方法来实现,具体如下所示。

if __name__ == '__main__':
	#定义一个2维的Rosenbrock优化问题
    zop = ZOptimization(2)
    # 使用MethodType方法为类动态增加方法
    zop._getProblemToSolve = MethodType(initialProblem, zop)
    # zop._getDirectionFunction = MethodType(practicalNewtonDirection, zop)
    zop._getDirectionFunction = MethodType(lineSearchDirection, zop)
    zop._getConditionFunction = MethodType(ArmijoCondition, zop)
    tick = time.time()
    x_k, res, x_k_list, z_list = zop.search()
    timeSpend = time.time() - tick
    print("[optimization] Time cost is {} ns".format(timeSpend))
    
    x_k_x = []
    x_k_y = []
    for i in range(len(x_k_list)):
        x_k_x.append(x_k_list[i][0])
        x_k_y.append(x_k_list[i][1])
    fig, ax3d = plt.subplots(subplot_kw={"projection": "3d"})
    ax3d.plot(x_k_x, x_k_y, z_list, 'ro-', label='Curve')
    X = np.arange(-3, 3, 0.25)
    Y = np.arange(-3, 3, 0.25)
    X, Y = np.meshgrid(X, Y)
    Z = 100 * (X * X - Y) * (X * X - Y) + (X - 1) * (X - 1)
    ax3d.plot_surface(X, Y, Z, edgecolor='royalblue', lw=0.5, alpha=0.3, cmap='plasma')
    ax3d.set_title('optimization of Rosenbrock function')
    plt.show()

值得注意的是,为类动态增加方法时,我们的方法即使没有使用到self,也要在参数列表中添加这一项,否则就会报错,这一点很容易被忽略掉。比如:

# 输入:维度 输出: 方程、变量列表
def initialProblem(self, dimensions):
    functionToSolve = 0
    variables = []
    for i in range(dimensions):
        variables.append(sp.Symbol('x'+str(i+1)))
    for i in range(int(dimensions/2)):
        functionToSolve = functionToSolve + 100 * ( variables[2*i] * variables[2*i] - variables[2*i+1] ) * \
             ( variables[2*i] * variables[2*i] - variables[2*i+1] ) + \
                ( variables[2*i] - 1 ) * ( variables[2*i] - 1 )
    return functionToSolve, variables

我们看到它虽然没有定义在类内,但是依然需要self参数。

实验结果

以2维Rosenbrock问题为例,当我们以[-2,-2]为优化的起点,停机条件为1e-6时,SGD用时80s左右,迭代2414轮;
NPM用时3s左右,迭代222轮。迭代过程如下所示
SGD
PNM我们可以发现PNM的迭代步长相比于SGD更大,因此每一步效率更高。而SGD严格按照梯度下降。
PNM没有按照梯度下降的原因是PNM在迭代点处进行了二阶泰勒展开,这是一把双刃剑,虽然可以直接求出最优的迭代方向,但是又以后因为二阶展开产生了误差。

全部代码

'''
Description: optimization tutorials
version: 0.0.0.1
Author: Alexios Zhou
Date: 2023-01-09 10:41:48
LastEditors: Alexios Zhou
LastEditTime: 2023-01-10 15:26:40
'''

import numpy as np 
import sympy as sp 
from sympy import hessian
from sympy import oo
import numpy as np
from scipy.linalg import solve_triangular
import matplotlib.pyplot as plt
from types import MethodType
import time

C_CONSTANT = 0.9
STOP_CONSTANT = 1e-6

def initialProblem(self, dimensions):
    functionToSolve = 0
    variables = []
    for i in range(dimensions):
        variables.append(sp.Symbol('x'+str(i+1)))
    for i in range(int(dimensions/2)):
        functionToSolve = functionToSolve + 100 * ( variables[2*i] * variables[2*i] - variables[2*i+1] ) * \
             ( variables[2*i] * variables[2*i] - variables[2*i+1] ) + \
                ( variables[2*i] - 1 ) * ( variables[2*i] - 1 )
    return functionToSolve, variables

def lineSearchDirection(self, gradientVector, hessianMatrix, variables, x_k):
    return -gradientVector.subs([(variables[i], x_k[i]) for i in range(len(variables))])

def practicalNewtonDirection(self, gradientVector, hessianMatrix, variables, x_k):
    gradientVectorValue = gradientVector.subs([(variables[i], x_k[i]) for i in range(len(variables))])
    epsilon = min(1, gradientVectorValue.norm(oo))/10.0
    M = hessianMatrix.subs([(variables[i], x_k[i]) for i in range(len(variables))]) + epsilon * sp.eye(len(variables))
    M = np.matrix(M.tolist()).astype(np.float64)
    gradientVectorValue = np.matrix(gradientVectorValue.tolist()).astype(np.float64)
    direction = cholesky_solve(M, -gradientVectorValue)
    direction = sp.Matrix(direction.tolist())
    return direction

def cholesky_solve(A, b):
    L = np.linalg.cholesky(A)
    y = solve_triangular(L, b, lower=True, check_finite=False)
    x = solve_triangular(L.T, y, lower=False, check_finite=False)
    return x

def ArmijoConditionLeft(function, variables, direction, tau, x_k):
    return float(function.subs([(variables[i], x_k[i] + tau * direction[i]) for i in range(len(variables))]))

def ArmijoConditionRight(function, variables, direction, tau, x_k, gradient):
    gd = gradient.subs([(variables[i], x_k[i]) for i in range(len(variables))])
    dgd = direction.T * gd
    return float(function.subs([(variables[i], x_k[i]) for i in range(len(variables))])) + \
        float(C_CONSTANT * tau * dgd[0])

def ArmijoCondition(self, function, variables, direction, tau, x_k, gradient):
    while ArmijoConditionLeft(function, variables, direction, tau, x_k) > \
        ArmijoConditionRight(function, variables, direction, tau, x_k, gradient):
        tau = tau / 2.0
    return tau

class Core(object):
    def __init__(self) -> None:
        self._coreNam = 'core'

class ZOptimization(object):
    def  __init__(self, dimensions) -> None:
        self._optimizationName = 'z optimization'
        self._version = '0.0.0.1'
        self._dimensions = dimensions
        self._core = None
    
        
    def _getGradient(self, function, variables):
        gradientList = []
        for variable in variables:
            gradient_i = sp.diff(function, variable)
            gradientList.append(gradient_i)
        return sp.Matrix(gradientList)

    def _getHessian(self, function, variables):
        return hessian(function, variables)

    def _getProblemToSolve(self, dimensions):
        return None, None
    
    def _getDirectionFunction(self, gradient, hessian, variables, x_k):
        return None

    def _getConditionFunction(self, function, variables, direction, tau, x_k, gradient):
        return None

    def _update(self, x_k, tau, direction):
        return [x_k[i] + tau * direction[i] for i in range(len(x_k))]
        
    def search(self):
        x_k = [-2] * self._dimensions
        iter = 0
        functionToSolve, variables = self._getProblemToSolve(self._dimensions)
        gradient_ = self._getGradient(functionToSolve, variables)
        hessian_ = self._getHessian(functionToSolve, variables)
        direction = self._getDirectionFunction(gradient_, hessian_, variables, x_k)
        gradientVector = gradient_.subs([(variables[i], x_k[i]) for i in range(len(variables))])
        gradientNorm = gradientVector.norm(oo)
        x_k_list = []
        z_list = []
        while gradientNorm >= STOP_CONSTANT:
            tau = self._getConditionFunction(functionToSolve, variables, direction, 1.0, x_k, gradient_)
            x_k = self._update(x_k, tau, direction)
            direction = self._getDirectionFunction(gradient_, hessian_, variables, x_k)
            iter = iter + 1
            gradientVector = gradient_.subs([(variables[i], x_k[i]) for i in range(len(variables))])
            gradientNorm = gradientVector.norm(oo)
            print("norm {}".format(gradientNorm))
            print("Iter {}, x_k is {}, gradient is {}".format(iter, x_k, gradientNorm))
            x_k_list.append(x_k)
            z_list.append(functionToSolve.subs([(variables[i], x_k[i]) for i in range(len(variables))]))
        return x_k, functionToSolve.subs([(variables[i], x_k[i]) for i in range(len(variables))]), x_k_list, z_list


if __name__ == '__main__':
    zop = ZOptimization(2)
    zop._getProblemToSolve = MethodType(initialProblem, zop)
    # zop._getDirectionFunction = MethodType(practicalNewtonDirection, zop)
    zop._getDirectionFunction = MethodType(lineSearchDirection, zop)
    zop._getConditionFunction = MethodType(ArmijoCondition, zop)
    tick = time.time()
    x_k, res, x_k_list, z_list = zop.search()
    timeSpend = time.time() - tick
    print("[optimization] Time cost is {} ns".format(timeSpend))
    
    x_k_x = []
    x_k_y = []
    for i in range(len(x_k_list)):
        x_k_x.append(x_k_list[i][0])
        x_k_y.append(x_k_list[i][1])
    fig, ax3d = plt.subplots(subplot_kw={"projection": "3d"})
    ax3d.plot(x_k_x, x_k_y, z_list, 'ro-', label='Curve')
    X = np.arange(-3, 3, 0.25)
    Y = np.arange(-3, 3, 0.25)
    X, Y = np.meshgrid(X, Y)
    Z = 100 * (X * X - Y) * (X * X - Y) + (X - 1) * (X - 1)
    ax3d.plot_surface(X, Y, Z, edgecolor='royalblue', lw=0.5, alpha=0.3, cmap='plasma')
    ax3d.set_title('optimization of Rosenbrock function')
    plt.show()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/152988.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CVE-2021-25296 复现

# 漏洞描述 名称&#xff1a;Apache OFBiz rmi反序列化漏洞 cve编号&#xff1a;cve-2021-25296 危害&#xff1a;未授权远程命令执行 影响版本&#xff1a;Apache OFBiz < 17.12.06 OFBiz是一个非常著名的电子商务平台&#xff0c;是一个非常著名的开源项目&#xff0…

Python识别屏幕题目并模拟做题

前言 马上就要过年了&#xff0c;有许多小伙伴们本本还没拿到&#xff0c;还在苦苦刷题&#xff0c;一直及格不了&#xff0c;现在&#xff0c;我们用Python模拟做题&#xff0c;看看效果。 环境使用 python 3.9pycharm 模块使用 requestsreselenium谷歌驱动 import reimpor…

动态规划|474. 一和零

题目看上去很唬人&#xff0c;但是恰恰是这样说明该题设计的目的很强&#xff0c;指向dp的01背包&#xff0c;就是为了考01背包设计的。 像极了中学时代的那种看上去花里胡哨&#xff0c;实质上是根据考点设计出题的题目。&#xff08;这种题看破出题意图&#xff0c;往往都很简…

电脑是自动获取ip,VMware安装linux时候,设置固定ip并且能访问外网

首先虚拟机网络模式是NAT模式。设置主机名和打开网络&#xff0c;也可以不设置主机名&#xff1a;安装好后&#xff0c;设置linux的ip地址。执行vi /etc/sysconfig/network-scripts/ifcfg-ens33&#xff0c;修改里面的ip配置&#xff1a;注意IP的范围。查看ip的范围的方法如下图…

Day 5 Spring的后处理器

1 Spring后处理器Spring的后处理器是Spring对外开发的重要扩展点&#xff0c;允许我们介入到整个Bean实例化流程中来&#xff0c;以达到动态注册BeanDefinition&#xff0c;动态修改BeanDefinition&#xff0c;以及动态修改Bean的作用。BeanFactoryPostProcessor: Bean工厂后处…

基于蜜蜂算法求解电力系统经济调度(Matlab代码实现)

目录 1 蜜蜂优化算法 1.1 蜂群觅食机制 1.2 蜜蜂算法 1.3 流程 2 经济调度 3 运行结果 4 参考文献 5 Matlab代码实现 1 蜜蜂优化算法 蜜蜂算法( Bees Algorithm&#xff0c;BA) 由英国学者 AfshinGhanbarzadeh 和他的研究小组于 2005 年提出。该算法是一种有别于蚁群…

学习使用php获取数组最大值并返回对应键名max和array_search函数,最后一个元素的值使用end函数

在php中&#xff0c;可以使用max函数和array_search函数获得数组的最大值&#xff0c;同时获得最大值对应的键名解决方案打印结果解决方案 使用max函数获得数组的最大值&#xff0c;并使用array_search函数找到最大值对应的键名&#xff0c;键名保存在$key变量中。 end() 函数…

android架构拆分方案

编译拆https://blog.csdn.net/dongyi1988/article/details/128629011结构拆https://blog.csdn.net/dongyi1988/article/details/128633808一、背景android设备已经遍及各行各业&#xff0c;手机整个项目阶段包括需求沟通&#xff0c;硬件设计打板&#xff0c;研发联调&#xff…

新网站如何快速被收录?网站收录如何查询

新网站如何快速被收录? 首先是向搜索引擎提交 各大搜索引擎都向网站提供了自动提交功能&#xff0c;而百度搜索引擎也有网站提交入口&#xff0c;主动提交网站能够增加百度收录几率&#xff0c;让搜索引擎尽快发现我们的网站已经上线。 然后是做好SEO优化&#xff1b; 1.网站内…

强(矩阵快速幂)

题目描述 Lh&#xff1a;粉兔你教我一下抽屉原理吧 Clz&#xff1a;就是给你一个长度为 n 的序列&#xff0c;每个数只能取 0,1,2&#xff0c;那你连续取三个数必然有两个相等…… Lh&#xff1a;等等你梭啥&#xff0c;再说一遍 Clz&#xff1a;……emmm 当我没说 Marser&…

测试框架 Jest 实用教程

官网 https://jestjs.io/docs/getting-started 安装 cnpm i --save-dev jest使用 在项目中的任意位置&#xff08;通常单独建个名为 test 的文件夹&#xff09;新建以 .test.js 为后缀的测试文件&#xff0c;如 expect.test.js &#xff08;若项目中有 test.js 存在&#xff0c…

【阶段三】Python机器学习10篇:机器学习项目实战:K近邻算法的基本原理、计算步骤与KNN(K近邻)分类模型

本篇的思维导图: K近邻算法(英文为K-Nearest Neighbor,因而又简称KNN算法)是非常经典的机器学习算法。 K近邻算法的基本原理 K近邻算法的原理非常简单:对于一个新样本,K近邻算法的目的就是在已有数据中寻找与它最相似的K个数据,或者说“离它最近”的K个数…

设计模式——备忘录模式

备忘录模式一、基本思想二、应用场景三、结构图四、代码五、优缺点5.1 优点5.2 缺点一、基本思想 在不破坏封装性的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对象之外保存这个状态&#xff0c;以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫…

Bilibili支持了AV1编码,关于AV1编码你知道吗?

Bilibili支持了AV1编码&#xff0c;关于AV1编码你知道吗&#xff1f; AV1编码是一种新的视频编码标准&#xff0c;由联合开发的开源编码器&#xff0c;它由英特尔、微软、谷歌、苹果、Netflix、AMD、ARM、NVIDIA和其他一些公司共同开发&#xff0c;旨在替代H.264和HEVC等现有的…

Word控件 Aspose.words for.NET 授权须知

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

实验 2 灰度变换与空间滤波

目录实验 2 灰度变换与空间滤波一、实验目的二、实验例题1. 灰度变换函数 imadjust2. 使用对数变换压缩动态范围。3. 直方图均衡化 histogram equalization实验 2 灰度变换与空间滤波 一、实验目的 掌握灰度变换的原理和应用。掌握对数变换、幂律变换和直方图均衡化的原理和应…

融合注意力模块SE基于轻量级yolov5s实践路面坑洼目标检测系统

在很多的项目实战中验证分析注意力机制的加入对于模型最终性能的提升发挥着积极正向的作用&#xff0c;在我之前的一些文章里面也做过了一些尝试&#xff0c;这里主要是想基于轻量级的s系列模型来开发构建路面坑洼检测系统&#xff0c;在模型中加入SE注意力模块&#xff0c;以期…

Android开发-AS学习(二)

1.5 ProgressBar常用属性描述android:max进度条的最大值android:progress进度条已完成进度值android:indeterminate如果设置为true&#xff0c;则进度条不精确显示进度style“&#xff1f;android:attr/progressBarStyleHorizontal"水平进度条MainActivity.java package c…

机试_1_暴力求解

一、枚举 判断是否可以使用枚举&#xff1a;分析数据量。 若时间限制在1000ms的情况下&#xff0c;大约可以进行10⁷的运算。 复杂度数据量O(n!)10O(2ⁿ)20O(n)200O(n)3000O(nlogn)10⁶O(n)10⁷O(√10)10⁴O(logn)>10⁰1 abc–清华大学 描述 设a、b、c均是0到9之间的数字…

Java课程设计——学生成绩管理系统

1 需求分析1.1 需求分析概述需求分析是开发软件系统的重要环节&#xff0c;是系统开发的第一步和基础环节。通过需求分析充分认识系统的目标、系统的各个组成部分、各部分的任务职责、工作流程、工作中使用的各种数据及数据结构、各部门的业务关系和数据流程等&#xff0c; 为系…