算法中的递归和生成函数


18

组合学在计算机科学中起着重要作用。我们经常在分析以及算法设计中使用组合方法。例如,一种用于在图形中查找ķ顶点覆盖集的方法可能只是检查所有(ñķ)可能的子集。尽管二项式函数呈指数增长,但如果ķ是某个固定常数,我们将通过渐近分析得出多项式时间算法。

通常,现实生活中的问题需要更复杂的组合机制,我们可以根据复发情况定义这些机制。一个著名的例子是斐波那契数列(天真的)定义为:

F(ñ)={1个如果 ñ=1个0如果 ñ=0F(ñ-1个)+F(ñ-2)除此以外

现在,使用该递归计算第ñ个项的值呈指数增长,但是由于有了动态编程,我们可以在线性时间内对其进行计算。现在,并不是所有的递归都适合于DP(即阶乘函数),但是当将某些计数定义为递归而不是生成函数时,它是潜在的可利用属性。

生成函数是一种对给定结构进行形式化计数的优雅方法。也许最著名的是定义为的二项式生成函数:

(X+ÿ)α=∑ķ=0∞(αķ)Xα-ķÿķ

幸运的是,这有一个封闭式解决方案。并非所有生成函数都允许进行这种紧凑的描述。

现在我的问题是:算法设计中多久使用一次生成函数?很容易看出如何利用它们来通过分析来了解算法所需的增长率,但是当创建解决某些问题的方法时,它们可以告诉我们什么问题?

如果多次将相同的计数重新构造为递归,则可能适合进行动态编程,但是相同的生成函数可能又具有封闭形式。因此,它并不是那么均匀地削减。


如果生成函数给出的公式(例如,斐波那契数的Binet公式)可用于计算数字,而不是使用递归(也许更有效),您是否认为这是答案?
— Aryabhata 2012年

Answers:


11

在设计计数算法时,生成函数很有用。也就是说,不仅在寻找具有某种属性的对象的数量时,而且在寻找枚举这些对象的方法(并且可能会生成计算对象的算法)时。罗纳德·格雷厄姆(Ronald Graham),唐纳德·克努斯(Donald Knuth)和奥伦·帕塔什尼克(Oren Patashnik)在《具体数学》的第7章中做了非常好的介绍。下面的示例来自这些书(错误和缺乏清晰度是我的)。

假设您正在寻找使用给定硬币集进行更改的方法。例如,对于常见的美国面额¹,可能的硬币为。要给¢42零钱,一种可能性是;另一种可能性是。我们将写。更笼统地说,我们可以为所有更改方式编写生成函数: 在更专业的术语中,是在五个变量的幂级数空间中的一项[ 25 ] [ 10 ] [ 5 ] [ 1 ] [ 1 ] [ 10 ] [ 10 ] [ 10 ] [ 10 ] [ 1 ] [ 1 ] 42 ⟨ [ 25 ] [ 10 ][1个],[5],[10],[25],[100][25][10][5][1个][1个][10][10][10][10][1个][1个]ħ = Σ ħ ≥ 0 Σ q ≥ 0 Σ d ≥ 0 Σ Ñ ≥ 0 Σ p ≥ 0 [ 100 ] ^ h [ 25 ] q [ 10 ] d [ 5 ] n [ 1 ] p42⟨[25][10][5][1个]2⟩=⟨[10]4[1个]2⟩

H=∑H≥0∑q≥0∑d≥0∑ñ≥0∑p≥0[100]H[25]q[10]d[5]ñ[1个]p
[ 100 ] ,[ 25 ] ,[ 10 ] ,[ 5 ] ,[ 1 ]H[100],[25],[10],[5],[1个]。通过定义此空间中单项式的估值 然后,赋予分的改变的方法是,估值为的单身人士的数量。我们可以用递增的方式表示,方法是先写下仅改变便士的方法,然后写下改变便士和镍的方法,以此类推。(意思是没有硬币。) v v
⟨[100]H[25]q[10]d[5]ñ[1个]p⟩=100H+25q+10d+5ñ+p
vvP Ñ 我P = 我+ [ 1 ] + [ 1 ] 2 + [ 1 ] 3 +HPñ一世
P=I+[1]+[1]2+[1]3+…=II−[1]N=(I+[5]+[5]2+[5]3+…)P=PI−[5]D=(I+[10]+[10]2+[10]3+…)N=NI−[10]Q=(I+[25]+[25]2+[25]3+…)D=DI−[25]H=(I+[100]+[100]2+[100]3+…)Q=QI−[100]
如果您想计数而不只是列举更改的方法,那么有一种简单的方法可以使用我们获得的正式系列文章。应用同态 的系数在是的方式,得到数美分变化。
S:[1]↦X,[5]↦X5,[10]↦X10,[25]↦X25,[100]↦X100
Xv小号(C)v

一个更难的例子:假设您想研究所有用2×1多米诺骨牌平铺矩形的方法。例如,有两种方法可以使用两个水平多米诺骨牌或两个垂直多米诺骨牌来平铺2×2矩形。计算矩形的平铺方法相当容易,但是情况很快就变得不明显了。我们可以通过将多米诺骨贴在一起来枚举高度为3的水平带的所有可能的平铺,这会快速产生重复的模式: 3 × n2×ñ3×ñ

{ü=Ø+大号V+ΓΛ+≡üV=一世ü+=-VΛ=一世ü+-=Λ
其中有趣的形状代表基本的多米诺骨牌排列:不是多米诺骨牌,是水平多米诺骨牌左侧的垂直多米诺骨牌,是与高度3的条带的底部对齐的垂直多米诺骨牌,是与条带的顶部对齐的水平多米诺骨牌,其下还有两个水平的多米诺骨牌,向右走一步,在此,乘法表示水平级联,而不是可交换的,但是基本模式之间存在方程,这些方程构成该幂级数中的变量。与以前的硬币一样,我们可以用代替每个多米诺骨牌,并得到一个生成序列,用于生成Ø大号一世-=X3×(2ñ/3)矩形(即的系数是平铺区域的矩形的方法的数量,该区域包含多米诺骨牌,宽度为)。该系列还可以更通用的方式使用。例如,通过区分垂直和水平的多米诺骨牌,我们可以计算给定数量的垂直和水平的多米诺骨牌。X3ķ6ķ3ķ2ķ

再次阅读“ 具体数学 ”以减少匆忙的演讲。

¹ 我知道我的清单不完整;假定适用于数学示例的简化美国
。²² 而且,如果出现这种情况,请假定球形硬币。
³ 和更好的排版。


8

我记得在2001年的一次学生编程比赛中必须解决的一个问题。这个问题是:

给定1、7、13,...的质量(我不记得是哪个质量,但是有一组有限的确定质量),请设计一个函数,该函数确定是否可以使用此函数在给定的重量上称重群众集。

我从嵌套循环开始,但很快就碰壁了。然后我意识到,在继续进行较重的质量之前,我必须先枚举较轻的质量可以做什么。我可以使用许多非嵌套循环来解决此问题。

那时我还不是很自大和自负(我知道并实践过生成函数),我可能已经定义了生成函数的问题,例如:

定义F(X)为OGF,以给出给定质量集的权重称重方式的数量。ñ

给定单个质量1,我可以在右秤盘上称重多少?

三种可能性:

  • 如果将砝码放在左锅上,则可以称重1。
  • 如果将砝码放在正确的平底锅上,则重量为-1。
  • 如果我不使用质量,则可以称0。

因此,有一种方法可以权衡,一种方法可以权衡,另一种方法可以权衡。该质量的生成函数类似于-1个01个X-1个+1个+X,它对应于:

1个-X3X(1个-X)

为单质的生成函数是,它是:米X-米+1个+X米

1个-X3米X米(1个-X米)

给定质量的多集,表示为单个质量生成函数的乘积:中号F

F(X)=∏米∈中号(1个-X3米)X∑米∈中号米∏米∈中号(1个-X米)

现在,给定一个可以对多项式执行运算的程序包,您只需要:

  • 计算两个产品。
  • 从最低的等级开始进行这些产品的划分。(终止)
  • 移位多项式(用欧氏除法,保留商并倾销余数)Xķ

这样就完成了。现在,您的多项式具有在索引处加权的方法的数量。唯一的输入是质量的多重集。w≥0w中号

我使用数学上合理的成分设计了算法。该算法的主要部分是具有最低优先级的多项式除法,它是线性的,可以通过现成的软件包来实现。它可能不是最佳选择,但它的性能肯定比我在比赛中要好,而且错误率也较低。

如果仔细观察除法过程,您很快就会发现余数可以看作过程的每个状态的“当前隐藏状态”,并且是商的结果。当“当前隐藏状态”在任何地方都达到零时,该过程终止。

您可以将多项式实现为数组,或者如果它们真的很稀疏,则实现为索引系数有序列表,但这不会改变算法。


3

ℓγℓ+1个(米)=(2ℓ-米)γℓ(米)+(米-ℓ+1个)γℓ-1个(米),γ0(米)=1个,γ米+1个(米)=Ë。
γℓ(米)=米(γℓ(米-1个)-γℓ-1个(米-1个))γ(0)γ(米),再次使用生成函数。如果您很好奇,您可以在本文中找到解决方案,尽管我们从不理会包括此推导。

0

最清楚的例子也许是对Quicksort及其许多变体的广泛研究。组合考虑因素决定了替代方法的考虑因素,分析相当复杂的方程式的解决方案表明(或没有)它们的性能优势。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.