0

As part of an experiment I wanted to see how long it would take to compute Ack(0,0) to Ack(4,19) both with and without caching/memoization. But I keep running into a simple stumbling block... My stack keeps overflowing.

Here's my code:

import org.joda.time.{Seconds, DateTime}
import scala.collection.mutable

// Wrapper case class to make it easier to look for a specific m n combination when using a Map 
// also makes the code cleaner by letting me use a match rather than chained ifs
case class Ack(m: Int, n: Int)

object Program extends App {
  // basic ackermann without cache; reaches stack overflow at around Ack(3,11)
  def compute(a: Ack): Int = a match {
    case Ack(0, n) => n + 1
    case Ack(m, 0) => compute(Ack(m - 1, 1))
    case Ack(m, n) => compute(Ack(m - 1, compute(Ack(m, n - 1))))
  }

  // ackermann WITH cache; also reaches stack overflow at around Ack(3,11)
  def computeWithCache(a: Ack, cache: mutable.Map[Ack, Int]): Int = if(cache contains a) {
    val res = cache(a)
    println(s"result from cache: $res")
    return res // explicit use of 'return' for readability's sake
  } else {
    val res = a match {
      case Ack(0, n) => n + 1
      case Ack(m, 0) => computeWithCache(Ack(m - 1, 1), cache)
      case Ack(m, n) => computeWithCache(Ack(m - 1, computeWithCache(Ack(m, n - 1), cache)), cache)
    }
    cache += a -> res
    return res
  }

  // convenience method
  def getSeconds(start: DateTime, end: DateTime): Int = 
    Seconds.secondsBetween(start, end).getSeconds

  val time = DateTime.now
  val cache = mutable.Map[Ack, Int]()

  for{ 
    i <- 0 to 4
    j <- 0 until 20
  } println(s"result of ackermann($i, $j) => ${computeWithCache(Ack(i, j), cache)}. Time: ${getSeconds(time, DateTime.now)}")
}

I run Scala 2.11.1 in IntelliJ Idea 13.1.3 with the Scala and SBT plugins.

Is there anything I can do to not stack overflow at around Ack(3, 11)?

I've tried adding javacOptions += "-Xmx2G" to my build.sbt, but it just seems to make the problem worse.

Electric Coffee
  • 11,733
  • 9
  • 70
  • 131
  • Ackermann's function is super-exponential in growth, in both time and space. You might try a different algorithm: http://home.versatel.nl/vspickelen/Largefiles/Ackerman.htm – Bob Dalgleish Jul 07 '14 at 21:40
  • I've seen people do this in Java, C++ and even C. Surely if it can be done in Java, it can be done in Scala too – Electric Coffee Jul 07 '14 at 21:43
  • 3
    That's not my point. Pure recursive Ackermann's will overflow the stack very quickly. The alternative implementations don't grow as quickly in space used. Also, just increasing the JVM size isn't enough; you have to increase the stack size available to the program. – Bob Dalgleish Jul 07 '14 at 21:57
  • And how would I increase the stack size? – Electric Coffee Jul 07 '14 at 21:58
  • The parameter is `-Xss4M` to use, for instance, 4 Megabytes of stack space. – Bob Dalgleish Jul 07 '14 at 21:59

0 Answers0