【SLAM】最最最简单的直线拟合情形下的多种求解方法

本文我们讨论一个最最最简单情况下的拟合的情形,并尝试使用不同的方法来进行求解。

假如有一组数 x 1 , x 2 , x 3 , . . . , x n x_1,x_2,x_3,...,x_n x1,x2,x3,...,xn,对应的值为 y 1 , y 2 , y 3 , . . . , y n y_1,y_2,y_3,...,y_n y1,y2,y3,...,yn,并且 y y y x x x之间近似满足 y = k x + b y=kx+b y=kx+b的关系。求解出最佳的k的值。

为了方便后续的代码编写,这边就将 x i x_i xi y i y_i yi都定义下:

std::vector<Eigen::Vector2d> data = {{2.5, 2.4}, {0.5, 0.7}, {2.2, 2.9}, {1.9, 2.2}, {3.1, 3.0}, {2.3, 2.7}, {2, 1.6}, {1, 1.1}, {1.5, 1.6}, {1.1, 0.9}
};

最小二乘法

微分法

按照最小二乘法的定义:

假设 y ^ \hat{y} y^为满足近似关系的 y y y的值,即 y ^ i = k x i + b \hat{y}_i=kx_i+b y^i=kxi+b,那么也就是求解误差平方和

S S E = ∑ i = 1 n ( y i − y ^ i ) 2 = ∑ i = 1 n [ y i − ( k x i + b ) ] 2 SSE=\sum_{i=1}^{n}(y_i-\hat{y}_i)^{2}=\sum_{i=1}^{n}[y_i-(kx_i+b)]^{2} SSE=i=1n(yiy^i)2=i=1n[yi(kxi+b)]2

最小时的 k k k值。

这里,我们将误差视为关于 k k k b b b的函数,那么问题就变成了求各自偏导的问题了:

∂ S S E ∂ k = ∑ i = 1 n ( 2 k x i 2 − 2 x i y i + 2 b x i ) \frac{\partial SSE}{\partial k}=\sum_{i=1}^{n}(2kx_i^2-2x_iy_i+2bx_i) kSSE=i=1n(2kxi22xiyi+2bxi)

∂ S S E ∂ b = ∑ i = 1 n ( 2 b − 2 y i + 2 k x i ) \frac{\partial SSE}{\partial b}=\sum_{i=1}^{n}(2b-2y_i+2kx_i) bSSE=i=1n(2b2yi+2kxi)

令各自偏导数等于0,求解出:

k = n ∑ i = 1 n ( x i y i ) − ∑ i = 1 n ( x i ) ∑ i = 1 n ( y i ) n ∑ i = 1 n ( x i ) 2 − ( ∑ i = 1 n ( x i ) ) 2 k=\frac{n\sum_{i=1}^{n}(x_iy_i)-\sum_{i=1}^{n}(x_i)\sum_{i=1}^{n}(y_i)}{n\sum_{i=1}^{n}(x_i)^2-(\sum_{i=1}^{n}(x_i))^2} k=ni=1n(xi)2(i=1n(xi))2ni=1n(xiyi)i=1n(xi)i=1n(yi)

b = ∑ i = 1 n y i − k ∑ i = 1 n x i n b=\frac{\sum_{i=1}^{n}y_i-k\sum_{i=1}^{n}x_i}{n} b=ni=1nyiki=1nxi

下面的代码就使用该公式直接求解:

#include <iostream>
#include <vector>
#include <numeric>int main() {// 创建一个二维数据集std::vector<Eigen::Vector2d> data = {{2.5, 2.4}, {0.5, 0.7}, {2.2, 2.9}, {1.9, 2.2}, {3.1, 3.0}, {2.3, 2.7}, {2, 1.6}, {1, 1.1}, {1.5, 1.6}, {1.1, 0.9}};double x_sum = 0.0;double y_sum = 0.0;double xy_sum = 0.0;double x2_sum = 0.0;for (const auto& point : data) {x_sum += point[0];y_sum += point[1];xy_sum += point[0] * point[1];x2_sum += point[0] * point[0];}double n = static_cast<double>(data.size());// 使用微分法求解double slope = (n * xy_sum - x_sum * y_sum) / (n * x2_sum - x_sum * x_sum);double intercept = (y_sum - slope * x_sum) / n;// 输出结果std::cout << "Slope: " << slope << std::endl;std::cout << "Intercept: " << intercept << std::endl;return 0;
}

