Fuss-Catalan 数字的 Julia 记忆代码
Julia memoized code for Fuss-Catalan numbers
来自 Fuss-Catalan 系列 C{4}_n(请参阅整数序列在线百科全书 OEIS A002293),我想使用记忆计算第 n 项。我在下面编写的代码有效,但在我的笔记本电脑上当 n=200 时大约需要 43 秒。有没有办法进一步加快速度?
numterms = 20
C4 = Array{BigInt}(numterms+1) # memoization dictionary
fill!(C4,-1) # -1 implies not yet computed
C4[1] = 1 # Base case for n=0, C[n+1] provides nth term
function catalan4(n,C)
C[n+1] == -1 || return C[n+1]
sum1 = convert(BigInt,0)
for i in 1:n
sum2 = convert(BigInt,0)
for j in 1:(n-i+1)
sum3 = convert(BigInt,0)
for k in 1:(n-i-j+2)
sum3+= catalan4(k-1,C)*catalan4(n-i-j-k+2,C)
end
sum2 += catalan4(j-1,C)*sum3
end
sum1 += catalan4(i-1,C)*sum2
end
C[n+1] = sum1
return sum1
end
for i in 1:numterms
println(i,"\t",catalan4(i,C4))
end
这按预期提供:
1 1
2 4
3 22
4 140
5 969
6 7084
7 53820
8 420732
9 3362260
10 27343888
11 225568798
12 1882933364
13 15875338990
14 134993766600
15 1156393243320
16 9969937491420
17 86445222719724
18 753310723010608
19 6594154339031800
20 57956002331347120
谢谢!
我怀疑糟糕的 BigInt
性能是罪魁祸首。您可能想尝试使用 Nemo, which uses the Flint library 来表示任意精度的整数,据我所知,这样效率要高得多。如果你想留在标准的 Julia 中(Nemo 是基于 Julia 的,但在某些语言语义上与 Julia 不同,afaik - 它是一个计算机代数系统)并且你的数字被限制为小于 2^127,那么你可以尝试使用Int128
相反——这些可以堆栈分配并保存在寄存器中,不像 BigInts
,它必须是堆分配的,LLVM 不知道如何推理(它不能转换创建新的 BigInts
变成现有的突变)。创建自定义 Int256
和 Int512
类型并使用 two/four Int128
值来进行运算可能并不难,尤其是当您只需要支持加法和乘法时。
因此,在 Stefan 的上述回答之后 - 我尝试了几件事。为了查看 cuplrit 是否真的是 Julia 的 BigInt,我在上述代码的 C 版本中尝试了 GMP mpz_t 整数。事实上,当 n=200 时,速度快了大约 10 倍。但是,有时我需要 n=4096,而 GMP 和 C 并不能真正帮助我减少计算时间。然后我注意到内部循环也被反复重新计算。所以我按如下方式记住了两个内部总和,现在代码为我提供了 n=200,Julia 和 BigInts 的评估时间为 0.06 秒(从 43 秒下降)!
numterms = 200
C4 = Array{BigInt}(numterms+1) # memoization dictionary
fill!(C4,-1) # -1 implies not yet computed
C4[1] = 1 # Base case for n=0, C[n+1] provides nth term
# memoize partial sums as well
sum2store = similar(C4)
sum3store = similar(C4)
fill!(sum2store, -1)
fill!(sum3store, -1)
function catalan4(n,C)
C[n+1] == -1 || return C[n+1]
sum1 = convert(BigInt,0)
for i in 1:n
if sum2store[n-i+1] == -1
sum2 = convert(BigInt,0)
for j in 1:(n-i+1)
if sum3store[n-i-j+2] == -1
sum3 = convert(BigInt,0)
for k in 1:(n-i-j+2)
sum3+= catalan4(k-1,C)*catalan4(n-i-j-k+2,C)
end
sum3store[n-i-j+2] = sum3
end
sum2 += catalan4(j-1,C)*sum3store[n-i-j+2]
end
sum2store[n-i+1] = sum2
end
sum1 += catalan4(i-1,C)*sum2store[n-i+1]
end
C[n+1] = sum1
return sum1
end
现在我不需要 Julia 的 BigInt 很快 - 我很高兴我可以使用它。
来自 Fuss-Catalan 系列 C{4}_n(请参阅整数序列在线百科全书 OEIS A002293),我想使用记忆计算第 n 项。我在下面编写的代码有效,但在我的笔记本电脑上当 n=200 时大约需要 43 秒。有没有办法进一步加快速度?
numterms = 20
C4 = Array{BigInt}(numterms+1) # memoization dictionary
fill!(C4,-1) # -1 implies not yet computed
C4[1] = 1 # Base case for n=0, C[n+1] provides nth term
function catalan4(n,C)
C[n+1] == -1 || return C[n+1]
sum1 = convert(BigInt,0)
for i in 1:n
sum2 = convert(BigInt,0)
for j in 1:(n-i+1)
sum3 = convert(BigInt,0)
for k in 1:(n-i-j+2)
sum3+= catalan4(k-1,C)*catalan4(n-i-j-k+2,C)
end
sum2 += catalan4(j-1,C)*sum3
end
sum1 += catalan4(i-1,C)*sum2
end
C[n+1] = sum1
return sum1
end
for i in 1:numterms
println(i,"\t",catalan4(i,C4))
end
这按预期提供:
1 1
2 4
3 22
4 140
5 969
6 7084
7 53820
8 420732
9 3362260
10 27343888
11 225568798
12 1882933364
13 15875338990
14 134993766600
15 1156393243320
16 9969937491420
17 86445222719724
18 753310723010608
19 6594154339031800
20 57956002331347120
谢谢!
我怀疑糟糕的 BigInt
性能是罪魁祸首。您可能想尝试使用 Nemo, which uses the Flint library 来表示任意精度的整数,据我所知,这样效率要高得多。如果你想留在标准的 Julia 中(Nemo 是基于 Julia 的,但在某些语言语义上与 Julia 不同,afaik - 它是一个计算机代数系统)并且你的数字被限制为小于 2^127,那么你可以尝试使用Int128
相反——这些可以堆栈分配并保存在寄存器中,不像 BigInts
,它必须是堆分配的,LLVM 不知道如何推理(它不能转换创建新的 BigInts
变成现有的突变)。创建自定义 Int256
和 Int512
类型并使用 two/four Int128
值来进行运算可能并不难,尤其是当您只需要支持加法和乘法时。
因此,在 Stefan 的上述回答之后 - 我尝试了几件事。为了查看 cuplrit 是否真的是 Julia 的 BigInt,我在上述代码的 C 版本中尝试了 GMP mpz_t 整数。事实上,当 n=200 时,速度快了大约 10 倍。但是,有时我需要 n=4096,而 GMP 和 C 并不能真正帮助我减少计算时间。然后我注意到内部循环也被反复重新计算。所以我按如下方式记住了两个内部总和,现在代码为我提供了 n=200,Julia 和 BigInts 的评估时间为 0.06 秒(从 43 秒下降)!
numterms = 200
C4 = Array{BigInt}(numterms+1) # memoization dictionary
fill!(C4,-1) # -1 implies not yet computed
C4[1] = 1 # Base case for n=0, C[n+1] provides nth term
# memoize partial sums as well
sum2store = similar(C4)
sum3store = similar(C4)
fill!(sum2store, -1)
fill!(sum3store, -1)
function catalan4(n,C)
C[n+1] == -1 || return C[n+1]
sum1 = convert(BigInt,0)
for i in 1:n
if sum2store[n-i+1] == -1
sum2 = convert(BigInt,0)
for j in 1:(n-i+1)
if sum3store[n-i-j+2] == -1
sum3 = convert(BigInt,0)
for k in 1:(n-i-j+2)
sum3+= catalan4(k-1,C)*catalan4(n-i-j-k+2,C)
end
sum3store[n-i-j+2] = sum3
end
sum2 += catalan4(j-1,C)*sum3store[n-i-j+2]
end
sum2store[n-i+1] = sum2
end
sum1 += catalan4(i-1,C)*sum2store[n-i+1]
end
C[n+1] = sum1
return sum1
end
现在我不需要 Julia 的 BigInt 很快 - 我很高兴我可以使用它。