学习笔记:三分法(唔,好像不是唉)
明明保存了,却奇怪的消失了,真令人迷惑,只好再写一遍。
题目链接:P3382 【模板】三分法
我们知道极值处导函数值为\(0\),那么我们把这个函数的导数求出来,二分答案即可。
刚刚学习了导数,来看一下与这题有关的导数法则。
\[[f(x)\pm g(x)]'=f'(x)\pm g'(x)
\]
那我们推导到更多加减法的求导,对于一个\(n\)次多项式\(f(x)\):
\[f(x)=\sum\limits_{i=0}^na_ix^i
\]
显然,它的导数是:
\[f'(x)=\sum\limits_{i=0}^{n-1}(i+1)a_{i+1}x^i
\]
这个直接模拟就好了。
关于多项式的求值
对于高次项,容易想到对自变量\(x\)快速幂,不过这样是\(\mathcal O(n\log n)\)的,有没有更好的方法呢?
有!
早在很早以前,中国数学家秦九韶就提出了秦九韶算法,我们来举个栗子吧。
有一个多项式\(g(x)\):
\[g(x)=ax^3+bx^2+cx+d
\]
我们这样化简一下:
\[g(x)=x(x(xa+b)+c)+d
\]
这样就能\(\mathcal O(n)\)了。
double f(double g,int n)//求n次多项式f(g)的值
{
double x=fuc[n];
for(int i=1;i<=n;i++) x=g*x+fuc[n-i];
return x;
}
自己的想法
在讲述秦九韶算法时,莫名发现一种奇妙算法:
快速幂为什么慢呢?
显然是重复处理了一些幂次,那我们直接暴算幂次即可,这样也是\(\mathcal O(n)\)的:
double f(double g,double n)
{
double x=1,ans=0;
for(int i=0;i<=n;i++) ans=ans+x*fuc[i],x*=g;
return ans;
}
然后这道题就做完了:
\(Code\):
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define read(x) scanf("%d",&x)
#define read_d(x) scanf("%lf",&x)
double fuc[20];
double a[20];
int n;
double l,r;
double f(double g,int n)
{
double x=fuc[n];
for(int i=1;i<=n;i++) x=g*x+fuc[n-i];
return x;
}
int main()
{
read(n),read_d(l),read_d(r);
for(int i=n;i>=0;i--) read_d(a[i]);
//求导
for(int i=n;i>=1;i--) fuc[i-1]=a[i]*i;
double mid;
for(int i=1;i<=100;i++)
{
mid=(l+r)/2;
if(f(mid,n-1)<0) r=mid;
else l=mid;
}
printf("%.5lf\n",mid);
return 0;
}
那么你如果对于多项式函数的求导一般式一脸懵逼,那么直接按定义做就好了:
我们知道导数的定义为:
\[f'(x)=\lim\limits_{\mathit\Delta x\rightarrow 0}\dfrac{f(x+\mathit\Delta x)-f(x)}{\mathit\Delta x}
\]
我们可以把\(\mathit\Delta x\)设成很小的数如\(10^{-10}\),然后就行了。
大佬是这样做的,我来搬运代码:
\(Code\):
#include <bits/stdc++.h>
using namespace std;
const int N = 15;
const double eps = 1e-10;;
int n;
double L, R, a[N];
double f(double x) {
double c[N];
c[n] = a[n];
for (int i = n - 1; i >= 0; --i) c[i] = c[i + 1] * x + a[i];
return c[0];
}
double check(double x) {
double dx = eps;
double dy = f(x + eps) - f(x);
return dy / dx;
}
int main() {
cin >> n >> L >> R;
for (int i = 0; i <= n; ++i) cin >> a[n - i];
double mid;
while (R - L > eps) {
mid = (L + R) / 2;
if (check(mid) > 0) L = mid;
else R = mid;
}
printf("%.5lf", L);
return 0;
}
关于复杂度:
二分\(100\)次的话就是\(\mathcal O(100n)\)了,或者写成\(
\mathcal O(n\log eps)\)。
\(ps\):我自己\(yy\)的竟然比秦九韶算法快\(8\;ms\)!