【问题标题】:Polynomial Fitting using GNU Scientific C使用 GNU Scientific C 进行多项式拟合
【发布时间】:2015-01-07 18:16:18
【问题描述】:

我需要从 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 

然后,通过将 data 和 f(x) 绘制在一起,似乎给定的系数是正确的:

但是,我需要使用 c 代码来获得这样的拟合。在某些情况下,多项式拟合的 GNU 科学库代码 (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) 的样子:

在这样的图像中,a(x) 和 b(x) 是使用“多项式拟合”对少数点(4 和 7)进行拟合的函数。

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

【问题讨论】:

  • 实际上,@abligh,您可以完美地将n-1 度多项式拟合到任何n 点。
  • @chux 查看链接中的源代码,您需要传递系数的数量,而不是多项式的次数,尽管他们决定调用变量。
  • 嗯,想知道如果度数为 2、3、4 时结果如何……。问题是在 13 时出现还是显示出度数较小的迹象?
  • 有一个 12 次多项式完全适合,但无论这些数字来自什么真实世界的过程,它可能都不是一个合理的模型。这些点看起来非常接近线性关系。你的大多项式摆动以准确地击中所有点,但是一旦你离开[3.874360:4.877263] 范围,它就会疯狂地偏离它们大约在线上的线。在你的fit 命令之后尝试plot [3.5:5.5] f(x), '-' 看看我的意思
  • @chux,我尝试了 2、4、6、8、10 和 12 系数,而 12 是我遇到的第一个问题。 See here

标签: c gnu gsl


【解决方案1】:

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

如果您使用printf("%lf", ...) 的输出作为您的第二个gnuplot 程序的输入,那么您会损失很多准确性,因为printf 对数字的取整比任何一个程序的任何内部操作都多。而且因为这是一个数值不稳定的问题,所以稍微四舍五入会很痛苦。

x 是 4.877263 时,x**12 大约是 181181603.850932 所以如果你的 m0.000001 关闭(printf 的默认舍入级别),这会引入 181.181603850932 的误差,这是一个大约 30% 的相对误差该x 的实际y 值。

试试%.60lf,看看会不会好起来。

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

【讨论】:

  • 非常感谢。使用 %.60lf 确实有效。拟合是正确的,只是在绘制时我得到了一个糟糕的结果。
  • 而不是f in %.60lf 建议e。详情请见Printf width specifier to maintain precision
  • 我真的更喜欢%a,但是当我尝试这样做时,gnuplot 不喜欢它
【解决方案2】:

您正遭受数值不稳定的困扰。简单的线性回归证实了可以直观地观察到的情况:仅使用线性模型就可以解释 99.98% 的变化。

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

我会假设gsl_multifit_linear 溢出,或者不能包含数值不稳定性,并且不检查返回意味着我们不知道。

编辑:

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

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

【讨论】:

  • 我希望 inside gsl 库的代码比 调用 某些人编写的 gsl 库的代码质量稍高一些Rosettacode 爱好者...
  • 谢谢@WumpusQ.Wumbley 我没有仔细看链接。我假设该函数是 GSL 库的一部分。已编辑以修复此疏忽
  • @WumpusQ.Wumbley 我希望 gsl 库中的代码也是如此。不能对reosettacode一说同样的话,但我也不知道不稳定性在哪里。
  • @Degustaf,我认为这可能是由数值不稳定引起的。我尝试了另一种在互联网上发现的不安全代码,但对于较大的多项式也得到了奇怪的结果。但是我可以通过线性模型来解释变化,我需要 n 次多项式来遵循模型并进一步解释,因为当数据呈现接近线性模型时,模型不太容易出错。
  • @Isma 不稳定来自你的矩阵[x:x^2:x^3:...]。因为数据非常适合线性模型,所以当您尝试拟合三次多项式时,条件数会很大,而且只会变得更糟。
【解决方案3】:

我对问题陈述和示例代码有点困惑。 polynomialfit 函数通常期望 >= 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,则可能会出现问题(两个或多个方程将不是线性独立的)。如果发生这种情况,您可以少用一行或多行,或者改用传统的多项式拟合算法。

如果有 >= 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-17
    • 2013-02-17
    • 1970-01-01
    • 1970-01-01
    • 2019-01-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多