Перебор чисел ‹n, определение разложения каждого числа на простые множители

Я хочу перебрать каждое число ‹N и знать разложение на простые множители каждого числа. У меня вопрос, как лучше всего это сделать?

Я знаю, что могу использовать метод пробного деления, чтобы найти разложение на простые множители данного числа, и просто повторить это для каждого числа меньше N, но это неэффективно и занимает больше времени, чем создание каждого числа из известных простых множителей. . Я написал ниже реализацию генерации каждого числа, меньшего N, из всех простых множителей, меньших N. Есть ли более быстрый способ сделать это? Я пытаюсь использовать тот факт, что, поскольку я делаю это для всех чисел меньше N, чтобы сэкономить время вычислений, вместо этого применяю метод пробного деления.

Цель, которую я пытаюсь достичь: у меня есть алгоритм, который я хочу запускать для каждого числа, меньшего N. Для этого алгоритма мне нужна факторизация на простые множители каждого числа. Я пытаюсь получить факторизацию каждого числа на простые множители за минимальное время. На самом деле мне не нужно хранить простые факторизации, мне просто нужно запустить мой алгоритм с простой факторизацией. (Алгоритм решения (curNum, curFactors) в коде)

Я написал программу на python3, которая рекурсивно генерирует каждое число со знанием его простых множителей, но это довольно медленно. (Время обработки составляет ~ 58 секунд при N = 10 ^ 7. Функция решения ничего не делает для этого теста.)

curFactors - это список, в котором каждый четный элемент является индексом простого числа в факторизации, а каждый нечетный элемент является показателем простого числа. Я выровнял его из списка списков, чтобы сэкономить время вычислений. Простой начальный индекс используется для того, чтобы я не пересчитывал числа дважды. В настоящее время Solve ничего не делает, поэтому я могу протестировать эту функцию.

def iterateThroughNumbersKnowingFactors(curNumber, curFactors, primeStartIndex):
    #Generate next set of numbers
    #Handle multiplying by a prime already in the factorization seperately.
    for i in range(primeStartIndex+1,lenPrimes):
        newNum = curNumber * primelist[i]
        if(newNum > upperbound):
            break
        newFactors = curFactors[:]
        newFactors.append(i)
        newFactors.append(1)
        #Do something with this number and its factors
        solve(newNum, newFactors)
        #Go get more numbers
        iterateThroughNumbersKnowingFactors(newNum,newFactors,i)
    if(primeStartIndex > -1):
        newNum = curNumber * primelist[primeStartIndex]
        if(newNum > upperbound):
            return
        currentNumPrimes = len(curFactors)
        curFactors[currentNumPrimes-1] += 1
        #Do something with this number and its factors
        solve(newNum, curFactors)
        #Go get more numbers
        iterateThroughNumbersKnowingFactors(newNum,curFactors,primeStartIndex)

upperbound = 10**7

#https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n
primelist = primesfrom2to(upperbound+1)
lenPrimes = len(primelist)

t0 = time.clock()
iterateThroughNumbersKnowingFactors(1,[],-1)
print(str(time.clock() - t0) +" seconds process time")

Кто-нибудь знает, как это сделать лучше?


person Ninja_Coder    schedule 21.01.2017    source источник
comment
Можете ли вы добавить более четкое объяснение того, чего именно вы пытаетесь достичь? Вы хотите сгенерировать список списков целых чисел, которые содержат факторизацию на простые множители значения индекса этого списка до некоторого предела?   -  person Sean Pianka    schedule 21.01.2017
comment
Приношу свои извинения за непонятность. У меня есть алгоритм, который я хочу запустить для каждого числа, меньшего N. Для этого алгоритма мне нужно разложить каждое число на простые множители. Мой вопрос в том, каков самый быстрый способ получить разложение на простые множители каждого числа, меньшего N. Мне на самом деле не нужно хранить разложения на простые множители в памяти, мне просто нужно запустить алгоритм с разложением на простые множители.   -  person Ninja_Coder    schedule 21.01.2017
comment
Так, например: primes = []; for i in range(upper_bound): primes.append(prime_factorization(i)) - это, по сути, то, что вы пытаетесь сделать? Итак, вы ищете оптимизированную реализацию Python факторизации простых чисел?   -  person Sean Pianka    schedule 21.01.2017
comment
Да! Я пытаюсь найти более оптимизированный способ сделать это. Ответ на любом языке - это здорово, я могу преобразовать его в Python. На самом деле я не знаю более оптимизированного способа, чем то, что я делаю.   -  person Ninja_Coder    schedule 21.01.2017
comment
Возможный дубликат Python для определения основных факторов   -  person Sean Pianka    schedule 21.01.2017
comment
Я пытаюсь использовать тот факт, что нахожу факторизацию на простые множители всех чисел меньше N, чтобы сэкономить время вычислений. Следовательно, создание каждого числа путем объединения простых чисел экономит больше времени, чем просто выполнение метода пробного деления по всем числам. Использование метода пробного деления, который вы предлагаете по всем числам ‹10 ** 7, занимает гораздо больше времени.   -  person Ninja_Coder    schedule 21.01.2017
comment
Вам нужно перебрать все числа по порядку?   -  person rici    schedule 22.01.2017
comment
Нет, порядок не имеет значения!   -  person Ninja_Coder    schedule 22.01.2017


