用Julia实现递归函数返回的问题

用Julia实现递归函数返回的问题,julia,Julia,我已经在网格中实现了下一个向下和向右的代码,我正在尝试实现某种计数,比如return+1,但是我不能,如果我被迫使用global,它会使运行时相当长(比如乘以8,尽管它随迭代次数而变化)。 请告知如何用这种语言正确编码 我也想过使用C语言中的指针,但很难实现 function numOfPaths(NominalSize,x,y) (x < NominalSize) && numOfPaths(NominalSize,x+1,y) (y < Nomin

我已经在网格中实现了下一个向下和向右的代码,我正在尝试实现某种计数,比如return+1,但是我不能,如果我被迫使用global,它会使运行时相当长(比如乘以8,尽管它随迭代次数而变化)。 请告知如何用这种语言正确编码

我也想过使用C语言中的指针,但很难实现

function numOfPaths(NominalSize,x,y)
    (x < NominalSize) && numOfPaths(NominalSize,x+1,y)
    (y < NominalSize) && numOfPaths(NominalSize,x,y+1)
    (x >= NominalSize && y>=NominalSize) && global count+=1;
end
count = 0;
t1 = @elapsed numOfPaths(16,0,0)
函数numofpath(标称大小,x,y)
(x=NominalSize&y>=NominalSize)和&global count+=1;
结束
计数=0;
t1=@appeatednumofpath(16,0,0)
第二个版本使用Ref,而不是使用全局,几乎相同的可怕性能

function numOfPaths(NominalSize,x,y)
   (x < NominalSize) && numOfPaths(NominalSize,x+1,y)
   (y < NominalSize) && numOfPaths(NominalSize,x,y+1)
   ((y >= NominalSize) && (x >= NominalSize)) && (ref[] += 1 ;)
end
count = 0;
ref = Ref(count)
t1 = @elapsed anse = numOfPaths(15,0,0)
函数numofpath(标称大小,x,y)
(x=NominalSize)和&(x>=NominalSize))和&(参考[]+=1;)
结束
计数=0;
ref=ref(计数)
t1=@passed anse=numopath(15,0,0)

对于Julia编译器的当前状态,我认为您有两个选择(假设您不更改代码的逻辑)

第一种方法是保持代码中的内容不变,但将
count
更改为全局
const
。然而,当您想对它进行变异时,您必须引入一些包装器,一个自然的包装器是
Ref
。因此,您可以使用以下内容:

const count = Ref(0)
function numOfPaths(NominalSize,x,y)
    (x < NominalSize) && numOfPaths(NominalSize,x+1,y)
    (y < NominalSize) && numOfPaths(NominalSize,x,y+1)
    (x >= NominalSize && y>=NominalSize) && global (count[] +=1)
end
count[] = 0
@time numOfPaths(16,0,0)
count[]
作为一个参数,但它会慢一点

最后(这是一个比第一个快一点的选项,建议使用),您可以执行
count
的累加,而无需将其作为参数传递(这次它将只是一个整数):

希望将来能解决这个问题(这是一个已知的问题)


编辑2

首先让我评论一下,如果您想让这段代码运行得更快,应该怎么做。在这种情况下,可以使用动态规划代替递归,例如:

function numOfPaths(NominalSize, x, y)
    y, x = minmax(x, y)
    a = ones(BigInt, NominalSize + 1 - y)
    for i in 2:NominalSize - x + 1
        i > length(a) || (a[i] = 2 * a[i])
        for j in i+1:length(a)
            a[j] += a[j-1]
        end
    end
    return a[end]
end
(请注意,我在这里使用的是
BigInt
,与此代码一样,您可以测试远大于
Int64
类型范围的值;如果您想更快,只需在代码中将
BigInt
切换到
Int
,但对于
NominalSize
大于
33
的值,您将得到溢出)


现在,转到@Alex Zh的问题,为什么第二个代码运行缓慢。您可以在其上运行
@code\u warntype
,了解以下内容:

julia> function numOfPaths(NominalSize,x,y)
          (x < NominalSize) && numOfPaths(NominalSize,x+1,y)
          (y < NominalSize) && numOfPaths(NominalSize,x,y+1)
          ((y >= NominalSize) && (x >= NominalSize)) && (ref[] += 1 ;)
       end
numOfPaths (generic function with 1 method)

julia> count = 0;

julia> ref = Ref(count)
Base.RefValue{Int64}(0)

