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