**这是一种用相当古老的语言(标准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
是除数。
以下是我想象算法的工作原理:
首先,如果ts = []
表示我们没有多项式来划分,所以我们返回qs,我们的累积商数结果作为答案。由于我们继续将((m-n, a/b) :: qs)
添加到qs列表的头部,我们需要反转列表以使列表以最大指数开头。
当除数的指数n大于我们正在处理的当前功率m时,第一个if条件处理这种情况。在这种情况下,我们只是不添加元素并返回(rev qs, (m,a) :: ts)
,即(quo, rem)
其他条件是我感到困惑的部分。我知道第二个方括号((m-n, a/b) :: qs)
是我们要添加的累积结果,但polysum ts (map (termproduct(m-n, ~a/b)) us))
实际上在做什么?这背后的数学是什么?
通常,我们将多项式除数一次分开,即$ 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测试它。
答案 0 :(得分:3)
我认为您目前的理解大多是正确的,而且我不确定您在else
分支中究竟有什么理解。
基本理念和不变性
为避免对名称阴影造成混淆,请ts0
为传递给ts
和polyquotremd
= us0
的{{1}}的值。然后,对于递归的每个步骤从外部上下文捕获((n,b) :: us)
的{{1}},除了最后一个,即由返回quo ts qs
和us0
分支处理的那些,以下不变量持有
quo [] qs = (rev qs, [])
换句话说,我们迭代地(通过递归)在答案(if m < n then (rev qs, (m,a) :: ts)
)和新的ts0 = us0 * rev qs + ts
中产生代表当前其余部分的最低权力。显然,如果我们可以通过此流程降低qs
的最高功率,使其最高功效ts
小于ts
最高功率m
,那么我们可以在us0
和n
。所以我们现在需要做的就是实际降低qs
的最高功率。我们得到的只是标准long division算法。
长除法算法详情
长期分工的一个步骤如下:
对于红利(当前休息)ts
和除数ts
中的最高字词,即((m,a) :: ts)
和((n,b) :: us)
,请检查(m,a)
。如果是 - 完成算法。
将(n,b)
除以m < n
并找到他们的商数。这显然是(m,a)
乘以该商,即(n,b)
乘以整数除数。请注意,这不是代码中的行。等一下,我们也会到那里。
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)
。显然,这只是将多项式乘以单个项的一种方法。
us
来将此减法替换为和。所以现在是(m-n, a/b)
现在我们可以注意到最高条款实际上会相互抵消。这并不奇怪,因为我们将乘数计算为它们的商,即完全如此,它们会相互抵消。所以现在我们可以删除它们并用代码中的那一行替换它:
-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))
。如果你想要一个例子 - 你可以在我上面提到的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
。乘数为-x
,x+1
为-1
=&gt;乘数为2*x
x+2
再次,您可以看到除数的非最高项的变化会影响结果,但不影响结果的最高项。