8.2 雷米兹算法

雷米兹介绍

  切比雪夫只是提出理论,但是没给出计算方法。乌克兰数学家雷米兹Evgeny Yakovlevich Remez提出了计算切比雪夫近似值的算法。
在这里插入图片描述

  雷米兹对切比雪夫近似值提供一个操作简单的算法,这是个迭代过程。
  N次迭代,每次两步。对于这种迭代过程,是没法预测迭代多少步,类似的迭代过程还有线性代数中的Gram-Schmidt分解。但是他们有个共同的特点,就是数学家们证明了迭代过程的收敛性,假如不收敛,迭代再多次也是无益的,就比如Hessenberg矩阵的QR分解,迭代无数次还是Hessenberg矩阵,因为不收敛。雷米兹算法的收敛性,我这里不提供证明,因为太难了,哈哈,我也看不懂。

算法第一步——解方程

  假设是求sin(x)在(-π/16, π/16)的九阶切比雪夫近似值。这个手算是不太可能的,会比较累。九阶多项式,有10个我们要求的未知数,其形式如下:
a x 9 + b x 8 + c x 7 + d x 6 + e x 5 + f x 4 + g x 3 + h x 2 + i x + j ax^9+bx^8+ cx^7+dx^6+ex^5+fx^4+gx^3+hx^2+ix+j ax9+bx8+cx7+dx6+ex5+fx4+gx3+hx2+ix+j
  我们把未知数有a到j十个字母来表示。N次迭代,按下面步骤来:
  在区间内,找任意11(根据切比雪夫的N+2定义, N = 9 , 9 + 2 = 11 N=9,9+2=11 N=9,9+2=11)个点。
  第一步 求出系数,这个系数要求在11个点(N+2)上,切换正负号之后,错误幅值相等。
  将这11个点的值代入公式:
a x 0 9 + b x 0 8 + c x 0 7 + d x 0 6 + e x 0 5 + f x 0 4 + g x 0 3 + h x 0 2 + i x 0 + j = s i n ( x 0 ) − e a x 1 9 + b x 1 8 + c x 1 7 + d x 1 6 + e x 1 5 + f x 1 4 + g x 1 3 + h x 1 2 + i x 1 + j = s i n ( x 1 ) + e ⋮ a x 10 9 + b x 10 8 + c x 10 7 + d x 10 6 + e x 10 5 + f x 10 4 + g x 10 3 + h x 10 2 + i x 10 + j = s i n ( x 1 0 ) − e ax_0^9+bx_0^8+ cx_0^7+dx_0^6+ex_0^5+fx_0^4+gx_0^3+hx_0^2+ix_0+j = sin(x_0) - e\\ ax_1^9+bx_1^8+ cx_1^7+dx_1^6+ex_1^5+fx_1^4+gx_1^3+hx_1^2+ix_1+j = sin(x_1) + e\\ \vdots\\ ax_{10}^9+bx_{10}^8+ cx_{10}^7+dx_{10}^6+ex_{10}^5+fx_{10}^4+gx_{10}^3+hx_{10}^2+ix_{10}+j = sin(x_10) - e\\ ax09+bx08+cx07+dx06+ex05+fx04+gx03+hx02+ix0+j=sin(x0)eax19+bx18+cx17+dx16+ex15+fx14+gx13+hx12+ix1+j=sin(x1)+eax109+bx108+cx107+dx106+ex105+fx104+gx103+hx102+ix10+j=sin(x10)e
  e是误差。所以误差e也是个未知数。这11个等式,计算出来的a~j和误差e肯定不是最终结果。这个计算过程,扩展到任意阶是这样的公式:
c 0 + c 1 h i + ⋅ ⋅ ⋅ + c n h i n + ( − 1 ) i e = f ( x i ) i = 0 , 1 , 2 , . . . , n + 1 c_0 + c_1h_i + · · · + c_nh^n_i + (-1)^ie = f(x_i)\\ i = 0, 1, 2, . . . , n + 1 c0+c1hi+⋅⋅⋅+cnhin+(1)ie=f(xi)i=0,1,2,...,n+1
  公式中 h i n h_i^n hin是点的n次方,c是要求的系数。上面的例子,这一步我们得到了一个11元一次方程组,这个时候用高斯消元、LU分解或LUP分解可以解出10个系数和一个误差e。
  但是第一步的11个点,并不恰好是最大误差的11个点。他们的误差值e,也不是最小误差值。

算法第二步——重新选点

  把第一步得到的方程。利用二分法,把第一步的11个点,然后加上左边界和右边界重新划分为12个区域。
  在这12个区域,找最大误差或最小误差,就是说找误差绝对值最大的点。误差值按以下公式计算:
e ( x ) = s i n ( x ) − p ( x ) e(x) = sin(x)-p(x) e(x)=sin(x)p(x)
  在误差绝对值最大的点中去掉一个最小值,产生一个新的集合。
  一直循环迭代,直到第一步计算出的误差值e与第二步的误差最大值相等。
  比如三阶切比雪夫多项式的第二步就是下图这个样子:
在这里插入图片描述

  图中的函数是误差函数,图上*号是第一步的五个点,可以看出一正一负的,绝对值相等。这些区间里找到了最大值的点 x 0 ∗ , x 1 ∗ , ⋯   , x n + 1 ∗ x^∗ _0, x^∗_1, \cdots , x^∗_{n+1} x0,x1,,xn+1,也就是图上x坐标的圆圈。但是这样产生的点要比需要的点多一个的,所以必须去掉一个最大误差点。图中有一个区域,是误差绝对值最小的区域,我勾出来了,是所有区域里最大误差绝对值最小的区域,它的最大误差点也就是就是被剔除的点:
在这里插入图片描述

Java实现

雷米兹的核心代码如下:

/**
 * @param n 几次多项式
 */
public static BigDecimal[] remez(final int n,
                         final BigDecimal min,
                         final BigDecimal max,
                         final Function<BigDecimal, BigDecimal> target) {
    Random random = new Random();
    BigDecimal step1Error = null;
    BigDecimal step2Error = null;
    final BigDecimal[] horners = new BigDecimal[n + 1];
    while ((step1Error == null && step2Error == null) || step1Error.subtract(step2Error).abs().compareTo(MAX_ERROR) > 0) {
        // step1
        // 区间内区N个任意值
        // 0 和 1 之间 产生 3和6之间 6-3 =3.0 min + 3.0 * r
        BigDecimal[] randomValues = initRandomValue(n, min, max, random);
        // 产生一个n+2阶一次方程组
        final EquationGroup equationGroup = getEquationGroup(n, target, randomValues);
        final BigDecimal[] resolve = equationGroup.resolve();
        // 第一步最大误差
        step1Error = resolve[resolve.length - 1];
        // 将第一步得到11个值,利用二分法,形成10个分界点,这10个分界点与左右边界重新形成11个区域
        Region[] regions = getRegion(randomValues, min, max);
        // 在每个区域里找最大误差值或最小误差值
        // 第一个区间是 sin(x) - p(x) 正数,也就是最大值,第二个区间是最小值
        System.arraycopy(resolve, 0, horners,0, n + 1);
        final BigDecimal[] errors = new BigDecimal[n + 2];
        for (int i = 0; i < regions.length; i++) {
            Region region = regions[i];
            // 找出这些点
            final Error maxErrorPoint = getMaxErrorPoint(region, target, horners);
            randomValues[i] = maxErrorPoint.getPoint();
            // 然后求出最大误差
            errors[i] = maxErrorPoint.getError();
        }
        step2Error = Arrays.stream(errors).max(BigDecimal::compareTo).orElse(BigDecimal.ONE);

    }

    return horners;
}

Git地址

https://e.coding.net/buildt/learn/remez.git

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

醒过来摸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值