计算出的结果为:

y = 0.998198 x + 0.103262

矩阵法

按照最小二乘法的定义:

假设 y ^ \hat{y} y^为满足近似关系的 y y y的值,即 y ^ i = k x i + b \hat{y}_i=kx_i+b y^i=kxi+b,那么也就是求解误差平方和:

S S E = ∑ i = 1 n ( y i − y ^ i ) 2 = ( y − y ^ ) T ( y − y ^ ) SSE=\sum_{i=1}^{n}(y_i-\hat{y}_i)^{2}=(\mathbf{y}-\hat{\mathbf{y}})^T(\mathbf{y}-\hat{\mathbf{y}}) SSE=i=1n(yiy^i)2=(yy^)T(yy^)

其中,

y = [ y 1 y 2 . . . y n ] \mathbf{y}=\begin{bmatrix}y_1 \\ y_2 \\ ... \\ y_n\end{bmatrix} y= y1y2...yn

由于 y ^ i = k x i + b \hat{y}_i=kx_i+b y^i=kxi+b,那么:

y ^ = X θ \hat{\mathbf{y}}=\mathbf{X}\mathbf{\theta} y^=Xθ

其中,

X = [ x 1 1 x 2 1 . . . . . . x n 1 ] \mathbf{X}=\begin{bmatrix}x_1 & 1\\ x_2 &1 \\ ... & ... \\ x_n & 1\end{bmatrix} X= x1x2...xn11...1

θ = [ k b ] \mathbf{\theta}=\begin{bmatrix}k & b\end{bmatrix} θ=[kb]

那么,该问题就转化为求解 y ^ = X θ \hat{\mathbf{y}}=\mathbf{X}\mathbf{\theta} y^=Xθ的最小二乘解。

接下来,可以使用特征值方法、奇异值方法来进行求解。这部分的详细求解方法,可以参考文档:【Math】线性方程组的最小二乘解。

下面的代码就使用Eigen库的方法直接求解:

#include <Eigen/Dense>
#include <iostream>
#include <vector>int main() {// 创建一个二维数据集std::vector<Eigen::Vector2d> data = {{2.5, 2.4}, {0.5, 0.7}, {2.2, 2.9}, {1.9, 2.2}, {3.1, 3.0}, {2.3, 2.7}, {2, 1.6}, {1, 1.1}, {1.5, 1.6}, {1.1, 0.9}};// 创建设计矩阵A和目标向量bEigen::MatrixXd A(data.size(), 2);Eigen::VectorXd b(data.size());for (size_t i = 0; i < data.size(); ++i) {A(i, 0) = data[i](0);A(i, 1) = 1;b(i) = data[i](1);}// 使用最小二乘法求解Eigen::VectorXd x = A.bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);// 输出结果std::cout << "Slope: " << x(0) << std::endl;std::cout << "Intercept: " << x(1) << std::endl;return 0;
}

计算出的结果为:

y = 0.998198 x + 0.103262

迭代优化法

无论是微分法的:

S S E = ∑ i = 1 n ( y i − y ^ i ) 2 = ∑ i = 1 n [ y i − ( k x i + b ) ] 2 SSE=\sum_{i=1}^{n}(y_i-\hat{y}_i)^{2}=\sum_{i=1}^{n}[y_i-(kx_i+b)]^{2} SSE=i=1n(yiy^i)2=i=1n[yi(kxi+b)]2

还是,矩阵法的:

S S E = ∑ i = 1 n ( y i − y ^ i ) 2 = ( y − y ^ ) T ( y − y ^ ) SSE=\sum_{i=1}^{n}(y_i-\hat{y}_i)^{2}=(\mathbf{y}-\hat{\mathbf{y}})^T(\mathbf{y}-\hat{\mathbf{y}}) SSE=i=1n(yiy^i)2=(yy^)T(yy^)

