在函数式语言中理解这种多项式除法算法

时间:2017-12-31 00:29:18

标签: algorithm math functional-programming sml

**这是一种用相当古老的语言(标准ML)实现的算法。我很难理解这个算法:

fun polyquotremd ts ((n,b) :: us) = 
    let fun quo [] qs = (rev qs, [])
        | quo ((m,a) :: ts) qs = 
            if m < n then (rev qs, (m,a) :: ts)
            else
                quo (polysum ts
                    (map (termproduct(m-n, ~a/b)) us))
                    ((m-n, a/b) :: qs)
    in
        quo ts []
    end;

这是目标。我们通过使用元组列表来密集地表示多项式,每个元组包含索引0处的幂和索引1处的系数。例如,$ x ^ 2 + 1 $表示为[(2, 1.0), (0, 1.0)]。这由类型(int*real)list给出,如下面的辅助函数所示。我们想要获得一个输出(quo, rem),每个输出都是一个元组列表,分别表示商和余数。 ts是要分割的多项式,而us是除数。

以下是我想象算法的工作原理:

  1. 首先,如果ts = []表示我们没有多项式来划分,所以我们返回qs,我们的累积商数结果作为答案。由于我们继续将((m-n, a/b) :: qs)添加到qs列表的头部,我们需要反转列表以使列表以最大指数开头。

  2. 当除数的指数n大于我们正在处理的当前功率m时,第一个if条件处理这种情况。在这种情况下,我们只是不添加元素并返回(rev qs, (m,a) :: ts),即(quo, rem)

  3. 其他条件是我感到困惑的部分。我知道第二个方括号((m-n, a/b) :: qs)是我们要添加的累积结果,但polysum ts (map (termproduct(m-n, ~a/b)) us))实际上在做什么?这背后的数学是什么?

  4. 通常,我们将多项式除数一次分开,即$ x + 1 $。但是这个算法首先使用$ x $,然后是$ 1 $。这怎么工作?为什么有~a/b(供您参考,意为-a/b)。

    助手功能:

    fun take ([], _) = []
    | take (x::xs, i) = if i>0
    then x :: take(xs, i-1)
    else [];
    
    fun drop ([], _) = []
    | drop (x::xs, i) = if i>0 then drop(xs,i-1)
    else x::xs;
    
    fun termproduct (m,a) (n,b) = (m+n, a*b) : (int*real);
    
    fun polyproduct [] us = []
        | polyproduct [(m,a)] us = map (termproduct(m,a)) us
        | polyproduct ts us =
        let 
            val k = length ts div 2
        in 
            polysum (polyproduct (take(ts,k)) us)
                    (polyproduct (drop(ts,k)) us)
        end;
    

    有人可以向我解释第3点吗?我很困惑,特别是通过算法的数学运算。但它的工作原理..这个代码编译,你可以用标准的ML测试它。

1 个答案:

答案 0 :(得分:3)

我认为您目前的理解大多是正确的,而且我不确定您在else分支中究竟有什么理解。

基本理念和不变性

为避免对名称阴影造成混淆,请ts0为传递给tspolyquotremd = us0的{​​{1}}的值。然后,对于递归的每个步骤从外部上下文捕获((n,b) :: us)的{​​{1}},除了最后一个,即由返回quo ts qsus0分支处理的那些,以下不变量持有

quo [] qs = (rev qs, [])

换句话说,我们迭代地(通过递归)在答案(if m < n then (rev qs, (m,a) :: ts))和新的ts0 = us0 * rev qs + ts 中产生代表当前其余部分的最低权力。显然,如果我们可以通过此流程降低qs的最高功率,使其最高功效ts小于ts最高功率m,那么我们可以在us0n。所以我们现在需要做的就是实际降低qs的最高功率。我们得到的只是标准long division算法。

长除法算法详情