Ответы (3)


Если у вас уже реализовано сито Эратосфена и его производительность приемлема, вы можете изменить его, чтобы сохранить простые множители.

Основной подход заключается в следующем: всякий раз, когда вы «вычеркиваете» число или удаляете его из списка как кратное простому, вместо этого проверяйте, сколько раз вы можете разделить его на простое число без остатка (используйте / и %). Это даст вам пару (простое число, показатель степени), представляющую этот компонент простой факторизации. Сохраните эти пары в списке или словаре, связанном с исходным номером. Когда сито завершится, каждый список будет описывать разложение на простые множители соответствующего числа.

person hnau    schedule 21.01.2017
comment
Моя ошибка, спасибо, что указали на это. Я отредактировал ответ, чтобы исправить это. Я думаю, что описанный мной подход требует небрежной реализации N log N. - person hnau; 22.01.2017
comment
Не нужно делить; перебирать две переменные во внутреннем цикле сита. Один увеличивается на штрих, другой - на единицу. - person Yann Vernier; 22.01.2017

Если вы хотите немного развлечься, вы можете использовать алгоритм Баха для генерации случайного число в интервале [1,N] в O(log(N)) раз. Повторение этого до тех пор, пока вы не найдете разложение на простые множители всех чисел меньше N, теоретически может занять бесконечное время, но ожидаемое время работы алгоритма будет O(log^2(n)).

Это может быть немного с точки зрения эффективности потерь, но если вам нужен забавный алгоритм, который не повторяется в линейном порядке, то это может быть вашим лучшим выбором :)

person Milo Moses    schedule 13.01.2021

Вдохновленный ситом Эратосфена, вы можете составить список факторов, распространив простые числа на список списков простых множителей вплоть до N:

Чтобы знать только, какие простые числа присутствуют:

def primeFactors(N):
    result = [[] for _ in range(N+1)]  # lists of factors from 0..N
    for p in range(1,N+1,2):
        if p<2: p=2 
        if result[p]: continue         # empty list is a prime 
        for k in range(p,len(result),p):
                result[k].append(p)    # propagate it to all multiples
    return result

print(*enumerate(primeFactors(10)))
# (0, []) (1, []) (2, [2]) (3, [3]) (4, [2]) (5, [5]) (6, [2, 3]) (7, [7]) (8, [2]) (9, [3]) (10, [2, 5])

Чтобы получить каждый экземпляр каждого простого числа в факторизации:

def primeFactorsAll(N):
    result = [[] for _ in range(N+1)]
    for p in range(1,N+1,2):
        if p<2: p=2
        if result[p]: continue
        pn = p
        while pn < N:
            for k in range(pn,len(result),pn): # propagate to multiples of
                result[k].append(p)            # each power of p
            pn *= p
    return result

print(*enumerate(primeFactorsAll(10)))
# (0, []) (1, []) (2, [2]) (3, [3]) (4, [2, 2]) (5, [5]) (6, [2, 3]) (7, [7]) (8, [2, 2, 2]) (9, [3, 3]) (10, [2, 5])

Для большого N это должно работать намного быстрее, чем подход с делением. Для N = 10 ^ 7 на моем ноутбуке primeFactors (N) занимает 8,1 секунды, а primeFactorsAll (N) - 9,7 секунды.

person Alain T.    schedule 14.01.2021