题目:http://acm.hdu.edu.cn/showproblem.php?pid=4851
题意:
给定一个非负整数
n
n
,请你构造一个次多项式
f(x)
f
(
x
)
,使得
∫10(f(x)−ex)2dx
∫
0
1
(
f
(
x
)
−
e
x
)
2
d
x
取得最小值。给出
f(x)
f
(
x
)
的
n+1
n
+
1
个项的系数,要求精度误差不超过
10−50
10
−
50
。
0≤n≤10
0
≤
n
≤
10
。
题解:
这个
n
n
实在是太小了,给人一种可以打表的感觉,于是先来打打表试试!
对于,设
f(x)=c
f
(
x
)
=
c
,则
这个函数的极小值点满足导数为 0 0 ,也就是,即 c=e−1 c = e − 1 。可以注意到常数项是不影响答案的。
对于 n=1 n = 1 ,一通强算之后也能得到 f(x)=(18−6e)x+(4e−10) f ( x ) = ( 18 − 6 e ) x + ( 4 e − 10 ) ,然而对于更高的情况就很难强算啦,所以需要一个更形式化的做法。
令 f(x)=∑ni=0ai⋅xi f ( x ) = ∑ i = 0 n a i ⋅ x i ,那么有
注意到这里有一个 ∫10xiexdx ∫ 0 1 x i e x d x 没有化简,考虑分部积分的形式
而 ∫10exdx=e−1 ∫ 0 1 e x d x = e − 1 ,所以可以归纳证明 ∫10xiexdx ∫ 0 1 x i e x d x 一定可以被表示成 bie+ci b i e + c i 的形式(因为之间的关系是数乘),则有
因此积分后的函数就是
现在要求这个 (n+1) ( n + 1 ) 元函数的最小值,可以使用拉格朗日乘数法。
令 L(a0,a1,⋯,an)=∑ni=0∑nj=0aiaji+j+1−∑ni=02ai(bie+ci)+e2−12 L ( a 0 , a 1 , ⋯ , a n ) = ∑ i = 0 n ∑ j = 0 n a i a j i + j + 1 − ∑ i = 0 n 2 a i ( b i e + c i ) + e 2 − 1 2 ,则有偏导 ∂L∂ai=2∑nj=0aji+j+1−(bie+ci) ∂ L ∂ a i = 2 ∑ j = 0 n a j i + j + 1 − ( b i e + c i ) 。
极小值的点一定是稳定点,且二阶偏导是正定的,一阶偏导等于 0 0 ,于是就有了个形如 ∑nj=0aji+j+1=bie+ci ∑ j = 0 n a j i + j + 1 = b i e + c i 的方程。
由于方程未知数的系数是有理数,所以 aj a j 也一定能写成 kje+tj k j e + t j 的形式,于是分别对于 bi b i 和 ci c i 进行一次高斯消元,就可以得到对应的 kj k j 和 tj t j 。
然而此题要求的精度十分离谱,所以需要更进一步的分析。可以归纳证明 kj k j 和 tj t j 都是整数,但是数字可能大于 109 10 9 ,但不会太大。
于是找两个大素数做模意义下的高斯消元,再用中国剩余定理计算出其实际值即可(需要实际值小于模数)。
不过我找的两个大素数是 1000000007 1000000007 和 1000000009 1000000009 ,而在 n=10 n = 10 的时候系数已经超过了模数(因为算出的 f(x) f ( x ) 系数居然出现了负数),所以我选择了第三个素数 998244353 998244353 ,得到对应的同余方程组之后,用python做中国剩余定理算出了实际值,大约有 4⋅1020 4 ⋅ 10 20 左右,刚好超出 64 64 位整型数的表示范围。
粗略估计为了保证精度,需要 e e 在小数点后位都是精确的,所以泰勒展开求出一个保留小数点后 70 70 位的值之后,将所有数字乘以 1070 10 70 转化为整数计算,得出每个系数的值,打入表中输出即可。
分析复杂度的话,最大的复杂度部分是 O(n3logn) O ( n 3 l o g n ) 的高斯消元,最后的高精度计算是常数的主要部分。
我是打成字符串后交的C++代码,但是可以使用Java的BigDecimal进行高精度浮点数的运算,会方便许多。
代码:
打表的代码感觉还是不要给了,等研究出Java的写法再补。