julia> @code_warntype numOfPaths(15,0,0)
Variables
  #self#::Core.Compiler.Const(numOfPaths, false)
  NominalSize::Int64
  x::Int64
  y::Int64

Body::Any
1 ─ %1  = (x < NominalSize)::Bool
└──       goto #3 if not %1
2 ─ %3  = (x + 1)::Int64
│         Main.numOfPaths(NominalSize, %3, y)
└──       goto #3
3 ┄ %6  = (y < NominalSize)::Bool
└──       goto #5 if not %6
4 ─ %8  = (y + 1)::Int64
│         Main.numOfPaths(NominalSize, x, %8)
└──       goto #5
5 ┄ %11 = (y >= NominalSize)::Bool
└──       goto #9 if not %11
6 ─ %13 = (x >= NominalSize)::Bool
└──       goto #8 if not %13
7 ─ %15 = Base.getindex(Main.ref)::Any
│   %16 = (%15 + 1)::Any
│         Base.setindex!(Main.ref, %16)
└──       return %16
8 ─       return false
9 ─       return false
您可以看到,当Julia从
Main
获取
ref
时,它不知道它的类型,因为它是一个全局变量,不是常量。因此,编译器无法生成有效的机器代码,因为必须在运行时(而不是在编译时)解析
ref
的类型

现在考虑下面的更改(您需要重新启动朱丽亚来测试它):


由于这一次编译器拥有它所需的所有类型信息,因此可以生成效率更高的机器代码。

我发现一个使用缓存的Pkg,20 NominalSize的运行时为0.0002673[秒] 对于相同的大小,在没有缓存的情况下执行此操作大约需要20[分钟]

因此,它的博古米卡米斯基答案+回忆录

using Memoization
@memoize function numOfPaths(NominalSize,x,y)
   count = 0
   x < NominalSize && (count += numOfPaths(NominalSize,x+1,y))
   y < NominalSize && (count += numOfPaths(NominalSize,x,y+1))
   x >= NominalSize && y>=NominalSize && (count+=1)
   return count
end
t = @elapsed paths = numOfPaths(20,0,0)
使用记忆
@记忆函数NUMOFPATHES(NominalSize,x,y)
计数=0
x=NominalSize&&y>=NominalSize&&(计数+=1)
返回计数
结束
t=@appeated path=numofpath(20,0,0)

last option是last Matt B的最佳选项。表示计数的累积(在我对建议的内容添加注释后,它不是last)。我同意最后一个选择是最好的。特别是选项1不是线程安全的,但我已经展示了它,因为原始代码使用了globals。这真的很有帮助,我找了几个小时的答案,用了另一种方法,但速度较慢,请解释一下原因。谢谢你最初的回答!我在函数count=0后使用;ref=ref(计数);后来改变了函数中ref的值,但它和我显示的全局变化一样慢,为什么你的Ref版本要快得多?你能用
Ref
在你的问题中发布你的解决方案吗?然后我可以试着解释为什么速度慢,在我的回答中,我也会试着解释你如何在实践中诊断这些事情。当然,那将是令人惊讶的,我正在上大学做C的课程,我很喜欢它,但是用Julia编写算法要容易得多。我马上就要发帖了——我想这是一个关于语言设计的问题。如果你想改进算法,你可以使用动态规划。我将在我的回答中添加这样一个解决方案的示例。
julia> @code_warntype numOfPaths(16, 0, 0)
Variables
  #self#::Core.Compiler.Const(numOfPaths, false)
  NominalSize::Int64
  x::Int64
  y::Int64
  f@_5::Core.Box
  count@_6::Core.Box
  f@_7::Union{}
  f@_8::Union{}
  count@_9::Union{}

