使用GNU Scientific C进行多项式拟合

时间:2015-01-07 18:16:19

标签: c gnu gsl

我需要从n + 1个数据点获得n度函数。通过使用以下gnuplot脚本,我得到了正确的拟合:

f(x) = a + b*x + c*x**2 + d*x**3 + e*x**4 + f*x**5 + g*x**6 + h*x**7 + i*x**8 + j*x**9 + k*x**10 + l*x**11 + m*x**12

# Initial values for parameters
a = 0.1
b = 0.1
c = 0.1
d = 0.1
e = 0.1
f = 0.1
g = 0.1
h = 0.1
i = 0.1
j = 0.1
k = 0.1
l = 0.1
m = 0.1


# Fit f to the following data by modifying the variables a, b, c
fit f(x) '-' via a, b, c, d, e, f, g, h, i, j, k, l, m
   4.877263 45.036000
   4.794907 44.421000
   4.703827 43.808000
   4.618065 43.251000
   4.530520 42.634000
   4.443111 42.070000
   4.357077 41.485000
   4.274298 40.913000
   4.188404 40.335000
   4.109381 39.795000
   4.027594 39.201000
   3.946413 38.650000
   3.874360 38.085000
e

拟合后,我得到以下系数:

a               = -781956        
b               = -2.52463e+06   
c               = 2.75682e+06    
d               = -553791        
e               = 693880         
f               = -1.51285e+06   
g               = 1.21157e+06    
h               = -522243        
i               = 138121         
j               = -23268.8       
k               = 2450.79        
l               = -147.834       
m               = 3.91268 

然后,通过将数据和f(x)绘制在一起,似乎给定的系数是正确的: Here one can see the function fitted over the data points.

但是,我需要通过使用c代码来获得这样的拟合。对于几种情况,GNU Scientific Library的多项式拟合代码(as in this link)得到了正确的结果。但是对于上面的数据(以及我的数据集中的其他几个案例),我得到的结果是有缺陷的。

例如,以下代码(使用与上述相同的数据):

void testOfPolynomialFit(){
   double x[13] = {4.877263, 4.794907, 4.703827, 4.618065, 4.530520, 4.443111, 4.357077, 4.274298, 4.188404, 4.109381, 4.027594, 3.946413, 3.874360};
   double y[13] = {45.036000, 44.421000, 43.808000, 43.251000, 42.634000, 42.070000, 41.485000, 40.913000, 40.335000, 39.795000, 39.201000, 38.650000, 38.085000};
   double coefficients[13];

   polynomialfit(13, 13, x, y, coefficients);

   int i, n = 13;

   for (i = 0; i < n; i++)
   {
    printf("%lf\t", coefficients[i]); 
   }
   printf("\n"); 

}

结果:

-6817581083.803348      12796304366.105989      -9942834843.404181      3892080279.353104             
 -630964566.517794        -75914607.005088         49505072.518952        -5062100.000931 
   -1426228.491628           514259.312320           -70903.844354            4852.824607
       -136.738756

它对应于以下形式的函数:

c(x)=-6837615134.799868+12834646330.586414*x**1-9973474377.668280*x**2+3904659818.834625*x**3-633282611.288889*x**4-76066283.747942*x**5+49670960.939126*x**6-5091123.449217*x**7-1426628.818192*x**8+515175.778491*x**9-71055.177018*x**10+4863.969973*x**11-137.065848*x**12

可以在这里查看c(x)的样子:

c(x) looks

在这样的图像中,a(x)和b(x)是使用&#34;多项式拟合&#34;拟合的函数。只有几个点(4和7)。

那么,关于我在这里做错了什么的提示?还有一些其他的c代码可以提供正确的拟合吗?

3 个答案:

答案 0 :(得分:2)

您正遭受数字不稳定的困扰。简单的线性回归证实了可以在视觉上观察到的内容:只使用线性模型就可以解释99.98%的变化。

您提供的链接中的代码会执行许多非常不安全的事情:不检查obsdegree是否为正,而不是检查内存分配是否成功,没有返回任何有用的东西,......

我认为gsl_multifit_linear溢出,或者不能包含数字不稳定性,而不检查返回意味着我们不知道。

修改

根据GSL Website多项式回归,由于计算大数的大功率,可能导致额外的数值不稳定性。尝试使用x预处理x = (x - avg_x) / sd_x值。这应该允许你在发生这种情况之前获得更多度数的多项式。

从长远来看,您可能会再次遇到此问题。如果您使用35或100或更多数据点进行此分析,则您不太可能找到任何克服不稳定性的技术。

答案 1 :(得分:2)

你的两种解决方法之间的一个主要区别是当你使用gnuplot时,你正在做一个fit来设置系数,并在同一个程序中从那里绘制函数,而使用GSL你是将数字从一个程序复制到另一个程序。