长期分工的一个步骤如下:

  1. 对于红利(当前休息)ts和除数ts中的最高字词,即((m,a) :: ts)((n,b) :: us),请检查(m,a) 。如果是 - 完成算法。

  2. (n,b)除以m < n并找到他们的商数。这显然是(m,a)

  3. 乘以该商,即(n,b)乘以整数除数。请注意,这不是代码中的行。等一下,我们也会到那里。

  4. Sidenote 或如何在代码中完成此操作。如果不清楚:map是一个经典的高阶函数,它接受另一个函数和一个集合,并返回相同大小的新集合,其中每个元素都是该函数应用的结果。相应的源元素。现在(m-n, -a/b)被称为partial application:它接受两个参数(map (termproduct(m-n, a/b)) ((n,b) :: us)))的函数并产生一个参数的新函数(原始函数中的第二个参数),第一个参数固定为值(termproduct(m-n, a/b))。因此整个结构如下所示:将termproduct(或代码中的(m-n, a/b))的每个项乘以((n,b) :: us)。显然,这只是将多项式乘以单个项的一种方法。

    1. 从当前休息中减去此产品。我们可以通过在前一步骤上再乘以us来将此减法替换为和。所以现在是
    2. (m-n, a/b)

      现在我们可以注意到最高条款实际上会相互抵消。这并不奇怪,因为我们将乘数计算为它们的商,即完全如此,它们会相互抵消。所以现在我们可以删除它们并用代码中的那一行替换它:

      -1
      1. 最后我们需要将(polysum ((m,a) :: ts) (map (termproduct(m-n, ~a/b)) ((n,b) :: us))) 的这个商/乘数加到我们的结果多项式中。而唯一的小问题是我们以相反的顺序填写它,即我们首先找到最高项,下一个,依此类推。这在纸上工作正常,但ML中的列表以相反的顺序填充。这就是我们&#34;退出&#34;分支中包含(polysum ts (map (termproduct(m-n, ~a/b)) us))
      2. 如果你想要一个例子 - 你可以在我上面提到的Polynomial long division上的wiki文章中关注一个。只是为了重新迭代:该算法的每一步都是(m-n, a/b)的另一个递归。

        希望这会有所帮助。如果有些事情仍然不明确,请告诉我。

        更新(回复评论)

          

        我还没有完全了解~a / b将如何相互抵消。

        首先,您是否尝试过我上面引用的example in the wiki?如果是这样,那里到底有什么不清楚的?

        回到你的问题,有几种方法来看待它。从纯粹的机械观点来看,这里的问题是:rev qs的结果是什么?显然它是quo = termproduct (m-n, ~a/b) (n,b)。所以这与(m-n + n, ~a/b*b)中的最高词完全相反。

        从算法的更高级别,他们将完全取消,因为我们计算乘数(m, -a)的唯一目的是他们取消。您似乎没有得到我在&#34; 基本理念和不变量 &#34;中所描述的内容。部分,我不确定我能以不同的方式重申它,但我会尝试。分裂可能被视为一次减法多次与反击我们做了多少次。如果我们这样看,很明显我们应该尝试减去除数的一些乘法,以抵消除数的最高项。

        示例1:假设我们将ts除以(m-n, ~a/b)。如果我们要删除2*x^2 + 3*x + 4项,我们必须将除数乘以x^2 + x + 1然后减去。因此,结果为商= 2*x^2,其余为2 = 2

        示例2:假设我们将2*x^2 + 3*x + 4 - 2*(x^2 + x + 1)除以x + 2。请注意,虽然除数与previos示例不同,但我们仍需将其乘以2*x^2 + 3*x + 4以移除被除数中的最高项。这是因为决定这种倍增的唯一因素是最高项的比例。 Obvioulsy其他术语影响最终结果(休息),但它们不影响结果的当前术语。请注意,结果仍为商数= x^2 + 2*x + 3,但其余为2 = 2

        示例3:让2*x^2 + 3*x + 4 - 2*(x^2 + 2*x + 3)-x - 2划分2*x^3+3*x^2以查看差异。请注意,要取消x+1,我们的第一个乘数(即商中的最高项)在两种情况下均应相同x+2 = x^3 所以2*x^3/x我们得到了

        2*x^2

        同样适用于x+1

        x+1: 2*x^3+3*x^2 = 2*x^2*(x+1) + (2*x^3+3*x^2) - 2*x^2*(x+1) = 2*x^2*(x+1) + x^2
        

        从现在开始,结果会有所不同,因为我们继续使用x+2的{​​{1}}和x+2: 2*x^3+3*x^2 = 2*x^2*(x+2) + (2*x^3+3*x^2) - 2*x^2*(x+2) = 2*x^2*(x+2) - x^2 +x^2的不同中间休息x+1。这意味着下一个乘数将是-x^2 = x+2+x^2/x = +x

        -x^2/x

        现在,-x =&gt;的最后一步是x+1: 2*x^3+3*x^2 = (2*x^2+x)*(x+1) + x^2 - x*(x+1) = (2*x^2+x)*(x+1) - x x+2: 2*x^3+3*x^2 = (2*x^2-x)*(x+2) - x^2 - (-x)*(x+2) = (2*x^2-x)*(x+2) + 2*x 。乘数为-xx+1-1 =&gt;乘数为2*x

        x+2

        再次,您可以看到除数的非最高项的变化会影响结果,但不影响结果的最高项。