Body::Any
1 ─       (f@_5 = Core.Box())
│         (count@_6 = Core.Box())
│   %3  = Main.:(var"#f#3")::Core.Compiler.Const(var"#f#3", false)
│   %4  = Core.typeof(NominalSize)::Core.Compiler.Const(Int64, false)
│   %5  = Core.apply_type(%3, %4)::Core.Compiler.Const(var"#f#3"{Int64}, false)
│   %6  = f@_5::Core.Box
│   %7  = %new(%5, NominalSize, %6, count@_6)::var"#f#3"{Int64}
│         Core.setfield!(f@_5, :contents, %7)
│         Core.setfield!(count@_6, :contents, 0)
│   %10 = Core.isdefined(f@_5, :contents)::Bool
└──       goto #3 if not %10
2 ─       goto #4
3 ─       Core.NewvarNode(:(f@_8))
└──       f@_8
4 ┄ %15 = Core.getfield(f@_5, :contents)::Any
│         (%15)(x, y)
│   %17 = Core.isdefined(count@_6, :contents)::Bool
└──       goto #6 if not %17
5 ─       goto #7
6 ─       Core.NewvarNode(:(count@_9))
└──       count@_9
7 ┄ %22 = Core.getfield(count@_6, :contents)::Any
└──       return %22
function numOfPaths(NominalSize, x, y)
    y, x = minmax(x, y)
    a = ones(BigInt, NominalSize + 1 - y)
    for i in 2:NominalSize - x + 1
        i > length(a) || (a[i] = 2 * a[i])
        for j in i+1:length(a)
            a[j] += a[j-1]
        end
    end
    return a[end]
end
julia> function numOfPaths(NominalSize,x,y)
          (x < NominalSize) && numOfPaths(NominalSize,x+1,y)
          (y < NominalSize) && numOfPaths(NominalSize,x,y+1)
          ((y >= NominalSize) && (x >= NominalSize)) && (ref[] += 1 ;)
       end
numOfPaths (generic function with 1 method)

julia> count = 0;

julia> ref = Ref(count)
Base.RefValue{Int64}(0)

julia> @code_warntype numOfPaths(15,0,0)
Variables
  #self#::Core.Compiler.Const(numOfPaths, false)
  NominalSize::Int64
  x::Int64
  y::Int64

Body::Any
1 ─ %1  = (x < NominalSize)::Bool
└──       goto #3 if not %1
2 ─ %3  = (x + 1)::Int64
│         Main.numOfPaths(NominalSize, %3, y)
└──       goto #3
3 ┄ %6  = (y < NominalSize)::Bool
└──       goto #5 if not %6
4 ─ %8  = (y + 1)::Int64
│         Main.numOfPaths(NominalSize, x, %8)
└──       goto #5
5 ┄ %11 = (y >= NominalSize)::Bool
└──       goto #9 if not %11
6 ─ %13 = (x >= NominalSize)::Bool
└──       goto #8 if not %13
7 ─ %15 = Base.getindex(Main.ref)::Any
│   %16 = (%15 + 1)::Any
│         Base.setindex!(Main.ref, %16)
└──       return %16
8 ─       return false
9 ─       return false
%15 = Base.getindex(Main.ref)::Any
ulia> function numOfPaths(NominalSize,x,y)
          (x < NominalSize) && numOfPaths(NominalSize,x+1,y)
          (y < NominalSize) && numOfPaths(NominalSize,x,y+1)
          ((y >= NominalSize) && (x >= NominalSize)) && (ref[] += 1 ;)
       end
numOfPaths (generic function with 1 method)

julia> count = 0;

julia> const ref = Ref(count)
Base.RefValue{Int64}(0)

julia> @code_warntype numOfPaths(15,0,0)
Variables
  #self#::Core.Compiler.Const(numOfPaths, false)
  NominalSize::Int64
  x::Int64
  y::Int64

Body::Union{Bool, Int64}
1 ─ %1  = (x < NominalSize)::Bool
└──       goto #3 if not %1
2 ─ %3  = (x + 1)::Int64
│         Main.numOfPaths(NominalSize, %3, y)
└──       goto #3
3 ┄ %6  = (y < NominalSize)::Bool
└──       goto #5 if not %6
4 ─ %8  = (y + 1)::Int64
│         Main.numOfPaths(NominalSize, x, %8)
└──       goto #5
5 ┄ %11 = (y >= NominalSize)::Bool
└──       goto #9 if not %11
6 ─ %13 = (x >= NominalSize)::Bool
└──       goto #8 if not %13
7 ─ %15 = Base.getindex(Main.ref)::Int64
│   %16 = (%15 + 1)::Int64
│         Base.setindex!(Main.ref, %16)
└──       return %16
8 ─       return false
9 ─       return false
7 ─ %15 = Base.getindex(Main.ref)::Int64
using Memoization
@memoize function numOfPaths(NominalSize,x,y)
   count = 0
   x < NominalSize && (count += numOfPaths(NominalSize,x+1,y))
   y < NominalSize && (count += numOfPaths(NominalSize,x,y+1))
   x >= NominalSize && y>=NominalSize && (count+=1)
   return count
end
t = @elapsed paths = numOfPaths(20,0,0)