【问题标题】:Is there a way to optimize this code for finding the divisors of a number?有没有办法优化此代码以查找数字的除数?
【发布时间】:2020-04-09 17:49:48
【问题描述】:

我在Julia 中编写了一个程序来有效地计算数字n 的除数。该算法是原创的(据我所知),大致基于Sieve of Eratosthenes。它基本上是这样工作的:

对于给定的素数p,让p^k || n;列表中的每个数字m 满足p^{k+1} | m 被删除,并重复此过程 每个素数p < n

使用传统的埃拉托色尼筛法在原位计算素数。

function ν(p, n)     #returns the smallest power of p that does not divide n
    q = 1

    for i = 0:n
        if mod(n, q) != 0
            return (i, q) 
        end

        q *= p
    end
end

function divisors(n)    #returns a list of divisors of n
    dsieve, psieve = BitArray([true for i = 1:n]), BitArray([true for i = 1:n])
    psieve[1] = false

    for i = 1:n
        if psieve[i] && dsieve[i]
            #sieving out the non-primes
            for j = i^2:i:n
                psieve[j] = false
            end

            #sieving out the non-divisors
            v = ν(i, n)[2]
            for j = v:v:n
                dsieve[j] = false
            end
        end
    end
    return dsieve #the code for converting this BitArray to an array of divisors has been omitted for clarity
end

虽然这工作得很好,但我发现同时使用两个筛子效率低下。我认为可以通过允许筛数组中的每个元素取三个不同的值(对应于uncheckeddivisornot divisor)来解决这个问题,但是这样就不能再实现为BitArray .

我也尝试过修改函数ν 以提高效率:

function ν₀(p, n)      #the same as ν, but implemented differently
    q = p
    while mod(n, q) == 0
        q = q^2
    end

    q = floor(Int64, √q)
    q < p ? 1 : q * ν₀(p, n÷q)    #change 1 to p to get the smallest power of p that does not divide n
end

虽然这更复杂,但它比之前的算法快一点——尤其是当p 除以n 的功率很大时。

注意:我知道有更好的算法可以找到数字的除数。我只是想知道上述算法可以优化到什么程度。如前所述,使用两个筛子相当麻烦,如果能找到一种方法,在不影响效率的情况下消除传统的素数筛子,那就太好了。

【问题讨论】:

    标签: algorithm julia integer-division sieve-of-eratosthenes number-theory


    【解决方案1】:

    我可以指出几件事-

    dsieve, psieve = BitArray([true for i = 1:n]), BitArray([true for i = 1:n])
    

    为每个数组分配两次(列表组合,然后是转换)。这样就可以了:(编辑:@DNF 在这里指出了Vector{Bool} 的优越性)

    dsieve = fill(true, n)
    psieve = fill(true, n)
    

    接下来,我们可以确保通过使用任何更快的索引来利用

    for i in eachindex(psieve)
    

    而不是手动范围。然后你可以在for循环前面加上

    @inbounds for i in eachindex(psieve)
    

    或者更进一步,如果您使用的是 Julia 1.3 或更高版本,并对其进行多线程处理(假设您在运行之前设置了 JULIA_NUM_THREADS

    @inbounds Threads.@threads for i in eachindex(psieve)
    

    【讨论】:

    • 我相信使用Vector{Bool} 比使用BitVector 更快。后者节省了内存,并且对于一些分块操作和缩减来说可能很快,但处理单个元素的速度较慢。将dsievepsieve 实例化为fill(true, n)
    • 感谢您提供有用的建议,Miles Lucas 和 @DNF。我对 Julia 编程还是很陌生,所以我不熟悉大多数内置函数和优化。实施这些建议后,执行时间大大减少。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-16
    • 1970-01-01
    • 2013-04-06
    • 2017-05-23
    • 1970-01-01
    • 1970-01-01
    • 2019-11-26
    相关资源
    最近更新 更多