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

Здесь очень тупой способ:

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: Прекратите говорить, что это дубликат этой публикации. Вычисление числа делителей заданного числа не требует вычисления всех делителей. Это другая проблема, если вы думаете, что нет, тогда ищите "функцию Divisor" в википедии. Прочитайте вопросы и ответ перед публикацией, если вы не понимаете, что такое тема, просто не добавляйте не полезные и уже предоставленные ответы.

Ответ 1

Учитывая вашу функцию 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

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

Ответ 2

Чтобы расширить то, что сказал Шими, вы должны использовать только цикл от 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]

Ответ 3

Я думаю, что вы можете остановиться на 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 заполняются.

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

Ответ 4

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

Этот:

  • удобочитаемый
  • короткая
  • автономный, копировать и вставлять готов
  • быстро (в случаях с большим количеством простых факторов и делителей> в 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

Ответ 5

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

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

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

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

Пьетро Сперони (pietrosperoni dot it)

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. Я с нетерпением жду обратной связи.

Ответ 6

Адаптировано из 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))

Ответ 7

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

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

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

Ответ 8

Вот умный и быстрый способ сделать это для чисел до 10 и около 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))

Ответ 9

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

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

Ответ 10

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

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

Ответ 11

Вот мое решение. Кажется, это глупо, но хорошо работает... и я пытался найти все правильные делители, чтобы цикл начинался с я = 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

Ответ 12

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

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)

Ответ 13

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

Ответ 14

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

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

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