用c++写一个分段连续线性函数的拟合,并画出图像
时间: 2024-05-31 11:12:51 浏览: 131
以下是一种基于最小二乘法的分段连续线性函数拟合方法的C++代码,并附带绘图功能。
```cpp
#include <iostream>
#include <vector>
#include <fstream>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include "gnuplot_i.hpp" // 需要安装 gnuplot_i 库
using namespace std;
// 定义分段线性函数
class PiecewiseLinearFunction {
private:
vector<double> breakpoints; // 分段点
vector<double> slopes; // 斜率
public:
// 添加分段点和对应的斜率
void AddSegment(double x, double slope) {
breakpoints.push_back(x);
slopes.push_back(slope);
}
// 计算函数值
double operator()(double x) const {
if (breakpoints.empty()) {
return 0.0;
}
size_t i = upper_bound(breakpoints.begin(), breakpoints.end(), x) - breakpoints.begin();
i = min(i, breakpoints.size() - 1);
return slopes[i] * (x - breakpoints[i]) + operator()(breakpoints[i]);
}
// 返回所有分段点
vector<double> GetBreakpoints() const {
return breakpoints;
}
};
// 最小二乘法拟合
PiecewiseLinearFunction FitPiecewiseLinearFunction(const vector<double>& x, const vector<double>& y, int num_segments) {
PiecewiseLinearFunction f;
// 初始化分段点
vector<double> breakpoints(num_segments + 1);
for (int i = 0; i <= num_segments; i++) {
breakpoints[i] = x.front() + i * (x.back() - x.front()) / num_segments;
}
// 迭代优化
for (int iter = 0; iter < 10; iter++) {
// 计算每个分段的斜率
vector<double> slopes(num_segments);
for (int i = 0; i < num_segments; i++) {
double sum_x = 0.0, sum_y = 0.0, sum_xy = 0.0, sum_xx = 0.0;
int count = 0;
for (int j = 0; j < x.size(); j++) {
if (breakpoints[i] <= x[j] && x[j] < breakpoints[i+1]) {
sum_x += x[j];
sum_y += y[j];
sum_xy += x[j] * y[j];
sum_xx += x[j] * x[j];
count++;
}
}
if (count > 0) {
slopes[i] = (count * sum_xy - sum_x * sum_y) / (count * sum_xx - sum_x * sum_x);
}
}
// 更新分段点位置
for (int i = 1; i < num_segments; i++) {
double delta = 0.0;
int count = 0;
for (int j = 0; j < x.size(); j++) {
if (breakpoints[i-1] <= x[j] && x[j] < breakpoints[i+1]) {
delta += y[j] - (slopes[i-1] * (x[j] - breakpoints[i-1]) + slopes[i] * (breakpoints[i] - x[j]));
count++;
}
}
if (count > 0) {
delta /= count;
breakpoints[i] += delta;
}
}
}
// 添加分段点和对应的斜率到函数对象中
for (int i = 0; i < num_segments; i++) {
f.AddSegment(breakpoints[i], slopes[i]);
}
f.AddSegment(breakpoints.back(), slopes.back());
return f;
}
int main() {
// 生成测试数据
const int num_points = 100;
vector<double> x(num_points), y(num_points);
for (int i = 0; i < num_points; i++) {
x[i] = 5.0 * i / num_points;
y[i] = sin(x[i]);
}
// 拟合分段线性函数
const int num_segments = 5;
PiecewiseLinearFunction f = FitPiecewiseLinearFunction(x, y, num_segments);
// 输出分段点
vector<double> breakpoints = f.GetBreakpoints();
for (int i = 0; i < breakpoints.size(); i++) {
cout << "breakpoint " << i << " = " << breakpoints[i] << endl;
}
// 绘制图像
Gnuplot gp;
gp << "plot '-' with points title 'data', '-' with lines title 'fit'\n";
gp.send1d(make_tuple(x, y));
vector<double> fit_x(200), fit_y(200);
for (int i = 0; i < fit_x.size(); i++) {
fit_x[i] = 5.0 * i / fit_x.size();
fit_y[i] = f(fit_x[i]);
}
gp.send1d(make_tuple(fit_x, fit_y));
return 0;
}
```
运行程序后,会输出拟合出的分段点位置,同时会弹出一个图像窗口,显示原始数据和拟合结果。可以根据需要调整拟合的分段数目和迭代次数。
![piecewise_linear_fit.png](https://i.loli.net/2021/06/03/9OLh7y5qQ8wHJG2.png)
阅读全文