数值计算——最小二乘拟合二元一次多项式
最小二乘拟合:
就是根据一系列给定的数据点,求一条曲线使得数据点到曲线的某些(水平、竖直、垂直)距离最短。
推导过程:
1. 设拟合多项式为:
2. 各点到这条曲线的距离之和,即偏差平方和如下:
3. 为了求得符合条件的a值,对等式右边求ai偏导数,因而我们得到了:
.......
4. 将等式左边进行一下化简,然后应该可以得到下面的等式:
.......
5. 把这些等式表示成矩阵的形式,就可以得到下面的矩阵:
6. 将这个范德蒙得矩阵化简后可得到:
实现代码:
(里面用到的一些方法是前几篇博客写的)
下面这个代码是拟合y=ax+b的,拟合条件是数据点与拟合曲线的竖直距离最短,如果想要拟合水平距离最短的话只需要将原式写成x=cy+d同样的方法即可。也可以使用SVD方法,参见《科学计算导论》第3章。
package com.kexin.lab5;
import com.kexin.lab4.NomalEquation;
/**
* 数据点与拟合直线竖直距离最短
* @author KeXin
*
*/
public class LeastSquarel1 {
/**
* 生成矩阵A
* @return
*/
public static double[][] MakeA(double[] x){
double[][] a = new double[12][2];
for(int i = 0;i<12;i++){
for(int j = 0;j<2;j++){
a[i][j] = Math.round(Math.pow(x[i], j));
}
}
return a;
}
public static void main(String[] args) {
double[] x1= {0.5,1.1,1.7,2.1,2.5,2.9,3.3,3.7,4.2,4.9,5.3,6.0};//数据点x
double[] b1 = {1.6,2.4,3.8,4.3,4.7,4.8,5.5,6.1,6.3,7.1,7.1,8.2};//数据点y
double[][] A = MakeA(x1);//系数矩阵
//正规方程组方法解方程Ax = b
double[][] a2 = NomalEquation.Transposition(A); // A的转置矩阵
// 输入方程矩阵b
double[][] a = NomalEquation.MultiEquation(a2, A); // 系数矩阵的转置和系数矩阵的乘积n*n
// 求解变化后的结果矩阵a2*b1
int n = a2.length;
int m = b1.length;
double[] b = new double[n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
b[i] += a2[i][j] * b1[j];
}
}
// 楚列斯基分解系数矩阵的转置和系数矩阵的乘积a
a = NomalEquation.Cholesky(a);
m = b.length;
n = a.length;
double x[] = new double[m];
// 前代计算
for (int j = 0; j < m; j++) {
if (a[j][j] != 0) {
x[j] = b[j] / a[j][j];
b[j] = x[j];
}
for (int i = j + 1; i < m; i++) {
b[i] = b[i] - a[i][j] * x[j];
}
}
// 将a进行转置
a = NomalEquation.Transposition(a);
// 回代计算
for (int j = m - 1; j >= 0; j--) {
if (a[j][j] != 0) {
x[j] = b[j] / a[j][j];
}
for (int i = 0; i <= j - 1; i++) {
b[i] = b[i] - a[i][j] * x[j];
}
}
// 输出结果
// NomalEquation.PrintArray("x", x);
System.out.println("a :"+x[1]);
System.out.println("b :"+x[0]);
double r=0;
for(int i = 0;i<x1.length;i++){
r+=Math.pow((x[0]+x[1]*x1[i]-b1[i]),2);
}
System.out.println("||r||:"+r);
}
}
版权声明:本文为qq_29721419原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。