拉格朗日插值法&乘数法学习笔记
拉格朗日乘数法
这玩意有什么用
拉格朗日乘数法是用于解决一类多变量的有限制最值。
具体而言,就是给定一条带有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带入求解即可。
分子可以通过前后缀和进行优化,分母预处理阶乘即可

浙公网安备 33010602011771号