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 变成现有的突变)。创建自定义 Int256Int512 类型并使用 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 很快 - 我很高兴我可以使用它。