-1

I need to find all numbers in range (45 000 000, 50 000 000 +1), which have five odd dividers only. The number of even dividers has no limit. Here my code is too slow

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)
Max Sh
  • 109
  • 6
  • 1
    If you have working code and you're looking for improvements, consider to post your question on https://codereview.stackexchange.com/ instead. – Mike Scotty Mar 17 '21 at 15:50
  • 1
    For starters, if you don't care about even dividers, why even bother looking at `j`s that are even (`range(..., ..., 2)`)? That'll speed up that bit by half... – AKX Mar 17 '21 at 15:53
  • I suggest creating a separate `divisors()` function to return all the devisors of a number. Not only is this a good programming practice, it will help you think of ways to improve your algorithm. For example, you currently check all numbers from `1` to `sqrt(i)` to see if they divide `i`. Can you think of any ways to reduce which numbers you check even more? – Code-Apprentice Mar 17 '21 at 15:54
  • 1
    If I use step 2 I will lost odd dividers above sqrt (n) – Max Sh Mar 17 '21 at 15:57
  • After the above the next step might be a sieve, i.e. you keep note of the numbers that have 5 odd divisors and any number that has one of these as a divisor already has 5 and you don't need to assess it any further. – Ben Mar 17 '21 at 15:59
  • I check it "if counter >5: break" – Max Sh Mar 17 '21 at 16:01

1 Answers1

2

Take an odd number n. If this odd number n has an odd divisor m, then n/m is also an odd number. But that means that odd divisors come in pairs, m and n/m. The only way the number n can have 5 odd divisors is if some pair actually has the same numbers, i.e. m = n/m. But that means that n = m*m, i.e. n must be a square of an odd number. So, you don't need to check all odd numbers in the range, just check odd numbers which are squares of an odd number.

Now consider an even number n. Let's divide this number by 2 until we get an odd number m (for example, from n=40 we get 40 -> 20 -> 10 -> 5, so m = 5). Any odd divisor of m will be also an odd divisor of n and vice versa. So, n and m have the same number of odd divisors. So, as we see above, number m must be a square of an odd number. It means, that n must be a square of an odd number multiplied by some power of two. It means that you don't need to check every even number in your range, check only those, which are squares of an odd number multiplied by some power of 2.

UPDATE

Below are sample implementations:

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))

The first method1 uses the approach described above. The second method2 uses a more advanced approach and is about 100 times faster. The idea of the second method is to generate all prime numbers between 3 and the fourth root of the upper bound of the interval (50000000 in our case, so prime numbers must not be larger than 84). Then we make 4th power of all these prime numbers and try to multiply them by 2 until we get inside the required interval. Both methods produce the same output

[45212176, 45265984, 47458321, 48469444]

Below is a long proof that method 2 works:

To understand how method 2 works we need to continue our analysis for method 1. We know, that in order to have 5 divisors our number n must be equal to a square of an odd number multiplied by a power of two:

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

where m is some odd number. The number m*m must have exactly 5 odd divisors. However, we already know 3 of them: 1, m, and m*m. So, there should exist just 2 more odd divisors of m*m, only one of them will be between 1 and m. Let's denote the first odd divisor as p (it should be between 1 and m). Then the second odd divisor is m*m/p. The number p must be prime. If it is not, then any divisor of p will be also a divisor of m*m. We know that the second divisor is m*m/p, so m*m must be divisible by p. But because p is prime m should also be divisible by p: m = p*x, where x is some (odd) number. But if x is not equal to p, then we have found another number x between 1 and m which is a divisor of m*m. This is not possible, so x must be equal to p and m = p*p. So, we have proven, that m*m = p^4, i.e. our number n must be the fourth power of a prime number multiplied by some power of 2.

Alex Sveshnikov
  • 4,214
  • 1
  • 10
  • 26
  • Please explain me part two (even number). Why I have to consider only squares of an odd number multiplied by some power of 2? Because power of 2 has not any odd devisor? – Max Sh Mar 17 '21 at 16:34
  • Yes, power of 2 does not have any odd divisors, so `n` and `n` divided by power of 2 have the same number of odd divisors. – Alex Sveshnikov Mar 17 '21 at 16:46
  • Actually, you can prove a much stronger statement - that the number `n` must be equal to the fourth power of a primer number multiplied by some power of 2. Since the fourth root of 50000000 is approximately 84, you only need to check prime numbers which are not larger than 84. – Alex Sveshnikov Mar 17 '21 at 17:28