都是求解一个最小二乘问题,上面采用的就是解析法求解的。对于这种类型的问题,同样也都可以使用迭代法进行迭代求解

对于迭代计算的优化方法,这里就不细讲了。之后会单开一章进行讲解,也可以参考文章:最小二乘问题的四种解法——牛顿法,梯度下降法,高斯牛顿法和列文伯格-马夸特法的区别和联系。


协方差法

假设有一组样本 x 1 , x 2 , x 3 , . . . , x n x_1,x_2,x_3,...,x_n x1,x2,x3,...,xn,这组样本的均值为 E ( X ) E(X) E(X),每一个样本都与 E ( X ) E(X) E(X)之间存在误差,那么这组样本的方差被定义为:所有误差的和的均值,也即 D ( X ) = E [ ( X − E ( X ) ) 2 ] D(X)=E[(X-E(X))^2] D(X)=E[(XE(X))2],方差的作用就是用来衡量样本偏离均值的程度

而协方差:

C O V ( X , Y ) = E [ ( X − E ( X ) ) ( Y − E ( Y ) ) ] = E ( X Y ) − E ( X ) E ( Y ) \begin{aligned}COV(X,Y)&=E[(X-E(X))(Y-E(Y))]\\&=E(XY)-E(X)E(Y)\end{aligned} COV(X,Y)=E[(XE(X))(YE(Y))]=E(XY)E(X)E(Y)

仔细观察上述定义式,可知:如果两个变量的变化趋势一致,也就是说如果其中一个大于自身的期望值时另外一个也大于自身的期望值,那么两个变量之间的协方差就是正值;如果两个变量的变化趋势相反,即其中一个变量大于自身的期望值时另外一个却小于自身的期望值,那么两个变量之间的协方差就是负值

也就是说,协方差是用来衡量两个变量之间协同变化趋势的总体参数,即二个变量相互影响的参数。不太清楚的可以参考文章:深入理解协方差(图文详解)。

判断线性相关的相关系数,定义为:

ρ = C O V ( X , Y ) D ( X ) D ( Y ) \rho=\frac{COV(X,Y)}{\sqrt{D(X)}\sqrt{D(Y)}} ρ=D(X) D(Y) COV(X,Y)

通过协方差的性质,如果满足 Y = k X + b Y=kX+b Y=kX+b,那么有:

C O V ( X , Y ) = C O V ( X , k X + b ) = C O V ( X , k X ) = k C O V ( X , X ) = k D ( X ) \begin{aligned}COV(X,Y)&=COV(X,kX+b)\\&=COV(X,kX)\\&=kCOV(X,X)\\&=kD(X)\end{aligned} COV(X,Y)=COV(X,kX+b)=COV(X,kX)=kCOV(X,X)=kD(X)

即:

k = C O V ( X , Y ) D ( X ) k=\frac{COV(X,Y)}{D(X)} k=D(X)COV(X,Y)

其实,这个公式和微分法计算出的 k k k是一致的:

k = n ∑ i = 1 n ( x i y i ) − ∑ i = 1 n ( x i ) ∑ i = 1 n ( y i ) n ∑ i = 1 n ( x i ) 2 − ( ∑ i = 1 n ( x i ) ) 2 k=\frac{n\sum_{i=1}^{n}(x_iy_i)-\sum_{i=1}^{n}(x_i)\sum_{i=1}^{n}(y_i)}{n\sum_{i=1}^{n}(x_i)^2-(\sum_{i=1}^{n}(x_i))^2} k=ni=1n(xi)2(i=1n(xi))2ni=1n(xiyi)i=1n(xi)i=1n(yi)


PCA法

主成分分析PCA是一种数学降维方法:利用正交变换把一系列可能线性相关的变量转换为一组线性不相关的新变量,也称为主成分,从而利用新变量在更小的维度下展示数据的特征。

