找回密码
 欢迎注册
楼主: wayne

[讨论] 一道很棘手的函数拟合问题

[复制链接]
 楼主| 发表于 2010-2-6 12:58:04 | 显示全部楼层
你的图很特别 是用gnuplot画的吗
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2010-2-8 17:33:37 | 显示全部楼层
##### 9#的贴子是在哈尔滨的香格里拉大酒店里发的。 做个记号,留作纪念。 ##### 由于要参加ACM World Final的各项活动,所以睡前草草地编了一个C++程序跑起来就不管了。 我以为给她一个晚上的运行时间肯定足够了。 醒来后发现这个程序越跑越费劲,参数爬升了几级就爬不动了。 仔细一看才发现步长不合适了。 于是把步长改大几个数量级继续跑,然后我离开宾馆参加竞赛去了。 打完竞赛回到宾馆,发现参数又爬不动了。 于是再把步长改大,再跑一个晚上。 醒来后发现又不对劲了,参数爬升了几级又爬不动了。 然后才反应过来自己犯傻了,应该把步长设置为动态的! 修改完毕后再次运行,看到参数刷的一声至上云霄,瞬间登顶! 于是发贴汇报并自我检讨。 程序代码如下:
  1. #include<cmath>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. double x[180],y[180],a,p1,p2,p3,p4,p5,d1,d2,d3,d4,d5,min,tmp,b1,b2,b3,b4,b5;
  5. int i,n,t;
  6. double f(double x) //把x代入,计算f(x)的值
  7. {
  8. return p1/(p2+exp(p3*log(x)))+p4*exp(p5*log(x));
  9. }
  10. double ss() //把所有的x逐个代入,计算总偏差
  11. {
  12. int i;
  13. double s,s2;
  14. s2=0;
  15. for(i=0;i<n;i++)
  16. {
  17. s=f(x[i])-y[i]; //计算一个数据点的偏差
  18. s2+=s*s; //把偏差平方并累计
  19. }
  20. return s2;
  21. }
  22. int main()
  23. {
  24. scanf("%d",&n);
  25. for(i=0;i<n;i++)
  26. scanf("%lf%lf",&x[i],&y[i]); //读入数据
  27. p1=1.3738626629060553e+032; //设置初值
  28. p2=2.4369393036751976e+028; //设置初值
  29. p3=16.719431561664958; //设置初值
  30. p4=0.0012054475996989941; //设置初值
  31. p5=3.0453779404292880; //设置初值
  32. min=ss(); //计算偏差
  33. while(1) //死循环(在适当的时候人工终止运行,修改代码和参数,然后重新运行)
  34. {
  35. a=p1/1e12; //设置相对步长
  36. d1=(rand()-16384)*a; //设置绝对步长
  37. d2=(rand()-16384)*a/1e4; //设置绝对步长
  38. d3=rand()/1e13-1.6e-9; //设置绝对步长
  39. d4=rand()/1e16-1.6e-12; //设置绝对步长
  40. d5=rand()/1e14-1.6e-10; //设置绝对步长
  41. p1+=d1; //根据步长调整参数
  42. p2+=d2; //根据步长调整参数
  43. p3+=d3; //根据步长调整参数
  44. p4+=d4; //根据步长调整参数
  45. p5+=d5; //根据步长调整参数
  46. tmp=ss(); //计算调整参数后的偏差
  47. if(tmp<min) //如果偏差比原来小
  48. {
  49. min=tmp; //更新当前偏差,保留调整后的参数
  50. // t++; //累计更新次数
  51. // if(t>100) //每100次更新输出一次
  52. // {
  53. // t=0;
  54. printf("min=%lf\np1=%.0lf;\np2=%.0lf;\np3=%lf;\np4=%.9lf;\np5=%lf;\n\n",min,p1,p2,p3,p4,p5); //输出参数
  55. // }
  56. }
  57. else //如果偏差比原来大
  58. {
  59. p1-=d1; //还原参数
  60. p2-=d2; //还原参数
  61. p3-=d3; //还原参数
  62. p4-=d4; //还原参数
  63. p5-=d5; //还原参数
  64. }
  65. }
  66. return 0;
  67. }
复制代码
第一次做高难度的拟合作业,完全没有经验。 所以难以写出一气呵成的代码。 上述代码是跑跑停停,停停改改,改改又跑跑,才得到最终结果的。 其中$p_1$和$p_2$是分别从几百万和几千开始跑的。 然后眼看着它们一步一步地往上爬,一直没有停过。 最后才反应过来它们可能会直上云霄。 所以要把步长设置为相对的,而不是绝对的。 果然登顶的时候它们出奇的大,得用科学计数法来表示。 最后把步上逐渐减小,最终达到最佳拟合效果。 图是用curxpt软件画的。 curxpt软件自带拟合功能,但你这个数据它拟合不出来。 所以我才编了一个C++程序自己来拟合的。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2010-2-24 10:58:37 | 显示全部楼层
程序很清晰,我还以为你用到了某相关算法,有时间了我也试试用C++来做
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-2-24 21:10:53 | 显示全部楼层
原来高手们早就求解过1stOpt的优化题。 一方面,高手就是高手;另一方面,1stOpt的算法的确精悍。 1stOpt的9道优化题,Forcal目前只解出了7道(使用随机初值),包括这个题。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-2-24 22:37:54 | 显示全部楼层
14# forcal 很有意思,有时间也来尝试一下。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-2-25 19:20:11 | 显示全部楼层
14# forcal 很有意思,有时间也来尝试一下。 G-Spider 发表于 2011-2-24 22:37
恩,作为练手,或者爱好,都值得一试。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2013-1-20 00:17:32 | 显示全部楼层
6# wayne 这个是什么原理?为什么可以除以40和1000,那原来的公式还能满足吗?
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2013-1-20 10:36:21 | 显示全部楼层
17# gogdizzy 满足啊,那就变成了y/1000与x/40之间的函数关系了
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-11-23 20:54 , Processed in 0.023616 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表