I created the fastest way to find divisors of a number

I have verified that it is faster than the two fastest ways to find divisors of a number: searching to the root and decomposing the number into prime factors and then enumerating them.

How it works:

  1. Factors a number into prime factors

  2. Goes through the list of prime factors (i) and a list of all known divisors of the number (j):

2.1. If (prime factor with index i) * (known divisor with index j) is not found in the list of known divisors of a number, then this value is not added to the list (so that the loop does not go through duplicate values ​​each time)

2.2. If a prime factor with an index i is missing, then it is added

  1. Adds one to the end of the list of divisors of a number

  2. Returns a sorted list

Implementation (with the above methods for finding divisors of a number):

import time
from math import *
import itertools

def mydiv(n): # мой способ поиска делителей
    divs = [] # все делители
    prdivs = [] # простые делители
    nownum = n # текущее число, увидите его значение в разложении
    isPrime = False # в случае, если делителей до корня не нашли, isPrime = True

    # разложение на простые множители
    while isPrime == False:
        isPrime = True
        for i in itertools.chain([2], range(3, int(nownum ** 0.5) + 1, 2)):
            if nownum % i == 0:
                prdivs.append(i)
                isPrime = False
                nownum //= i
                break
    prdivs.append(nownum)

    for i in range(len(prdivs)):
        for j in range(len(divs)):
            if divs[j] * prdivs[i] not in divs:
                divs.append(divs[j] * prdivs[i])

        if prdivs[i] not in prdivs[0:i]:
            divs.append(prdivs[i])

    divs.append(1)
    return sorted(set(divs)) # set() нужен, потому что по непонятной мне причине на степенях двойки появляется лишняя единица

def sqrtdiv(n): # способ поиска делителей до корня
    divs = []
    for i in range(1, int(n ** 0.5) + 1):
        if n % i == 0:
            divs.append(i)
            divs.append(n // i)

    return sorted(divs)

def prchoosediv(n): # способ поиска делителей разложением числа на простые множители и их перебором
    divs = []
    prdivs = []
    nownum = n
    isPrime = False

    while isPrime == False:
        isPrime = True
        for i in itertools.chain([2], range(3, int(nownum ** 0.5) + 1, 2)):
            if nownum % i == 0:
                prdivs.append(i)
                isPrime = False
                nownum //= i
                break

    prdivs.append(nownum)

    # здесь я решил использовать бинарную логику
    num = 1
    for i in range(2 ** len(prdivs) - 1):
        whattomult = bin(num)[2:] # 0b в начале нам не нужно
        whattomult = "0" * (len(prdivs) - len(whattomult)) + whattomult # вставляем ноли столько раз, чтобы длина строки = длина prdivs

        mult = 1
        for j in range(len(whattomult)):
            if whattomult[j] == "1":
                mult *= prdivs[j]

        if mult not in divs:
            divs.append(mult)

        num += 1

    divs.append(1)
    return sorted(divs)

Before testing, I’ll note that the execution speed mydiv() And prchoosediv() not proportional nand the number of prime divisors n. And with simple n all these functions will be performed at the same time.

Tests:

n = int(input())

start = time.time()
mydiv(n)
end = time.time()
print(f"mydiv: {end - start}")

start = time.time()
sqrtdiv(n)
end = time.time()
print(f"sqrtdiv: {end - start}")

start = time.time()
prchoosediv(n)
end = time.time()
print(f"prchoosediv: {end - start}")

For n = 360:

mydiv: 0.0
sqrtdiv: 0.0
prchoosediv: 0.0

For n = 1000000:

mydiv: 0.0
sqrtdiv: 0.0
prchoosediv: 0.004986286163330078

For n = 10^10:

mydiv: 0.0
sqrtdiv: 0.00897526741027832
prchoosediv: 2.245990514755249

From here sqrtdiv() And prchoosediv() I don't check.

For n = 10^15:

mydiv: 0.0019936561584472656

For n = 10^50:

mydiv: 0.4697425365447998

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *