今天来看一个简单的算法,计算一元多项式的值。
暴力求值
对于多项式 ,最基本的计算思路就是逐次计算每一项,将结果相加。
应该将一个多项式抽象成什么样的数据结构呢?
注意到对于一个 次多项式,其系数刚好可以构成一个线性结构,因此可以用一个数组表示。
因此,我们可以写出如下的代码
pub fn evaluate_v1(coefficients: Vec<i32>, x: i32) -> i32 {
let mut res = 0;
for (i, a) in coefficients.iter().enumerate() {
let mut temp = 1;
// calculate x to the ith power
for _ in 0..i {
temp = temp * x as i32;
}
res = res + a * temp;
}
res
}
优化
以上代码虽然可以求值,但是在计算 时进行了大量的重复运算。
事实上,每一次外层循环都已经计算了当前的 ,在下一次循环时,只需要计算 就可以了。
pub fn evaluate_v2(coefficients: Vec<i32>, x: i32) -> i32 {
let mut res = 0;
let mut temp = 1;
for (i, a) in coefficients.iter().enumerate() {
// calculate x to the ith power, note x to the 0 power is 1
if i > 0 {
temp = temp * x as i32;
}
res = res + a * temp;
}
res
}
一共是进行了 次加法和 次乘法。
秦九韶算法(Horner's Rule)
秦九韶算法是一个高效的求解一元高次多项式值的算法,具体的原理如下:
对于 次多项式
将前 项提取公因子 ,得
再将括号内的前 项提取公因子 ,得
如此反复提取公因子 ,最后将方程化为
令
注意到第 到第 项可以得到一个递推式
令, ,可以得到
因此,我们可以写出如下代码
pub fn evaluate_v3(coefficients: Vec<i32>, x: i32) -> i32 {
let mut res = 0;
for i in (0..coefficients.len()).rev() {
res = coefficients[i] + res * x;
}
res
}
第一次循环计算的是 ,以此类推,最终只需要 次乘法和 次加法完成求值。
总结
今天我们一步一步的完成了一个高效的求一元多项式值的算法。
但是有一点小问题: 对于一个稀疏多项式,使用一元数组存储会造成极大的空间浪费。