简体   繁体   中英

Why does one of these Ruby methods result in 'stack overflow' but the other doesn't? (Permutations Algorithm)

Below are two slightly different methods for listing all lexicographic permutations of N objects. I can't understand why the first method works fine for smallish N, but fails above a certain limit and results in 'stack overflow'. The second method; however, works just fine up to my tested limit of 10**6. Thanks in advance for your help and insight!

$count = 0
$permutations = []

  def perms(array)  

     $permutations = array
     $count += 1

     if array.length <= 1
        return $permuations
     end

     i = (array.length - 2)
     until array[i] < array[i+1]
        i -= 1    
     end

     if i < 0
        return $permutations
     end

     j = (array.length - 1)
     until array[j] > array[i]
        j -= 1
     end

     array[i], array[j] = array[j], array[i]

     i += 1
     j = (array.length - 1)
     until j < i
        array[i], array[j] = array[j], array[i]
        i += 1
        j -= 1
     end

     while $count <= (10**6)-2
        perms(array)
     end
  end

  perms([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
  print $permutations

And here's the second method...

perm_limit = (10**6)

$count = 1 

def perms(array)
   if array.length <= 1
      return false
   end

   i = (array.length - 2)
   until array[i] < array[i+1]
      i = (i - 1)
   end

   if i < 0
      return false
   end

   j = (array.length - 1)
   until array[j] > array[i]
      j = (j - 1)
   end

   array[i], array[j] = array[j], array[i]

   i = (i + 1)
   j = (array.length - 1) 
   until j < i
      array[i], array[j] = array[j], array[i]
      i = (i + 1)
      j = (j - 1)
   end

   $count += 1 

   return true
end

array = [0,1,2,3,4,5,6,7,8,9]

while perms(array) == true
   if $count == perm_limit
      print array
   end
end    

Again, thanks.

The first code sample you provide is a recursive function:

 while $count <= (10**6)-2
    perms(array)
 end

The function is calling itself, is calling itself, is calling itself until your stack overflows (everytime a function is called, space on stack is allocated).

Your second algorithm does not use a recursive function and so the depth of your stack is only one - your stack is not growing.

For more information see "What is a stack overflow" . The question is for Java, but the concept is the same for all stack-based languages.

Recursive vs. iterative

So why are we writing recursive functions/algorithms if they can overflow? Because recursion can model some problems very nicely, and it can be easier to write a recursive algorithm (it's considered more "mathematically beautiful") than an iterative one. If you know that your recursion depth won't be too deep, then recursion might be the preferred method.

On the other hand, an iterative algorithm is usually the preferred if you're worried about your stack space. An iterative function can be easier or harder to write depending on how you model the problem. All recursive functions can be converted to iterative functions.

On a side note: there are some languages where recursion and stack space is not a problem. These languages may use "tail-calls" meaning the function is recursive, but instead of allocating new space on the stack, it simply re-uses the current function's stack space (and so the stack never grows). You can read more here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM