……

学习笔记:三分法(唔,好像不是唉)

明明保存了,却奇怪的消失了,真令人迷惑,只好再写一遍。
题目链接: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\)

posted @ 2020-04-02 21:40  童话镇里的星河  阅读(264)  评论(0编辑  收藏  举报