介绍
项目中遇到给出几个间隔时间点的数据,然后判断其他时刻的数据,需要整体考虑数据的变化趋势,不能通过插值来得到中间未知时刻的数据,所以需要使用多项式拟合来将数据补全。
多项式函数是一个很重要的建模手段,利用任意个点,就可以拟合出一个多项式函数,通过多项式函数来推导出其他点的函数值,然后绘制出函数曲线,这个是最基本的原理!
拟合方法
- 通过点来拟合,得到拟合多项式的函数关系;
- 将得到的集合关系转化成多项式函数的表达式,形如xxx + axx + b*x +c 的样子;
- 将多项式函数表达式,解析成一个计算方法;
- 通过计算方法,来得到任意x(横坐标)对应的y(纵坐标)的值。
下面是用Java语言来实现多项式拟合的代码:
Java实现
拟合功能在下面代码中,已完整实现,可直接使用。
坐标实体类
定义坐标点的类,x(横坐标),y(纵坐标)
package cn.com.em.pu.fitting;
import java.io.Serializable;
import java.util.Objects;
/** * 坐标点 * * @author pupengfei * @version 1.0 * @date 2020/8/27 14:45 */
public class Point implements Serializable {
private static final long serialVersionUID = 3256087124347421878L;
private double x;
private double y;
public Point() {
}
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
return Double.compare(point.x, x) == 0 &&
Double.compare(point.y, y) == 0;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
@Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
}
自定义异常
package cn.com.em.pu.fitting;
/** * 多项式拟合异常 * * @author pupengfei * @version 1.0 * @date 2020/8/27 15:29 */
public class PolynomialFittingException extends Exception {
public PolynomialFittingException() {
}
public PolynomialFittingException(String message) {
super(message);
}
public PolynomialFittingException(String message, Throwable cause) {
super(message, cause);
}
}
多项式计算公式接口
多项式计算公式,只有一个方法,根据x获取y的值
package cn.com.em.pu.fitting;
/** * 多项式计算公式,根据x获取y的值 * * @author pupengfei * @version 1.0 * @date 2020/8/27 13:57 */
@FunctionalInterface
public interface PolynomialFnc {
/** * 根据横坐标x,计算纵坐标y的值 * @param x 横坐标 * @return y,纵坐标 */
double getY(double x);
}
多项式拟合工具类
数据多项式拟合工具类
package cn.com.em.pu.fitting;
import java.util.ArrayList;
import java.util.List;
/** * 数据多项式拟合工具类 * * @author pupengfei * @version 1.0 * @date 2020/8/27 13:56 */
public class PolynomialUtil {
/** * 多项式拟合 * @param data 坐标点集合 * @return 多项式计算公式 */
public static PolynomialFnc fitting(List<Point> data) throws PolynomialFittingException {
// 多项式每一项的计算表达式字符串
List<String> returnResult = new ArrayList<>();
int n = data.size();
List<List<Double>> inputMatrix = new ArrayList<>();
for (int i = 0; i < n; i++) {
List<Double> tempArr = new ArrayList<>();
for (int j = 0; j < n; j++) {
tempArr.add(Math.pow(data.get(i).getX(), n - j - 1));
}
tempArr.add(data.get(i).getY());
inputMatrix.add(tempArr);
}
for (int i = 0; i < n; i++) {
double base = inputMatrix.get(i).get(i);
for (int j = 0; j < n + 1; j++) {
if (base == 0) {
//存在相同x不同y的点,无法使用多项式进行拟合
throw new PolynomialFittingException("存在相同x不同y的点,无法使用多项式进行拟合");
}
inputMatrix.get(i).set(j, inputMatrix.get(i).get(j) / base);
}
for (int j = 0; j < n; j++) {
if (i != j) {
double baseInner = inputMatrix.get(j).get(i);
for (int k = 0; k < n + 1; k++) {
inputMatrix.get(j).set(k, inputMatrix.get(j).get(k) - baseInner * inputMatrix.get(i).get(k));
}
}
}
}
for (int i = 0; i < n; i++) {
if (inputMatrix.get(i).get(n) > 0) {
returnResult.add("+");
}
if (inputMatrix.get(i).get(n) != 0) {
String tmp_x = "";
for (int j = 0; j < n - 1 - i; j++) {
tmp_x = tmp_x + "*x";
}
returnResult.add((inputMatrix.get(i).get(n) + tmp_x));
}
}
// 将多项式表达式,转换为计算公式
return x -> {
double y = 0;
for (String s : returnResult) {
if ("+".equals(s)) {
y += 0;
} else if (s.contains("*")) {
String[] split = s.split("\\*");
double temp = 1;
for (String s1 : split) {
if ("x".equals(s1)) {
temp *= x;
} else {
temp *= Double.parseDouble(s1);
}
}
y += temp;
} else {
y += Double.parseDouble(s);
}
}
return y;
};
}
}
演示代码
package cn.com.em.pu.fitting;
import java.util.ArrayList;
import java.util.List;
/** * 测试 * * @author pupengfei * @version 1.0 * @date 2020/8/27 20:18 */
public class TestExample {
public static void main(String[] args) throws PolynomialFittingException {
List<Point> data = new ArrayList<>();
data.add(new Point(1, 1));
data.add(new Point(2, 2));
data.add(new Point(3, 2));
data.add(new Point(4, 1));
PolynomialFnc fitting = PolynomialUtil.fitting(data);
System.out.println(fitting.getY(3));
data = new ArrayList<>();
data.add(new Point(1, 1));
data.add(new Point(2, 3));
fitting = PolynomialUtil.fitting(data);
System.out.println(fitting.getY(3));
}
}