主成分是原有变量的线性组合,其数目不多于原始变量。组合之后,相当于我们获得了一批新的观测数据,这些数据的含义不同于原有数据,但包含了之前数据的大部分特征,并且有着较低的维度,便于进一步的分析。

在空间上,PCA可以理解为把原始数据投射到一个新的坐标系统,第一主成分为第一坐标轴,它的含义代表了原始数据中多个变量经过某种变换得到的新变量的变化区间;第二成分为第二坐标轴,代表了原始数据中多个变量经过某种变换得到的第二个新变量的变化区间。这样我们把利用原始数据解释样品的差异转变为利用新变量解释样品的差异。

对于数据拟合的问题,我们可以看到,无论是正相关还是负相关,只要呈现为线性关系,那么它的主成分,相较于剩下的成分,一定是呈现出一个较大的差距。

在这里插入图片描述

那么,我们就可以通过这个来进行判断。

PCA的推导过程可以参考文章:一文让你彻底搞懂主成成分分析PCA的原理及代码实现(超详细推导)。

这里只介绍下最终计算方式:

假设有 n n n m m m维数据, X m × n = x 1 , x 2 , . . . , x n X_{m\times n}=x_1, x_2, ..., x_n Xm×n=x1,x2,...,xn,其中的每个 x i x_i xi是一个 m m m维的列向量,

  1. 去中心化, X = X − 1 n ∑ i = 1 n ( x i ) X=X-\frac{1}{n}\sum_{i=1}^{n}(x_i) X=Xn1i=1n(xi)
  2. 计算协方差矩阵, C O V = 1 n X X T COV=\frac{1}{n}XX^T COV=n1XXT
  3. 对协方差矩阵进行特征值分解得到 m m m个特征矩阵(按特征值从大到小以列排)

其中,这些特征值对应的特征向量就是这组数据的主成分

当然,除了对协方差矩阵进行特征值分解,SVD分解也是一种方式。

下面的代码就使用Eigen库的方法直接求解:

#include <Eigen/Dense>
#include <iostream>
#include <vector>int main() {// 创建一个二维数据集std::vector<Eigen::Vector2d> data = {{2.5, 2.4}, {0.5, 0.7}, {2.2, 2.9}, {1.9, 2.2}, {3.1, 3.0}, {2.3, 2.7}, {2, 1.6}, {1, 1.1}, {1.5, 1.6}, {1.1, 0.9}};// 计算数据的均值Eigen::Vector2d mean = Eigen::Vector2d::Zero();for (const auto& point : data) {mean += point;}mean /= static_cast<double>(data.size());// 将数据中心化Eigen::MatrixXd centeredData(data.size(), 2);for (size_t i = 0; i < data.size(); ++i) {centeredData.row(i) = data[i] - mean;}// 计算协方差矩阵Eigen::MatrixXd covariance = centeredData.transpose() * centeredData;// -----------------方法一:使用特征值// 计算协方差矩阵的特征值和特征向量Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> solver(covariance);Eigen::VectorXd eigenvalues = solver.eigenvalues();Eigen::MatrixXd eigenvectors = solver.eigenvectors();// 找到最大特征值对应的特征向量int maxEigenvalueIndex;eigenvalues.maxCoeff(&maxEigenvalueIndex);Eigen::Vector2d principalComponent = eigenvectors.col(maxEigenvalueIndex);// -----------------方法二:使用奇异值// 使用SVD找到数据的主成分Eigen::JacobiSVD<Eigen::MatrixXd> svd(centeredData, Eigen::ComputeThinU | Eigen::ComputeThinV);Eigen::Vector2d principalComponent = svd.matrixV().col(0);// 计算直线的斜率和截距double slope = principalComponent[1] / principalComponent[0];double intercept = mean[1] - slope * mean[0];std::cout << "Slope: " << slope << std::endl;std::cout << "Intercept: " << intercept << std::endl;return 0;
}

计算出的结果为:

y = 1.08454 x - 0.0530116

可以看出,这种方法计算出来的拟合值和上文的其他方法不太一样。但是也算是比较接近了。如果从SSE的角度衡量的话,自然是不如上面的方法精确的。


