有没有办法优化此代码以查找数字的除数?

Posted

技术标签:

【中文标题】有没有办法优化此代码以查找数字的除数?【英文标题】: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 的功率很大时。

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

【问题讨论】:

【参考方案1】:

我可以指出几件事-

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

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

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)

【讨论】:

我相信使用VectorBool 比使用BitVector 更快。后者节省了内存,并且对于一些分块操作和缩减来说可能很快,但处理单个元素的速度较慢。将dsievepsieve 实例化为fill(true, n) 感谢您提供有用的建议,Miles Lucas 和 @DNF。我对 Julia 编程还是很陌生,所以我不熟悉大多数内置函数和优化。实施这些建议后,执行时间大大减少。

以上是关于有没有办法优化此代码以查找数字的除数?的主要内容,如果未能解决你的问题,请参考以下文章

如何优化此代码以运行更大的值? [复制]

查找数字除数的总数,用于python中的t测试用例

如何改进此算法以优化运行时间(在段中查找点)

Eclipse 优化导入以包括静态导入

如何优化此替换列和索引的代码?

如何优化此代码以仅显示圆形数组的轮廓?