如何加速我的代码:只找到五个奇数除法器偶数分隔符的数量没有限制

问题描述

我需要找到范围 (45 000 000,50 000 000 +1) 中的所有数字,这些数字只有五个奇数除法器。偶数分隔符的数量没有限制。这里我的代码太慢了

listN=[]
for i in range (45000000,50000000+1):
    sqrtI=i**0.5
    counter=0
    sqrtI=int(sqrtI)
        
    for j in range (1,sqrtI+1):
        divj,modj=divmod(i,j)
        if modj==0:
            if j%2==1:
                counter+=1
            if divj%2==1:
                counter+=1
        if counter >5:
            break
    if counter==5:
        listN.append(i)
    
        
    if i%100000==0: # 
        print(i)
        
print(listN)

解决方法

取一个奇数 n。如果这个奇数 n 有一个奇数除数 m,那么 n/m 也是一个奇数。但这意味着奇数除数成对出现,mn/m。数字 n 可以有 5 个奇数除数的唯一方法是,如果某对实际上具有相同的数字,即 m = n/m。但这意味着 n = m*m,即 n 必须是奇数的平方。因此,您不需要检查范围内的所有奇数,只需检查是奇数平方的奇数即可。

现在考虑偶数 n。让我们将这个数除以 2,直到我们得到一个奇数 m(例如,从 n=40 我们得到 40 -> 20 -> 10 -> 5,所以 m = 5)。 m 的任何奇数除数也将是 n 的奇数除数,反之亦然。因此,nm 具有相同数量的奇数除数。因此,正如我们在上面看到的,数字 m 必须是奇数的平方。这意味着,n 必须是奇数乘以 2 的某个幂的平方。这意味着您不需要检查范围内的每个偶数,只检查那些是奇数乘以 2 的幂的平方。

更新

以下是示例实现:

def method1(a,b):
    result = []
    for n in range(a,b + 1):
        m = n
        while (m > 1) and (m % 2 == 0):
            m /= 2  
  
        root = int(m ** 0.5 + 0.5)
        if root * root != m:
            continue    

        count = 0
        for i in range (3,root):
            if (i % 2 == 0) or (m % i != 0):
                continue
            count += 1
            if count > 1:
                break
    
        if count == 1:
            result.append(n)    
    return result

def generate_primes(start,end):
    result = []
    for n in range(start,end + 1):
        if n % 2 == 0:
             continue
        root = int(n ** 0.5 + 0.5)
        for i in range(3,root + 1):
             if n % i == 0:
                 break
        else:
            result.append(n)
    return result

def method2(a,b):
    result = []
    primes = generate_primes(3,int(b ** 0.25 + 0.5))   
    for p in primes:
        n = p**4
        while n < a:
            n *= 2
        while n <= b:
            result.append(n)
            n *= 2
    return sorted(result)
        
a = 45000000
b = 50000000

print(method1(a,b))
print(method2(a,b))

第一个 method1 使用上述方法。第二个 method2 使用更高级的方法,速度大约快 100 倍。第二种方法的想法是生成 3 到区间上限的四次根之间的所有素数(在我们的例子中是 50000000,所以素数不能大于 84)。然后我们对所有这些质数进行 4 次方,并尝试将它们乘以 2,直到我们进入所需的区间。两种方法产生相同的输出

[45212176,45265984,47458321,48469444]

以下是方法 2 有效的详细证明:

要了解方法 2 的工作原理,我们需要继续分析方法 1。我们知道,为了有 5 个除数,我们的数 n 必须等于奇数乘以幂的平方两个:

n = m*m * (some power of 2)

其中 m 是某个奇数。数字 m*m 必须正好有 5 个奇数除数。但是,我们已经知道其中的 3 个:1、mm*m。因此,m*m 的奇数除数应该只存在 2 个,其中只有一个在 1 和 m 之间。 让我们将第一个奇数除数表示为 p(它应该在 1 和 m 之间)。那么第二个奇数除数是 m*m/p。数 p 必须是质数。如果不是,则 p 的任何约数也是 m*m 的约数。我们知道第二个除数是 m*m/p,所以 m*m 必须能被 p 整除。但是因为 p 是质数 m 也应该被 p 整除:m = p*x,其中 x 是某个(奇数)数。但是如果 x 不等于 p,那么我们在 1 和 x 之间找到了另一个数 m,它是 m*m 的除数。这是不可能的,因此 x 必须等于 pm = p*p。因此,我们已经证明,m*m = p^4,即我们的数 n 必须是素数乘以 2 的某个幂的四次方。