拉格朗日插值法&乘数法学习笔记

拉格朗日乘数法

这玩意有什么用

拉格朗日乘数法是用于解决一类多变量的有限制最值。

具体而言,就是给定一条带有n个变量的等式,如$f(x_1,...,x_n)=0$作为限制

求一个包含n个变量的式子的最值,如$g(x_1,...,x_n)$

这玩意怎么搞

我们把f和g的图像在坐标系中画出来

通过一堆复杂的证明(感性理解也行)可知当这两个图像相切(这里拿n=2举例)时,切点坐标对应的$(x_1,..,x_n)$就是最值取得的地方

显然,相切处两图像的切线斜率一致(严谨的话应该叫坡度向量),即该处的导数一致。这样我们就可以求出两函数的导函数,然后列方程求解即可

注意f,g是多元函数,所以列方程时应该把每一元的偏导数(即把这一元当作主元,其他元当作常量)单独拿出来列方程,这样我们就有n+1条方程(包括最开始的约束方程),求解即可。

求解方程的方法试题目而定,如noi川藏旅行中结合单调性求解,或牛顿迭代法解决。

例题

noi骑行川藏

#include<iostream>
#include<cstdio>
using namespace std;
#define N 10010
double v2[N],e,v[N],s[N],k[N],v3[N];
int n;
bool check(double mid)
{
	double t=0;
	for(int i=1;i<=n;i++)
	{
		double l=v3[i],r=100000;
		while(r-l>=1e-15*5)
		{
			double x=(l+r)/2.0 ;
			if(mid*2*k[i]*x*x*(x-v[i])+1>=0) l=x;
			else r=x;
		}
		v2[i]=l;
		if(v2[i]<=v[i]) return false;
		t+=k[i]*s[i]*(v2[i]-v[i])*(v2[i]-v[i]);
	}
	if(t>e)  return false;
	for(int i=1;i<=n;i++) v3[i]=v2[i];
	return true;
}
int main()
{	
	cin>>n>>e;
	for(int i=1;i<=n;i++) scanf("%lf%lf%lf",&s[i],&k[i],&v[i]);
	double l=-100000,r=0;
	while(r-l>1e-15*5)
	{
		double mid=(l+r)/2.0;
		if(check(mid)) l=mid;
		else r=mid;
	}
	double ans=0;
	for(int i=1;i<=n;i++) ans+=s[i]/v3[i];
	printf("%.8f",ans);
}
	

  

 拉格朗日插值法

用途

求过n个点的函数解析式(即解一元非线性方程组)

在OI题中一般用于求一些系数已知的递推式或求和式等的任意一项(可以很大)

比如什么自然数幂求和。

但使用前需知道最高次数(可以不精确,稍微大一点)

另外,有些范围很大的二维dp,如果转移方程固定,形如$f(i)(j)=c*f(k)(j-1)*(a*j+b)$且j较小,要求f(n)(m)。可以把j看作主元x,那么转移时j每加1,x的最高次就加1,所以f(n)(m)就是一个关于x的m次多项式,先求出前面的f(1)(m)到f(m)(m),然后插值解决f(n)(m)。(比如【BZOJ2655】calc)

如果给定点是连续的自然数,时间复杂度为$O(n)$(n为最高次)

否则为$O(n^2)$

做法

利用已知的系数将数值1,2,3,...n(多项式的项数)带入计算得到$y_1,y_2,...,y_n$

通过一些数学推导可得其解析式为$f(x)=\sum_{i=1}^n y_i*\frac{\prod_{j=1,j!=i}^n x-j}{\prod_{j=1,j!=i}^n i-j}$

那么,在求解数值x对应的函数值时,直接将x带入求解即可。

分子可以通过前后缀和进行优化,分母预处理阶乘即可

posted @ 2021-07-15 21:42  linzhuohang  阅读(336)  评论(0编辑  收藏  举报