如何在Julia中更改最大递归深度?

时间:2017-09-21 14:48:11

标签: julia

我很好奇Rosseta代码(https://rosettacode.org/wiki/Ackermann_function)对(4,2)参数的算法是多么快速和准确。但得到了StackOverflowError。

julia> using Memoize
@memoize ack3(m, n) =
m == 0 ? n + 1 :
n == 0 ? ack3(m-1, 1) :
ack3(m-1, ack3(m, n-1))

# WARNING! Next line has to calculate and print number with 19729 digits!
julia> ack3(4,2) # -> StackOverflowError 
# has to be -> 2003529930406846464979072351560255750447825475569751419265016973710894059556311
# ...
# 4717124577965048175856395072895337539755822087777506072339445587895905719156733

修改 奥斯卡史密斯是正确的,尝试ack3(4,2)是不现实的。这是从Rosseta的C ++翻译的版本:

module Ackermann
  function ackermann(m::UInt, n::UInt) 
    function ack(m::UInt, n::BigInt)
      if m == 0
        return n + 1
      elseif m == 1
        return n + 2
      elseif m == 2      
        return 3 + 2 * n;
      elseif m == 3
        return 5 + 8 * (BigInt(2) ^ n - 1)
      else
        if n == 0
          return ack(m - 1, BigInt(1)) 
        else 
          return ack(m - 1, ack(m, n - 1))
        end
      end
    end

    return ack(m, BigInt(n))
  end
end

julia> import Ackermann;Ackermann.ackermann(UInt(1),UInt(1));@time(a4_2 = Ackermann.ackermann(UInt(4),UInt(2)));t = "$a4_2"; println("len = $(length(t)) first_digits=$(t[1:20]) last digits=$(t[end-20:end])")
  0.000041 seconds (57 allocations: 33.344 KiB)
len = 19729 first_digits=20035299304068464649 last digits=445587895905719156733

2 个答案:

答案 0 :(得分:3)

Julia本身对堆栈大小没有内部限制,但操作系统却有。这里的确切限制(以及如何更改它们)将取决于系统。在我的Mac上(我假设其他POSIX-y系统),我可以检查并更改我的shell使用ulimit调用的程序的堆栈大小:

$ ulimit -s
8192

$ julia -q
julia> f(x) = x > 0 ? f(x-1) : 0 # a simpler recursive function
f (generic function with 1 method)

julia> f(523918)
0

julia> f(523919)
ERROR: StackOverflowError:
Stacktrace:
 [1] f(::Int64) at ./REPL[1]:1 (repeats 80000 times)

$ ulimit -s 16384

$ julia -q
julia> f(x) = x > 0 ? f(x-1) : 0
f (generic function with 1 method)

julia> f(1048206)
0

julia> f(1048207)
ERROR: StackOverflowError:
Stacktrace:
 [1] f(::Int64) at ./REPL[1]:1 (repeats 80000 times)

我相信适合你的堆栈的递归调用的确切数量将取决于你的系统和函数本身的复杂性(也就是说,每个递归调用需要在堆栈上存储多少)。这是最低限度的。我不知道为了计算Ackermann函数你需要多大的堆栈限制。

请注意,我将堆栈大小加倍,并且它使递归调用的数量增加了一倍多 - 这是因为持续的开销:

julia> log2(523918)
18.998981503278365

julia> 2^19 - 523918
370

julia> log2(1048206)
19.99949084151746

julia> 2^20 - 1048206
370

答案 1 :(得分:2)

只是fyi,即使你改变了最大递归深度,你也不会得到正确的答案,因为Julia使用64位整数,所以整数溢出make make不起作用。为了得到正确的答案,你将不得不使用大的int来获得任何希望。接下来的问题是你可能不想记忆,因为几乎所有的计算都没有重复,你将计算超过10 ^ 19729个不同输入的函数,你真的不想存储它们。