Как лучше всего получить все делители числа?

Вот самый тупой способ:

def divisorGenerator(n):
    for i in xrange(1,n/2+1):
        if n%i == 0: yield i
    yield n

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

Я могу найти простые множители и их кратность достаточно быстро. У меня есть генератор, который генерирует фактор таким образом:

(фактор1, кратность1)
(фактор2, кратность2)
(фактор3, кратность3)
и так далее...

то есть выход

for i in factorGenerator(100):
    print i

является:

(2, 2)
(5, 2)

Я не знаю, насколько это полезно для того, что я хочу делать (я закодировал его для других задач), в любом случае мне нужен более умный способ сделать

for i in divisorGen(100):
    print i

выведите это:

1
2
4
5
10
20
25
50
100

Обновлено: Огромное спасибо Грегу Хьюджиллу за его «умный путь» :) Вычисление всех делителей 100000000 заняло 0,01 с его путем по сравнению с 39 с, которые этот тупой способ взял на мою машину, очень круто: D

ОБНОВЛЕНИЕ 2: Хватит говорить, что это дубликат сообщения это. Для вычисления числа делителей данного числа не нужно вычислять все делители. Это другая проблема, если вы думаете, что это не так, то поищите "Функция делителя" в Википедии. Прочтите вопросы и ответ перед публикацией, если вы не понимаете, о чем идет речь, просто не добавляйте бесполезные и уже предоставленные ответы.

Причина, по которой было предложено, что этот вопрос почти дублирует «Алгоритм вычисления количества делителей заданного числа», заключалась в том, что предложенным первым шагом в этом вопросе было найти все делители, что, я считаю, именно то, что вы пытались сделать?

Andrew Edgecombe 06.10.2008 02:43

Эндрю, чтобы узнать, сколько есть делителей, вам просто нужно найти простые множители, а затем использовать их, чтобы подсчитать, сколько может быть делителей. В этом случае поиск делителей не требуется.

Loïc Faure-Lacroix 14.08.2011 03:25

@Andrea Ambu, исправьте, пожалуйста, названия функций

minerals 29.01.2017 14:56

Эй, ты читаешь это 12 лет спустя, тебе нужен sympy.divisors

baqyoteto 26.02.2021 21:07
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
113
4
157 989
16
Перейти к ответу Данный вопрос помечен как решенный

Ответы 16

Чтобы расширить то, что сказал Шими, вы должны запускать свой цикл только от 1 до квадратного корня из n. Затем, чтобы найти пару, выполните n / i, и это покроет все проблемное пространство.

Как было также отмечено, это NP, или «сложная» проблема. То, как вы делаете исчерпывающий поиск, почти так же хорошо, как и для гарантированных ответов. Этот факт используется алгоритмами шифрования и т.п. для их защиты. Если бы кто-то решил эту проблему, большая часть, если не все наши текущие «безопасные» коммуникации были бы небезопасными.

Код Python:

import math

def divisorGenerator(n):
    large_divisors = []
    for i in xrange(1, int(math.sqrt(n) + 1)):
        if n % i == 0:
            yield i
            if i*i != n:
                large_divisors.append(n / i)
    for divisor in reversed(large_divisors):
        yield divisor

print list(divisorGenerator(100))

Что должно вывести список вроде:

[1, 2, 4, 5, 10, 20, 25, 50, 100]

Потому что, если у вас есть список элементов от 1 до 10, вы можете легко сгенерировать любой из элементов от 11 до 100. Вы получите {1, 2, 4, 5, 10}. Разделите 100 на каждый из этих элементов, и вы {100, 50, 20, 25, 10}.

Matthew Scharley 05.10.2008 15:40

Факторы ВСЕГДА генерируются парами в соответствии с определением. Выполняя поиск только в sqrt (n), вы сокращаете свою работу в степени 2.

Matthew Scharley 05.10.2008 15:42

Это намного быстрее, чем версия в моем сообщении, но все же слишком медленнее, чем версия с простыми факторами.

Andrea Ambu 05.10.2008 16:24

Согласен, это не лучшее решение. Я просто указывал на «лучший» способ выполнить «тупой» поиск, который уже сэкономил бы много времени.

Matthew Scharley 05.10.2008 16:39

Факторизация не является NP-трудной. en.wikipedia.org/wiki/Integer_factorization И проблема заключалась в том, чтобы найти все делители, учитывая, что простые множители (сложная часть) уже были найдены.

Jamie 11.10.2008 04:46

Это означает, что факторизация - это NP-сложный алгоритм по расширению. Тот факт, что у них уже есть основные факторы, не имеет значения. Приведенный пример был исчерпывающим поиском, который медленный и трудный.

Matthew Scharley 11.10.2008 13:00

если i не n / i: должно быть, если i! = n / i:, для значений, квадратный корень которых больше 256 'is', работать не будет.

Abhishek 28.06.2014 12:51

для небольших чисел это намного быстрее, чем версия, использующая простые множители

Kukosk 08.01.2015 18:33
Ответ принят как подходящий

Учитывая вашу функцию factorGenerator, вот divisorGen, который должен работать:

def divisorGen(n):
    factors = list(factorGenerator(n))
    nfactors = len(factors)
    f = [0] * nfactors
    while True:
        yield reduce(lambda x, y: x*y, [factors[x][0]**f[x] for x in range(nfactors)], 1)
        i = 0
        while True:
            f[i] += 1
            if f[i] <= factors[i][1]:
                break
            f[i] = 0
            i += 1
            if i >= nfactors:
                return

Общая эффективность этого алгоритма будет полностью зависеть от эффективности factorGenerator.

вау потребовалось 0,01 для вычисления всех делителей 100000000 против 39, которые пошли глупым путем (остановившись на n / 2), очень круто, спасибо!

Andrea Ambu 05.10.2008 14:20

Для тех из нас, кто не понимает питонский диалект, что это на самом деле делает?

Matthew Scharley 07.10.2008 17:24

монооксид: это вычисляет все мультипликативные комбинации данных факторов. Большая часть этого должна быть понятной; строка "yield" похожа на возврат, но продолжается после возврата значения. [0] * nfactors создает список нулей длины nfactors. reduce (...) вычисляет произведение факторов.

Greg Hewgill 08.10.2008 02:33

@SpeckiniusFlecksis: Нет причин, operator.mul будет работать там одинаково хорошо.

Greg Hewgill 23.10.2012 22:46

Это, конечно, значительно лучше, чем деление на каждое число до n / 2 или даже на sqrt (n), но у этой конкретной реализации есть два недостатка: довольно безвредный: тонны умножения и возведения в степень, многократное умножение одних и тех же степеней и т. д. Выглядит Pythonic, но я не думаю, что Python убивает производительность. Проблема вторая: делители возвращаются не по порядку.

Tomasz Gandor 10.12.2014 17:37

Спасибо, Грег. Ваш алгоритм вдохновил мою более функциональную версию здесь: rosettacode.org/wiki/Proper_divisors#Python:_From_prime_fact‌ или

Paddy3118 16.12.2014 09:18

Мне нравится решение Грега, но хотелось бы, чтобы оно было больше похоже на питон. Я чувствую, что это будет быстрее и читабельнее; так что после некоторого времени кодирования я вышел с этим.

Первые две функции необходимы для создания декартова произведения списков. И может быть повторно использован всякий раз, когда возникает эта проблема. Кстати, мне пришлось самому программировать это, если кто-нибудь знает стандартное решение этой проблемы, пожалуйста, свяжитесь со мной.

«Факторгенератор» теперь возвращает словарь. Затем словарь загружается в «делители», которые используют его для создания сначала списка списков, где каждый список представляет собой список факторов в форме p ^ n с простым p. Затем мы производим декартово произведение этих списков и, наконец, используем решение Грега для создания делителя. Мы их сортируем и возвращаем.

Я протестировал его, и он кажется немного быстрее, чем предыдущая версия. Я тестировал его как часть более крупной программы, поэтому не могу точно сказать, насколько он быстрее.

Пьетро Сперони (расставил точки pietrosperoni)

from math import sqrt


##############################################################
### cartesian product of lists ##################################
##############################################################

def appendEs2Sequences(sequences,es):
    result=[]
    if not sequences:
        for e in es:
            result.append([e])
    else:
        for e in es:
            result+=[seq+[e] for seq in sequences]
    return result


def cartesianproduct(lists):
    """
    given a list of lists,
    returns all the possible combinations taking one element from each list
    The list does not have to be of equal length
    """
    return reduce(appendEs2Sequences,lists,[])

##############################################################
### prime factors of a natural ##################################
##############################################################

def primefactors(n):
    '''lists prime factors, from greatest to smallest'''  
    i = 2
    while i<=sqrt(n):
        if n%i==0:
            l = primefactors(n/i)
            l.append(i)
            return l
        i+=1
    return [n]      # n is prime


##############################################################
### factorization of a natural ##################################
##############################################################

def factorGenerator(n):
    p = primefactors(n)
    factors = {}
    for p1 in p:
        try:
            factors[p1]+=1
        except KeyError:
            factors[p1]=1
    return factors

def divisors(n):
    factors = factorGenerator(n)
    divisors=[]
    listexponents=[map(lambda x:k**x,range(0,factors[k]+1)) for k in factors.keys()]
    listfactors=cartesianproduct(listexponents)
    for f in listfactors:
        divisors.append(reduce(lambda x, y: x*y, f, 1))
    divisors.sort()
    return divisors



print divisors(60668796879)

P.S. это первый раз, когда я отправляю сообщение в stackoverflow. Я с нетерпением жду отзывов.

В Python 2.6 есть itertools.product ().

jfs 17.12.2008 04:07

Версия, которая везде использует генераторы вместо list.append, могла бы быть чище.

jfs 17.12.2008 04:10

Сито Эратосфена можно использовать для генерации простых чисел, меньших или равных sqrt (n) stackoverflow.com/questions/188425/project-euler-problem#193‌ 605

jfs 17.12.2008 04:17

Стиль кодирования: показатели = [k ** x для k, v в Factors.items () для x в диапазоне (v + 1)]

jfs 17.12.2008 04:23

Для listexponents: [[k ** x для x в диапазоне (v + 1)] для k, v в factor.items ()]

klenwell 09.09.2012 20:24

Адаптировано из CodeReview, вот вариант, который работает с num=1!

from itertools import product
import operator

def prod(ls):
   return reduce(operator.mul, ls, 1)

def powered(factors, powers):
   return prod(f**p for (f,p) in zip(factors, powers))


def divisors(num) :

   pf = dict(prime_factors(num))
   primes = pf.keys()
   #For each prime, possible exponents
   exponents = [range(i+1) for i in pf.values()]
   return (powered(primes,es) for es in product(*exponents))

Кажется, я получаю сообщение об ошибке: NameError: global name 'prime_factors' is not defined. Ни один из других ответов, ни исходный вопрос не определяют, что это делает.

AnnanFay 26.08.2016 21:43

Предполагая, что функция factors возвращает коэффициенты п (например, factors(60) возвращает список [2, 2, 3, 5]), вот функция для вычисления делителей п:

function divisors(n)
    divs := [1]
    for fact in factors(n)
        temp := []
        for div in divs
            if fact * div not in divs
                append fact * div to temp
        divs := divs + temp
    return divs

Это питон? Во всяком случае, это точно не python 3.x.

GinKin 20.03.2014 22:04

Это псевдокод, который должно быть просто перевести на python.

user448810 20.03.2014 22:23

На 3 года позже, лучше поздно, чем никогда :) ИМО, это самый простой и короткий код для этого. У меня нет сравнительной таблицы, но я могу разложить на множители и вычислить делители до миллиона в 1 на моем портативном ноутбуке i5.

Riyaz Mansoor 17.05.2017 22:20

return [x for x in range(n+1) if n/x==int(n/x)]

Спрашивающий попросил дать лучший алгоритм, а не просто более красивый формат.

Veedrac 01.06.2014 23:40

Вам нужно использовать range (1, n + 1), чтобы избежать деления на ноль. Кроме того, вам нужно использовать float (n) для первого деления при использовании Python 2.7, здесь 1/2 = 0

Jens Munk 13.08.2014 17:20

Для меня это отлично работает, а также чисто (Python 3)

def divisors(number):
    n = 1
    while(n<number):
        if (number%n==0):
            print(n)
        else:
            pass
        n += 1
    print(number)

Не очень быстро, но возвращает делители построчно, как вы хотели, также вы можете использовать list.append (n) и list.append (number), если действительно хотите

Вот мое решение. Это кажется глупым, но работает хорошо ... и я пытался найти все правильные делители, поэтому цикл начался с i = 2.

import math as m 

def findfac(n):
    faclist = [1]
    for i in range(2, int(m.sqrt(n) + 2)):
        if n%i == 0:
            if i not in faclist:
                faclist.append(i)
                if n/i not in faclist:
                    faclist.append(n/i)
    return facts

опечатка: вернуть факты => вернуть список факсов

Jonath P 27.07.2020 22:15

Старый вопрос, но вот мое мнение:

def divs(n, m):
    if m == 1: return [1]
    if n % m == 0: return [m] + divs(n, m - 1)
    return divs(n, m - 1)

Вы можете использовать прокси:

def divisorGenerator(n):
    for x in reversed(divs(n, n)):
        yield x

ПРИМЕЧАНИЕ. Для поддерживающих языков это может быть хвостовая рекурсия.

Думаю, можно остановиться на math.sqrt(n) вместо n / 2.

Я приведу вам пример, чтобы вы легко его поняли. Теперь sqrt(28) - это 5.29, поэтому ceil(5.29) будет 6. Итак, если я остановлюсь на 6, то я смогу получить все делители. Как?

Сначала посмотрите код, а затем посмотрите изображение:

import math
def divisors(n):
    divs = [1]
    for i in xrange(2,int(math.sqrt(n))+1):
        if n%i == 0:
            divs.extend([i,n/i])
    divs.extend([n])
    return list(set(divs))

Теперь см. Изображение ниже:

Допустим, я уже добавил 1 в свой список делителей, и я начинаю с i=2, поэтому

Divisors of a 28

Итак, в конце всех итераций, когда я добавил частное и делитель в свой список, все делители 28 заполнены.

Источник: Как определить делители числа

Красиво, красиво !! math.sqrt(n) instead of n/2 обязателен для элегантности

Rafa0809 17.04.2017 20:00

Это неверно. Вы забыли делится само на себя.

jasonleonhard 16.05.2017 02:30

Хороший ответ. Просто и понятно. Но для python 3 необходимо внести два изменения: n / i следует набирать с использованием int (n / i), потому что n / i дает число с плавающей запятой. Также rangex устарел в python 3 и заменен на range.

Geoffroy CALA 30.10.2018 16:00

@GeoffroyCALA Он также мог использовать n//i.

Mark Moretto 02.01.2021 07:07

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

Этот:

  • удобочитаемый
  • короткая
  • самодостаточный, готово для копирования и вставки
  • быстро (в случаях с большим количеством простых множителей и делителей> в 10 раз быстрее, чем принятое решение)
  • совместимость с python3, python2 и pypy

Код:

def divisors(n):
    # get factors and their counts
    factors = {}
    nn = n
    i = 2
    while i*i <= nn:
        while nn % i == 0:
            factors[i] = factors.get(i, 0) + 1
            nn //= i
        i += 1
    if nn > 1:
        factors[nn] = factors.get(nn, 0) + 1

    primes = list(factors.keys())

    # generates factors from primes[k:] subset
    def generate(k):
        if k == len(primes):
            yield 1
        else:
            rest = generate(k+1)
            prime = primes[k]
            for factor in rest:
                prime_to_i = 1
                # prime_to_i iterates prime**i values, i being all possible exponents
                for _ in range(factors[prime] + 1):
                    yield factor * prime_to_i
                    prime_to_i *= prime

    # in python3, `yield from generate(0)` would also work
    for factor in generate(0):
        yield factor

Я бы заменил while i*i <= nn на while i <= limit, где limit = math.sqrt(n)

Rafa0809 17.04.2017 19:58

Вот умный и быстрый способ сделать это для чисел до и около 10 ** 16 в чистом Python 3.6,

from itertools import compress

def primes(n):
    """ Returns  a list of primes < n for n > 2 """
    sieve = bytearray([True]) * (n//2)
    for i in range(3,int(n**0.5)+1,2):
        if sieve[i//2]:
            sieve[i*i//2::i] = bytearray((n-i*i-1)//(2*i)+1)
    return [2,*compress(range(3,n,2), sieve[1:])]

def factorization(n):
    """ Returns a list of the prime factorization of n """
    pf = []
    for p in primeslist:
      if p*p > n : break
      count = 0
      while not n % p:
        n //= p
        count += 1
      if count > 0: pf.append((p, count))
    if n > 1: pf.append((n, 1))
    return pf

def divisors(n):
    """ Returns an unsorted list of the divisors of n """
    divs = [1]
    for p, e in factorization(n):
        divs += [x*p**k for k in range(1,e+1) for x in divs]
    return divs

n = 600851475143
primeslist = primes(int(n**0.5)+1) 
print(divisors(n))

Как называется алгоритм, используемый для нахождения простых чисел и факторизации? Потому что я хочу реализовать это на C# ..

Kyu96 09.09.2018 06:23

Если вы заботитесь только об использовании списков, и все остальное для вас не имеет значения!

from itertools import combinations
from functools import reduce

def get_devisors(n):
    f = [f for f,e in list(factorGenerator(n)) for i in range(e)]
    fc = [x for l in range(len(f)+1) for x in combinations(f, l)]
    devisors = [1 if c==() else reduce((lambda x, y: x * y), c) for c in set(fc)]
    return sorted(devisors)

Я просто собираюсь добавить немного переработанную версию Anivarth's (поскольку я считаю, что она самая питоническая) для дальнейшего использования.

from math import sqrt

def divisors(n):
    divs = {1,n}
    for i in range(2,int(sqrt(n))+1):
        if n%i == 0:
            divs.update((i,n//i))
    return divs

Если на вашем компьютере много памяти, простая простая строка может быть достаточно быстрой с помощью numpy:

N = 10000000; tst = np.arange(1, N); tst[np.mod(N, tst) == 0]
Out: 
array([      1,       2,       4,       5,       8,      10,      16,
            20,      25,      32,      40,      50,      64,      80,
           100,     125,     128,     160,     200,     250,     320,
           400,     500,     625,     640,     800,    1000,    1250,
          1600,    2000,    2500,    3125,    3200,    4000,    5000,
          6250,    8000,   10000,   12500,   15625,   16000,   20000,
         25000,   31250,   40000,   50000,   62500,   78125,   80000,
        100000,  125000,  156250,  200000,  250000,  312500,  400000,
        500000,  625000, 1000000, 1250000, 2000000, 2500000, 5000000])

На моем медленном ПК занимает менее 1 секунды.

Мое решение с помощью функции генератора:

def divisor(num):
    for x in range(1, num + 1):
        if num % x == 0:
            yield x
    while True:
        yield None

Другие вопросы по теме