【问题标题】:Why does one of these Ruby methods result in 'stack overflow' but the other doesn't? (Permutations Algorithm)为什么其中一种 Ruby 方法会导致“堆栈溢出”,而另一种不会? (排列算法)
【发布时间】:2014-06-10 03:47:43
【问题描述】:

下面是列出 N 个对象的所有字典排列的两种略有不同的方法。我不明白为什么第一种方法适用于较小的 N,但在超过一定限制时失败并导致“堆栈溢出”。第二种方法;但是,在我测试的 10 ** 6 限制下工作得很好。提前感谢您的帮助和洞察力!

$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

这是第二种方法...

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    

再次感谢。

【问题讨论】:

    标签: ruby algorithm methods stack-overflow permutation


    【解决方案1】:

    您提供的第一个代码示例是一个递归函数:

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

    函数正在调用自己,正在调用自己,正在调用自己,直到堆栈溢出(每次调用函数时,都会分配堆栈空间)。

    您的第二个算法不使用递归函数,因此您的堆栈深度只有一个 - 您的堆栈没有增长。

    有关详细信息,请参阅"What is a stack overflow"。问题是针对 Java 的,但所有基于堆栈的语言的概念都是相同的。

    递归与迭代

    那么,如果递归函数/算法会溢出,我们为什么还要编写它们呢?因为递归可以很好地模拟一些问题,并且编写递归算法(它被认为更“数学上漂亮”)比迭代算法更容易。如果您知道递归深度不会太深,那么递归可能是首选方法。

    另一方面,如果您担心堆栈空间,通常首选迭代算法。根据您对问题的建模方式,迭代函数可能更容易或更难编写。 All recursive functions can be converted to iterative functions.

    附带说明:有些语言的递归和堆栈空间不是问题。这些语言可能使用“尾调用”,这意味着函数是递归的,但不是在堆栈上分配新空间,而是简单地重新使用当前函数的堆栈空间(因此堆栈永远不会增长)。你可以read more here

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-20
    • 1970-01-01
    • 2021-10-20
    • 2022-01-01
    • 2018-07-10
    • 2014-12-20
    • 1970-01-01
    相关资源
    最近更新 更多