Ransac法

上面的所有方法,都有一个共性的缺点:不能避免异常离群点对最终拟合结果的影响。而RANSAC方法,就可以比较好的解决这个问题

RANSAC实际上是一种框架,而不是一个具体的拟合的算法。所谓具体的拟合方法,就是比如上面提到的这些算法。

RANSAC(Random Sample Consensus,随机采样一致)算法是从一组含有外点(outliers)的数据中正确估计数学模型参数的迭代算法。外点一般指的的数据中的噪声,尤其是一些离群点。所以,RANSAC也是一种外点检测算法

内点就是组成模型参数的数据,外点就是不适合模型的数据。同时RANSAC假设:在给定一组含有少部分内点的数据,存在一个程序可以估计出符合内点的模型。在本文中,这个程序,就是具体的拟合的算法。

RANSAC是通过反复选择数据集去估计出模型,一直迭代到估计出认为比较好的模型。 具体的实现步骤可以分为以下几步:

  1. 选择出可以估计出模型的较小的数据集(对于直线拟合来说就是较少点,甚至可以是两个点);
  2. 使用这个数据集来计算出数据模型(对较少点利用具体的拟合算法,来拟合出一条直线);
  3. 将所有数据带入这个模型,计算出内点的数目(累加在一定误差范围内的适合当前迭代推出模型的数据);
  4. 比较当前模型和之前推出的最好的模型的内点的数量,记录最大内点数的模型参数和内点数;
  5. 重复1-4步,直到迭代结束或者当前模型已经足够好了(内点数目大于一定数量)。

更细节的内容可以参考文章:RANSAC算法(附RANSAC直线拟合C++与Python版本)。

下面的代码就使用Eigen库的方法直接求解:

#include <Eigen/Dense>
#include <vector>
#include <random>
#include <limits>
#include <algorithm>// RANSAC直线拟合
Eigen::Vector2d ransacLineFitting(const std::vector<Eigen::Vector2d>& data,int maxIterations,double distanceThreshold,int numPoints) {std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<> dis(0, data.size() - 1);int bestInliers = 0;Eigen::Vector2d bestLine;for (int i = 0; i < maxIterations; ++i) {// 随机选择numPoints个点std::vector<Eigen::Vector2d> points;for (int j = 0; j < numPoints; ++j) {points.push_back(data[dis(gen)]);}// 计算直线参数// 利用上文的具体的拟合算法对points进行拟合,甚至可以numPoints取2,直接算斜率// 拟合结果:y = kx + b// 计算内点数int inliers = 0;for (const auto& point : data) {double distance = (point[1] - k * point[0] - b) * (point[1] - k * point[0] - b);if (distance < distanceThreshold) {++inliers;}}// 更新最佳模型if (inliers > bestInliers) {bestInliers = inliers;Eigen::Vector2d line(k, b);bestLine = line;}}return bestLine;
}int main() {// 创建一个二维数据集std::vector<Eigen::Vector2d> data = {{2.5, 2.4}, {0.5, 0.7}, {2.2, 2.9}, {1.9, 2.2}, {3.1, 3.0}, {2.3, 2.7}, {2, 1.6}, {1, 1.1}, {1.5, 1.6}, {1.1, 0.9}};// 使用RANSAC进行直线拟合Eigen::Vector2d line = ransacLineFitting(data, 1000, 0.1, 2);std::cout << "Line parameters: " << line[0] << ", " << line[1] << std::endl;return 0;
}

需要注意的是:对于RANSAC方法,numPoints的取值,不能太小,也不能太大。太小的话,可能导致拟合结果无法代表较多点数的方向;太大的话,可能无法规避掉外点造成的影响。需要权衡。


上面的几种方法都可以进行直线拟合,至于选择哪一种,取决于具体的情形和问题。

一般来说,最小二乘法是最常见、使用最广泛的方法,也是最需要掌握的。如果你是SLAM相关的从业人员,那么矩阵法、迭代优化法、Ransac法也是必须掌握的能力。


相关阅读

  • 最优化方法——最小二乘法与梯度下降法
  • 线性拟合1-最小二乘
  • 深入理解协方差(图文详解)
  • 超全面的协方差矩阵介绍
  • 一文让你彻底搞懂主成成分分析PCA的原理及代码实现(超详细推导)
  • RANSAC算法(附RANSAC直线拟合C++与Python版本)

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

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

相关文章

10.11和10.8那个大(各种ai的回答)

问题&#xff1a;10.11和10.8两个数哪个大 腾讯混元 ✔️ chatGPT ❎ 通义千问 ❎ 文心一言 ✔️ 智谱清言 ❎ 讯飞星火 ✔️ C知道 ❎ 豆包 ✔️

TCP粘包问题详解和解决方案【C语言】

1.什么是TCP粘包 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输协议&#xff0c;它保证了数据的可靠性和顺序性。然而&#xff0c;由于TCP是基于字节流而不是消息的&#xff0c;因此在传输过…

【接口自动化_08课_Pytest+Yaml+Allure框架】

上节课一些内容 的补充 1、openxl这个方法&#xff0c;第一个元素是从1开始的&#xff0c;不是从0开始 回写的列在程序里写的是11&#xff0c;是因为是固定值 一、1. Yaml入门及应用 1、什么是yaml YAML&#xff08;/ˈjməl/&#xff0c;尾音类似camel骆驼&#xff09;是一…

Finding columns with a useful data type 找到合适的数据列的类型

Finding columns with a useful data type 在确定了原始查询的数据列数之后&#xff0c;接下来就是要确定合适的数据列的数据类型。可以用 SELECT a 的方式判断对应的数据列方式&#xff0c;有时候可以通过错误信息判断数据列的类型。如果服务器的响应没有报错&#xff0c;而…

Docker启动PostgreSql并设置时间与主机同步

在 Docker 中启动 PostgreSql 时&#xff0c;需要配置容器的时间与主机同步。可以通过在 Dockerfile 或者 Docker Compose 文件中设置容器的时区&#xff0c;或者使用宿主机的时间来同步容器的时间。这样可以确保容器中的 PostgreSql 与主机的时间保持一致&#xff0c;避免在使…

启动流程和切换流程

启动流程 #mermaid-svg-iUWGw8xl1SyAmoo9 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-iUWGw8xl1SyAmoo9 .error-icon{fill:#552222;}#mermaid-svg-iUWGw8xl1SyAmoo9 .error-text{fill:#552222;stroke:#552222;}#…

C语言·分支和循环语句(超详细系列·全面总结)

前言&#xff1a;Hello大家好&#x1f618;&#xff0c;我是心跳sy&#xff0c;为了更好地形成一个学习c语言的体系&#xff0c;最近将会更新关于c语言语法基础的知识&#xff0c;今天更新一下分支循环语句的知识点&#xff0c;我们一起来看看吧~ 目录 一、什么是语句&#xf…

linux配置podman阿里云容器镜像加速器

1.下载podman yum install -y podman systemctl status podman systemctl start podman 2.获取阿里云个人容器镜像加速器地址 访问阿里云官网&#xff1a;首先&#xff0c;您需要访问阿里云&#xff08;Alibaba Cloud&#xff09;的官方网站。阿里云官网的URL是&#xff1a;…

OS:处理机进程调度

1.BackGround&#xff1a;为什么要进行进程调度&#xff1f; 在多进程环境下&#xff0c;内存中存在着多个进程&#xff0c;其数目往往多于处理机核心数目。这就要求系统可以按照某种算法&#xff0c;动态的将处理机CPU资源分配给处于就绪状态的进程。调度算法的实质其实是一种…

对于品牌方来说,小红书探店应该怎么做?

小红书是中国最大的生活分享社交平台之一&#xff0c;它现在不仅仅是一个购物推荐平台&#xff0c;也是一个探店的好去处。 用户在网络上看到一家心仪的店铺&#xff0c;却又无法亲身到访&#xff0c;对店铺的产品存疑&#xff0c;这时候就会在小红书搜索具体的相关店铺信息。 …

【代码随想录】【算法训练营】【第58天 4】 [卡码104]建造最大岛屿

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 卡码网。 day 58&#xff0c;周四&#xff0c;ding~ 题目详情 [卡码104] 建造最大岛屿 题目描述 卡码104 建造最大岛屿 LeetCode类似题目827 最大人工岛 解题思路 前提&#xff1a; 思路&#xff1a; 重点…

【LeetCode】从前序与中序遍历序列构造二叉树

目录 一、题目二、解法完整代码 一、题目 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9…

在西藏上大学是一种什么体验?如何解决语言问题?

在西藏地区上大学是一种独特而难忘的经历&#xff0c;它不仅提供了接触壮丽自然风光和深入了解藏族文化的机会&#xff0c;也带来了适应高原气候和生活方式的挑战。学生将在这里体验到丰富的教育资源和学术研究素材&#xff0c;同时在生活和人际交往中培养适应能力和独立性。这…

密码学基础-Hash、MAC、HMAC 的区别与联系

密码学基础-Hash、MAC、HMAC 的区别与联系 Hash Hash 是一种从一段数据中创建小的数字“指纹”的方法。就像一个人的指纹代表一个人的信息一样&#xff0c;Hash 对输入的数据进行整理&#xff0c;生成一个代表该输入数据的“指纹” 数据。通常该指纹数据也可称之为摘要、散列…

文件包含漏洞: 函数,实例[pikachu_file_inclusion_local]

文件包含 文件包含是一种较为常见技术&#xff0c;允许程序员在不同的脚本或程序中重用代码或调用文件 主要作用和用途&#xff1a; 代码重用&#xff1a;通过将通用函数或代码段放入单独的文件中&#xff0c;可以在多个脚本中包含这些文件&#xff0c;避免重复编写相同代码。…

RabbitMQ的学习和模拟实现|Protobuf的介绍和简单使用

protbuf的介绍和简单使用 项目仓库&#xff1a;https://github.com/ffengc/HareMQ protobuf的安装&#xff1a;README-cn.md#环境配置 基于Protobuf的一个小项目&#xff1a;基于protobuf和httplib的在线通讯录项目框架&#xff5c;Protobuf应用小项目 protobuf是什么 Pro…

TinyVue:与 Vue 交往八年的组件库

本文由体验技术团队莫春辉老师原创~ 去年因故停办的 VueConf&#xff0c;今年如约在深圳举行。作为东道主 & 上届 VueConf 讲师的我&#xff0c;没有理由不来凑个热闹。大会结束后&#xff0c;我见裕波在朋友圈转发 Jinjiang 的文章《我和 Vue.js 的十年》&#xff0c;我就…

openlayers 3d 地图 非三维 立体地图 行政区划裁剪 地图背景

这是实践效果 如果没有任何基础 就看这个专栏&#xff1a;http://t.csdnimg.cn/qB4w0 这个专栏里有从最简单的地图到复杂地图的示例 最终效果&#xff1a; 线上示例代码&#xff1a; 想要做这个效果 如果你的行政区划编辑点较多 可能会有卡顿感 如果出现卡顿感需要将边界点相应…

Python爬虫-淘宝搜索热词数据

前言 本文是该专栏的第70篇,后面会持续分享python爬虫干货知识,记得关注。 在本专栏之前,笔者有详细针对“亚马逊Amazon搜索热词”数据采集的详细介绍,对此感兴趣的同学,可以往前翻阅《Python爬虫-某跨境电商(AM)搜索热词》进行查看。 而在本文,笔者将以淘宝为例,获取…

【扩散模型(五)】IP-Adapter 源码详解3-推理代码

系列文章目录 【扩散模型&#xff08;一&#xff09;】中介绍了 Stable Diffusion 可以被理解为重建分支&#xff08;reconstruction branch&#xff09;和条件分支&#xff08;condition branch&#xff09;【扩散模型&#xff08;二&#xff09;】IP-Adapter 从条件分支的视…