拉格朗日插值

拉 格 朗 日 插 值 法 可 构 造 一 个 经 过 任 意 n 个 点 ( a 1 , b 1 ) , ( a 2 , b 2 ) , ( a 3 , b 3 ) , … , ( a n , b n ) 的 函 数 f , 设 f = f 1 + f 2 + ⋯ + f n , 需 要 让 f 1 , f 2 , … , f n 均 经 过 这 n 个 点 , 且 当 x = a i 时 , f i = b i ; 当 x = a j ( j ≠ i ) 时 , f i = 0. 拉格朗日插值法可构造一个经过任意n个点(a_1,b_1),(a_2,b_2),(a_3,b_3),\dots,(a_n,b_n)的函数f,设f=f_1+f_2+\dots+f_n,需要让f_1,f_2,\dots,f_n均经过这n个点,且当x=a_i时,f_i=b_i;当x=a_j(j \ne i)时,f_i=0. n(a1,b1),(a2,b2),(a3,b3),,(an,bn)f,f=f1+f2++fn,f1,f2,,fnn,x=ai,fi=bi;x=aj(j=i),fi=0.

那 么 显 然 , 由 f 1 , f 2 , … , f n 相 加 得 到 的 的 f , 恰 好 经 过 了 a 1 , a 2 , … , a n . 那么显然,由f_1,f_2,\dots,f_n相加得到的的f,恰好经过了a_1,a_2,\dots,a_n. ,f1,f2,,fnf,a1,a2,,an.
f 1 , f 2 , … , f n 被 称 为 拉 格 朗 日 基 函 数 , 表 达 式 为 : f i = b i ∏ j ≠ i x − x j x i − x j f_1,f_2,\dots,f_n被称为拉格朗日基函数,表达式为:f_i=b_i\prod_{j\ne i} \frac {x-x_j} {x_i-x_j} f1,f2,,fn,:fi=bij=ixixjxxj

那 么 , 由 拉 格 朗 日 插 值 法 得 到 的 函 数 f 的 表 达 式 为 : ∑ i = 1 n b i ∏ j ≠ i x − x j x i − x j , 时 间 复 杂 度 为 O ( n 2 ) . 那么,由拉格朗日插值法得到的函数f的表达式为:\sum_{i=1}^{n}b_i\prod_{j\ne i} \frac{x-x_j} {x_i-x_j},时间复杂度为O(n^2). ,f:i=1nbij=ixixjxxj,O(n2).

若 在 程 序 实 现 中 , 每 一 次 计 算 x i − x j 值 时 都 求 一 次 其 的 乘 法 逆 元 , 时 间 复 杂 度 将 乘 上 l o g   n , 我 们 选 择 先 计 算 所 有 x i − x j 的 乘 积 , 再 求 逆 元 . 若在程序实现中,每一次计算x_i-x_j值时都求一次其的乘法逆元,时间复杂度将乘上log\ n,我们选择先计算所有x_i-x_j的乘积,再求逆元. ,xixj,log n,xixj,.

另外,此处求逆元必须选择快速幂方法,否则你将陷入深深的迷惑中.为什么呢?在此题中,可能会需要求负数的逆元,但根据辗转相除法求负数和-1的最大公约数时,求得的最大公约数为-1(在cmath库函数__gcd中计算也会得到-1,可见其运用的是辗转相除法).可是在数学定义上,gcd(a,b)=gcd(|a|,|b|),也就是说负数和-1的最大公约数为1.贝祖等式ax+by=gcd(a,b)的gcd(a,b)显然是数学定义上的.那么便不能得到ax+by=1,或者说我们算的其实是ax+by=-1,完全错误了.(我在这个问题上困扰了接近1个小时TAT)

代码:

#include <bits/stdc++.h>
#define ll long long

using namespace std;
ll n,k,x[2005],y[2005],mod=998244353,ans;//998244353是个题目常用取模质数 

ll fpm(ll x,ll power,ll mod) 
{
    x %= mod;
    ll ans = 1;
    for (; power; power >>= 1,(x*=x)%=mod)
    	if(power&1) (ans*=x)%=mod;
    return ans;
}

ll inv(ll x)
{
	return fpm(x,mod-2,mod);
}

int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>x[i]>>y[i];
	}
	for(int i=1;i<=n;i++)
	{
		ll ans_up=y[i],ans_down=1;
		for(int j=1;j<=n;j++)
		{
			if(j==i) continue;
			ans_up=(ans_up*(k-x[j]))%mod;
			ans_down=(ans_down*(x[i]-x[j]))%mod;
		}
		ans=(ans+(ans_up*inv(ans_down)))%mod;
	}
	cout<<(ans+mod)%mod<<endl;
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值