如果您使用printf("%lf", ...)的输出作为第二个gnuplot程序的输入,那么您已经失去了很多准确性,因为printf比任何内部操作都更加精确地舍入数字任何一个计划。而且因为这是一个数字上不稳定的问题,所以一点点的舍入会伤害很多。

x为4.877263时,x**12约为181181603.850932,因此如果m0.000001关闭(printf的默认舍入级别),则会引入错误181.181603850932,这是与y的实际x值相比,相对误差约为300%。

尝试%.60lf,看看它是否会变好。

如果其中一个程序在内部使用long double而另一个程序没有,那么无论你做什么,你都可能不会得到很好的匹配。

答案 2 :(得分:0)

我在问题陈述和示例代码之间有点困惑。多项式拟合函数通常期望> = n + 2个数据点以拟合度数为n的多项式。在n + 1个数据点的情况下,通过生成具有n + 1行和具有给定n + 1集的列的矩阵,生成精确解(如果浮点舍入不是问题),而不是进行拟合用于表示线性方程组的值:

| x[0]^n + x[0]^(n-1) + ... + x[0] + 1 |  | c[  n] |    | y[0] |
| x[1]^n + x[1]^(n-1) + ... + x[1] + 1 |  | c[n-1] |    | y[1] |
|  ...                                               =  | ...  |
| x[n]^n + x[n]^(n-1) + ... + x[n] + 1 |  | c[  0] |    | y[n] |

所以只有c []是变量,方程是线性的。反转x值的矩阵,然后将y值乘以反转矩阵以产生结果。如果实际多项式的次数低于n,则可能存在问题(两个或更多个等式不是线性无关的)。如果发生这种情况,您可以使用一个或多个较少的行或切换到使用传统的多项式拟合算法。

如果存在&gt; = n + 2个数据点,则一个选项是最小二乘型多项式拟合。这是指向.rtf文档的链接,该文档使用正交和递归定义的多项式来拟合一组数据点。正交多项式消除了反转矩阵的需要,因此它可以更准确地处理更大程度的多项式。

opls.rtf

示例运行,其中degree = 1且degree = 3.第一列是原始x值,第二列是原始y值,第三列是y值,第四列是(原始y值 - 计算y值):

variance =  9.6720e-004
 6.8488e+000 X**1 +  1.1619e+001

 4.877263e+000   4.503600e+001   4.502243e+001   1.356604e-002
 4.794907e+000   4.442100e+001   4.445839e+001  -3.739271e-002
 4.703827e+000   4.380800e+001   4.383460e+001  -2.660237e-002
 4.618065e+000   4.325100e+001   4.324723e+001   3.765950e-003
 4.530520e+000   4.263400e+001   4.264765e+001  -1.365429e-002
 4.443111e+000   4.207000e+001   4.204901e+001   2.099404e-002
 4.357077e+000   4.148500e+001   4.145977e+001   2.522524e-002
 4.274298e+000   4.091300e+001   4.089284e+001   2.016354e-002
 4.188404e+000   4.033500e+001   4.030456e+001   3.043591e-002
 4.109381e+000   3.979500e+001   3.976335e+001   3.165004e-002
 4.027594e+000   3.920100e+001   3.920321e+001  -2.205684e-003
 3.946413e+000   3.865000e+001   3.864721e+001   2.788204e-003
 3.874360e+000   3.808500e+001   3.815373e+001  -6.873392e-002

variance =  2.4281e-004
 8.0952e-001 X**3 + -1.0822e+001 X**2 +  5.4910e+001 X**1 + -5.9287e+001

 4.877263e+000   4.503600e+001   4.502276e+001   1.324045e-002
 4.794907e+000   4.442100e+001   4.444280e+001  -2.180419e-002
 4.703827e+000   4.380800e+001   4.381431e+001  -6.306292e-003
 4.618065e+000   4.325100e+001   4.323170e+001   1.929905e-002
 4.530520e+000   4.263400e+001   4.264294e+001  -8.935141e-003
 4.443111e+000   4.207000e+001   4.205786e+001   1.214442e-002
 4.357077e+000   4.148500e+001   4.148153e+001   3.468503e-003
 4.274298e+000   4.091300e+001   4.092369e+001  -1.069376e-002
 4.188404e+000   4.033500e+001   4.033844e+001  -3.436876e-003
 4.109381e+000   3.979500e+001   3.979160e+001   3.397859e-003
 4.027594e+000   3.920100e+001   3.921454e+001  -1.354191e-002
 3.946413e+000   3.865000e+001   3.862800e+001   2.199866e-002
 3.874360e+000   3.808500e+001   3.809383e+001  -8.830768e-003