1

I tried to write a function for fast power in scala, but I keep getting java.lang.StackOverflowError. I think it has something to do with two slashes that use in the third line when I recursively called this function for n/2. Can someone explain why is this happening

def fast_power(x:Double, n:Int):Double = {
if(n % 2 == 0 && n > 1)
        fast_power(x, n/2) * fast_power(x, n /2)
else if(n % 2 == 1 && n > 1)
        x * fast_power(x, n - 1)
else if(n == 0) 1
else  1 / fast_power(x, n)
}
Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • Although you can blow the stack if your n is too big(I mean > 1000). Take a look in one of the many articles about recursion in Scala, like https://medium.com/@olxc/trampolining-and-stack-safety-in-scala-d8e86474ddfa – Emiliano Martinez Apr 03 '18 at 20:55
  • 1
    What happens when `n` equals `1`? – jwvh Apr 03 '18 at 21:02
  • Even if you managed to fix it, it still wouldn't be "fast", because you are invoking `fastPower` twice in the `remainder = 0` case. The overall runtime is still linear instead of logarithmic. – Andrey Tyukin Apr 03 '18 at 21:38

2 Answers2

2

Your code doesn't terminate, because there was no case for n = 1. Moreover, your fast_power has linear runtime.

If you write it down like this instead:

def fast_power(x:Double, n:Int):Double = {
  if(n < 0) {
    1 / fast_power(x, -n)
  } else if (n == 0) {
    1.0
  } else if (n == 1) {
    x
  } else if (n % 2 == 0) {
    val s = fast_power(x, n / 2)
    s * s
  } else {
    val s = fast_power(x, n / 2)
    x * s * s
  }
}

then it is immediately obvious that the runtime is logarithmic, because n is at least halved in every recursive invocation.

I don't have any strong opinions on if-vs-match, so I just sorted all the cases in ascending order.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • This is the better answer. +1 for pointing out the runtime being logarithmic when assigning `fast_power(x, n/2)` to a `val` and multiplying it by itself afterwards. Came here for this. – Boregore Mar 15 '19 at 02:08
-1

Prefer the match construct instead of multiple if/else blocks. This will help you isolate the problem you have (wrong recursive call), and write more understandable recursive functions. Always put the termination conditions first.

def fastPower(x:Double, m:Int):Double = m match {
  case 0 => 1
  case 1 => x
  case n if n%2 == 0 => fastPower(x, n/2) * fastPower(x, n/2)
  case n => x * fastPower(x, n - 1)
}
retrospectacus
  • 2,518
  • 1
  • 11
  • 18
  • 1
    There is nothing "fast" in this "fastPower", because you are calling "fastPower" twice in the `mod = 0` case. The runtime is therefore not logarithmic. Not entirely your fault, but you could have pointed it out. – Andrey Tyukin Apr 03 '18 at 21:37