Algorithm Pascal三角形Scala:使用尾部递归方法计算Pascal三角形的元素
在帕斯卡的三角形中,三角形边缘的数字都是1,三角形内的每个数字都是它上面两个数字的和。帕斯卡三角形示例如下所示Algorithm Pascal三角形Scala:使用尾部递归方法计算Pascal三角形的元素,algorithm,scala,recursion,tail-recursion,Algorithm,Scala,Recursion,Tail Recursion,在帕斯卡的三角形中,三角形边缘的数字都是1,三角形内的每个数字都是它上面两个数字的和。帕斯卡三角形示例如下所示 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 我写了一个程序,用下面的技术计算帕斯卡三角形的元素 /** * Can I make it tail recursive??? * * @param c column * @param r row * @return */ def pascalTriangle(c: Int, r: Int): Int
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
我写了一个程序,用下面的技术计算帕斯卡三角形的元素
/**
* Can I make it tail recursive???
*
* @param c column
* @param r row
* @return
*/
def pascalTriangle(c: Int, r: Int): Int = {
if (c == 0 || (c == r)) 1
else
pascalTriangle(c-1, r-1) + pascalTriangle(c, r - 1)
}
例如,如果
i/p: pascalTriangle(0,2)
o/p: 1.
i/p: pascalTriangle(1,3)
o/p: 3.
上述程序正确,并按预期给出正确的输出。我的问题是,是否有可能编写上述算法的尾部递归版本?怎么做?试试看
def pascalTriangle(c: Int, r: Int): Int = {
@tailrec
def loop(c0: Int, r0: Int, pred: Array[Int], cur: Array[Int]): Int = {
cur(c0) = (if (c0 > 0) pred(c0 - 1) else 0) + (if (c0 < r0) pred(c0) else 0)
if ((c0 == c) && (r0 == r)) cur(c0)
else if (c0 < r0) loop(c0 + 1, r0, pred, cur)
else loop(0, r0 + 1, cur, new Array(_length = r0 + 2))
}
if ((c == 0) && (r == 0)) 1
else loop(0, 1, Array(1), Array(0, 0))
}
或
或
@DmytroMitin的第一个解决方案的一些优化: 用if c==0 | | c==R1替换if c==0&&r==01。 利用三角形的对称性,如果c的反射值大于r的一半,则使用c的反射值。 通过这些优化,通过1次和2次调用而不进行记忆,循环绘制30行三角形的调用次数从122760次减少到112375次,从1次减少到110240次
def pascalTail(c: Int, r: Int): Int = {
val cOpt = if (c > r/2) r - c else c
def loop(col: Int, row: Int, previous: Array[Int], current: Array[Int]): Int = {
current(col) = (if (col > 0) previous(col - 1) else 0) + (if (col < row) previous(col) else 0)
if ((col == cOpt) && (row == r)) current(col)
else if (col < row) loop(col + 1, row, previous, current)
else loop(0, row + 1, current, new Array(_length = row + 2))
}
if (c == 0 || c == r) 1
else loop(0, 1, Array(1), new Array(_length = 2))
}
我正在寻找一个代码来快速理解Pascal Triangle的尾部递归逻辑,偶然发现了这个线程。然而,我也想到了一个可读的解决方案,它可以清楚地说出逻辑。下面是我尝试的解决方案草案,但为了可读性可以进一步改进。从优化/性能的角度来看,我想这是很小心的
def pascal(c: Int, r: Int): Int = {
if(c > r) throw new RuntimeException
def symmetricalGet(row: Array[Int]) = {
val lastIndex = row.size - 1
lastIndex match {
case l if l >= c => row(c)
case l => {
val diffFromCenter = c - l
val mirrorIdx = l - diffFromCenter
row(mirrorIdx)
}
}
}
def computeRow(acc: Array[Int], isRowIdxOdd: Boolean): Array[Int] = {
val cArray = 1 +: acc
.sliding(2)
.map(_.sum)
.toArray
isRowIdxOdd match {
case true => cArray :+ cArray.last
case _ => cArray
}
}
@tailrec
def goNextRow(row: Int, acc: Array[Int] = Array(1, 1)): Array[Int] = {
val isOdd = row % 2 != 0
if(row == r) computeRow(acc, isOdd)
else goNextRow(row + 1, computeRow(acc, isOdd))
}
if(c == 0 || r <= 1 || c == r) 1
else symmetricalGet(goNextRow(2))
}
是的,与编写任何尾部递归函数的方法相同。您需要自己的剩余操作堆,一个用于跟踪结果的内部累加器。您对这种看似复杂的递归方法与迭代方法的优点有何看法?@GeoffLangenderfer您在谈论哪种递归方法,您在谈论哪种迭代方法?此示例生成一整行而不是一个单元格:@GeoffLangenderfer这是Java,不是斯卡拉。
def pascalTail(c: Int, r: Int): Int = {
val cOpt = if (c > r/2) r - c else c
def loop(col: Int, row: Int, previous: Array[Int], current: Array[Int]): Int = {
current(col) = (if (col > 0) previous(col - 1) else 0) + (if (col < row) previous(col) else 0)
if ((col == cOpt) && (row == r)) current(col)
else if (col < row) loop(col + 1, row, previous, current)
else loop(0, row + 1, current, new Array(_length = row + 2))
}
if (c == 0 || c == r) 1
else loop(0, 1, Array(1), new Array(_length = 2))
}
def pascal(c: Int, r: Int): Int = {
if(c > r) throw new RuntimeException
def symmetricalGet(row: Array[Int]) = {
val lastIndex = row.size - 1
lastIndex match {
case l if l >= c => row(c)
case l => {
val diffFromCenter = c - l
val mirrorIdx = l - diffFromCenter
row(mirrorIdx)
}
}
}
def computeRow(acc: Array[Int], isRowIdxOdd: Boolean): Array[Int] = {
val cArray = 1 +: acc
.sliding(2)
.map(_.sum)
.toArray
isRowIdxOdd match {
case true => cArray :+ cArray.last
case _ => cArray
}
}
@tailrec
def goNextRow(row: Int, acc: Array[Int] = Array(1, 1)): Array[Int] = {
val isOdd = row % 2 != 0
if(row == r) computeRow(acc, isOdd)
else goNextRow(row + 1, computeRow(acc, isOdd))
}
if(c == 0 || r <= 1 || c == r) 1
else symmetricalGet(goNextRow(2))
}