Question

Quelqu'un peut-il me expliquer un moyen efficace de trouver tous les facteurs d'un nombre en Python (2.7)?

Je peux créer un algorithme pour ce faire, mais je pense qu'il est mal codé et prend trop de temps pour produire un résultat pour un grand nombre.

Était-ce utile?

La solution

from functools import reduce

def factors(n):    
    return set(reduce(list.__add__, 
                ([i, n//i] for i in range(1, int(n**0.5) + 1) if n % i == 0)))

Ceci renvoie tous les facteurs, très rapidement, d'un certain nombre n.

Pourquoi la racine carrée comme la limite supérieure?

sqrt(x) * sqrt(x) = x. Donc, si les deux facteurs sont les mêmes, ils sont à la fois la racine carrée. Si vous faites un plus grand facteur, vous devez rendre l'autre plus petit facteur. Cela signifie que l'un des deux sera toujours inférieure ou égale à sqrt(x), donc il suffit de chercher jusqu'à ce moment-là pour trouver l'un des deux facteurs correspondants. Vous pouvez ensuite utiliser x / fac1 pour obtenir fac2.

Le reduce(list.__add__, ...) prend les petites listes de [fac1, fac2] et les réunir dans une longue liste.

Le [i, n/i] for i in range(1, int(sqrt(n)) + 1) if n % i == 0 retourne une paire de facteurs si le reste quand on divise par n le plus petit est égal à zéro (il n'a pas besoin de vérifier la plus grande aussi, il obtient juste que en divisant par n le plus petit.)

Le set(...) à l'extérieur est de se débarrasser des doublons, ce qui ne se produit que pour des carrés parfaits. Pour n = 4, cela retournera 2 deux fois, alors set se débarrasse d'un d'entre eux.

Autres conseils

La solution présentée par @agf est grande, mais on peut atteindre ~ 50% plus rapide pour le temps d'exécution arbitraire impair nombre en vérifiant la parité. Comme les facteurs d'un nombre impair sont eux-mêmes toujours étrange, il est nécessaire de vérifier ces lorsqu'ils traitent avec des nombres impairs.

Je viens de commencer à résoudre les énigmes Project Euler moi-même. Dans certains problèmes, un chèque de diviseur est appelé à l'intérieur de deux boucles de for imbriquées, et la performance de cette fonction est donc essentielle.

La combinaison de ce fait avec une solution excellente de faa, je l'ai fini avec cette fonction:

from math import sqrt
def factors(n):
        step = 2 if n%2 else 1
        return set(reduce(list.__add__,
                    ([i, n//i] for i in range(1, int(sqrt(n))+1, step) if n % i == 0)))

Cependant, un petit nombre (<~ 100), la surcharge supplémentaire de cette altération peut entraîner la fonction de prendre plus de temps.

J'ai couru quelques tests afin de vérifier la vitesse. Voici le code utilisé. Pour produire les différentes parcelles, j'ai modifié le X = range(1,100,1) en conséquence.

import timeit
from math import sqrt
from matplotlib.pyplot import plot, legend, show

def factors_1(n):
    step = 2 if n%2 else 1
    return set(reduce(list.__add__,
                ([i, n//i] for i in range(1, int(sqrt(n))+1, step) if n % i == 0)))

def factors_2(n):
    return set(reduce(list.__add__,
                ([i, n//i] for i in range(1, int(sqrt(n)) + 1) if n % i == 0)))

X = range(1,100000,1000)
Y = []
for i in X:
    f_1 = timeit.timeit('factors_1({})'.format(i), setup='from __main__ import factors_1', number=10000)
    f_2 = timeit.timeit('factors_2({})'.format(i), setup='from __main__ import factors_2', number=10000)
    Y.append(f_1/f_2)
plot(X,Y, label='Running time with/without parity check')
legend()
show()

X = distance (1,100,1) X = distance (1,100,1)

ici Aucune différence significative, mais avec des chiffres plus importants, l'avantage est évident:

X = distance (1,100000,1000) (seuls les nombres impairs) X = distance (1,100000,1000) (seuls les nombres impairs)

X = distance (2,100000,100) (seuls les nombres pairs) X = plage (2,100000,100) (uniquement les nombres pairs)

X = distance (1,100000,1001) (parité alternée) X = distance (1,100000,1001) (parité alternatif)

La réponse de

qu'AGF vraiment tout à fait refroidir. Je voulais voir si je pouvais réécrire pour éviter d'utiliser reduce(). C'est ce que je suis venu avec:

import itertools
flatten_iter = itertools.chain.from_iterable
def factors(n):
    return set(flatten_iter((i, n//i) 
                for i in range(1, int(n**0.5)+1) if n % i == 0))

J'ai essayé aussi une version qui utilise des fonctions de générateur délicat:

def factors(n):
    return set(x for tup in ([i, n//i] 
                for i in range(1, int(n**0.5)+1) if n % i == 0) for x in tup)

I chronométré en calculant:

start = 10000000
end = start + 40000
for n in range(start, end):
    factors(n)

je l'ai couru une fois pour laisser Python compiler, puis il a couru sous le temps (1) commande trois fois et gardé le meilleur temps.

  • réduire la version: 11,58 secondes
  • itertools version: 11,49 secondes
  • Version délicate: 11.12 secondes

Notez que la version itertools construit un tuple et passant à flatten_iter (). Si je change le code pour construire une liste à la place, il ralentit légèrement:

  • iterools (liste) Version: 11,62 secondes

Je crois que la version délicate des fonctions du générateur est le plus rapide possible en Python. Mais ce n'est pas vraiment beaucoup plus rapide que la réduire la version, environ 4% plus rapide en fonction de mes mesures.

Une approche alternative à la réponse de faa:

def factors(n):    
    result = set()
    for i in range(1, int(n ** 0.5) + 1):
        div, mod = divmod(n, i)
        if mod == 0:
            result |= {i, div}
    return result

Je l'ai essayé la plupart de ces merveilleuses réponses avec timeit de comparer leur efficacité par rapport à ma fonction simple et pourtant je vois constamment mes surclassent celles qui sont énumérées ici. Je pensais que je devais partager et voir ce que vous pensez tous.

def factors(n):
    results = set()
    for i in xrange(1, int(math.sqrt(n)) + 1):
        if n % i == 0:
            results.add(i)
            results.add(int(n/i))
    return results

Comme il est écrit que vous devrez les mathématiques à l'importation pour tester, mais en remplaçant math.sqrt (n) avec n **. 5 devrait fonctionner tout aussi bien. Je ne prends pas la peine de perdre de temps pour vérifier la présence de doublons, car les doublons ne peuvent pas exister dans un ensemble peu importe.

Poursuite de l'amélioration de l'AFG et la solution de eryksun. La pièce de code suivant retourne une liste triée de tous les facteurs sans changer le temps d'exécution complexité asymptotique:

    def factors(n):    
        l1, l2 = [], []
        for i in range(1, int(n ** 0.5) + 1):
            q,r = n//i, n%i     # Alter: divmod() fn can be used.
            if r == 0:
                l1.append(i) 
                l2.append(q)    # q's obtained are decreasing.
        if l1[-1] == l2[-1]:    # To avoid duplication of the possible factor sqrt(n)
            l1.pop()
        l2.reverse()
        return l1 + l2

Idée: Au lieu d'utiliser la fonction list.sort () pour obtenir une liste triée qui donne nlog (n) la complexité; Il est beaucoup plus rapide list.reverse à l'utilisation () sur l2 qui prend O (n) complexité. (Voilà comment python est fait.) Après l2.reverse (), l2 peut être ajoutée à obtenir la l1 liste triée des facteurs.

Avis, contient i -s qui augmentent. l2 contient q -s qui diminuent. Cest la raison derrière l'utilisation de l'idée ci-dessus.

Voici une alternative à la solution de @ faa qui met en oeuvre le même algorithme dans un style plus pythonique:

def factors(n):
    return set(
        factor for i in range(1, int(n**0.5) + 1) if n % i == 0
        for factor in (i, n//i)
    )

Cette solution fonctionne à la fois Python 2 et Python 3 sans les importations et est beaucoup plus facile à lire. Je ne l'ai pas testé les performances de cette approche, mais asymptotiquement il devrait être le même, et si la performance est une préoccupation sérieuse, ni solution est optimale.

Pour n jusqu'à 10 ** 16 (peut-être même un peu plus), voici une solution Python 3.6 rapide pur,

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

Il y a un algorithme de force de l'industrie dans sympy appelé factorint :

>>> from sympy import factorint
>>> factorint(2**70 + 3**80) 
{5: 2,
 41: 1,
 101: 1,
 181: 1,
 821: 1,
 1597: 1,
 5393: 1,
 27188665321L: 1,
 41030818561L: 1}

Cela a pris moins d'une minute. Il passe entre un cocktail de méthodes. Consultez la documentation liée ci-dessus.

Compte tenu de tous les facteurs premiers, tous les autres facteurs peut être construit facilement.


Notez que même si la réponse acceptée a été autorisé à courir assez longtemps (par exemple une éternité) au facteur le numéro ci-dessus, pour certains un grand nombre, il échouera, comme l'exemple suivant. Cela est dû à la int(n**0.5) bâclée. Par exemple, lorsque n = 10000000000000079**2, nous avons

>>> int(n**0.5)
10000000000000078L

Depuis 10000000000000079 est un premier , l'algorithme de la réponse acceptée ne trouvera jamais cette facteur. Notez que ce n'est pas seulement un hors par un; pour un plus grand nombre, il sera hors de plus. Pour cette raison, il est préférable d'éviter les nombres à virgule flottante dans les algorithmes de ce genre.

Voici une autre alternative sans réduire qui fonctionne bien avec un grand nombre. Il utilise sum pour aplatir la liste.

def factors(n):
    return set(sum([[i, n//i] for i in xrange(1, int(n**0.5)+1) if not n%i], []))

Assurez-vous de saisir le plus grand nombre que sqrt(number_to_factor) pour un nombre inhabituel comme 99 qui a 3 * 3 * 11 et floor sqrt(99)+1 == 10.

import math

def factor(x):
  if x == 0 or x == 1:
    return None
  res = []
  for i in range(2,int(math.floor(math.sqrt(x)+1))):
    while x % i == 0:
      x /= i
      res.append(i)
  if x != 1: # Unusual numbers
    res.append(x)
  return res

Voici un exemple si vous souhaitez utiliser le nombre de nombres premiers pour aller beaucoup plus vite. Ces listes sont faciles à trouver sur Internet. J'ai ajouté des commentaires dans le code.

# http://primes.utm.edu/lists/small/10000.txt
# First 10000 primes

_PRIMES = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 
        31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 
        73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 
        127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 
        179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 
        233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 
        283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 
        353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 
        419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 
        467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 
        547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 
        607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 
        661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 
        739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 
        811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 
        877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 
        947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 
# Mising a lot of primes for the purpose of the example
)


from bisect import bisect_left as _bisect_left
from math import sqrt as _sqrt


def get_factors(n):
    assert isinstance(n, int), "n must be an integer."
    assert n > 0, "n must be greather than zero."
    limit = pow(_PRIMES[-1], 2)
    assert n <= limit, "n is greather then the limit of {0}".format(limit)
    result = set((1, n))
    root = int(_sqrt(n))
    primes = [t for t in get_primes_smaller_than(root + 1) if not n % t]
    result.update(primes)  # Add all the primes factors less or equal to root square
    for t in primes:
        result.update(get_factors(n/t))  # Add all the factors associted for the primes by using the same process
    return sorted(result)


def get_primes_smaller_than(n):
    return _PRIMES[:_bisect_left(_PRIMES, n)]

un algorithme potentiellement plus efficace que ceux présentés ici déjà (surtout s'il y a des petites premiers factons dans n). l'astuce est ici ajuster la limite jusqu'à laquelle la division d'essai est nécessaire à chaque fois que les premiers facteurs se trouvent:

def factors(n):
    '''
    return prime factors and multiplicity of n
    n = p0^e0 * p1^e1 * ... * pk^ek encoded as
    res = [(p0, e0), (p1, e1), ..., (pk, ek)]
    '''

    res = []

    # get rid of all the factors of 2 using bit shifts
    mult = 0
    while not n & 1:
        mult += 1
        n >>= 1
    if mult != 0:
        res.append((2, mult))

    limit = round(sqrt(n))
    test_prime = 3
    while test_prime <= limit:
        mult = 0
        while n % test_prime == 0:
            mult += 1
            n //= test_prime
        if mult != 0:
            res.append((test_prime, mult))
            if n == 1:              # only useful if ek >= 3 (ek: multiplicity
                break               # of the last prime) 
            limit = round(sqrt(n))  # adjust the limit
        test_prime += 2             # will often not be prime...
    if n != 1:
        res.append((n, 1))
    return res

est de la division cours d'essai encore et rien de plus de fantaisie. et donc encore très limitée dans son efficacité (en particulier pour les grands nombres sans petits diviseurs).

est python3; la // division devrait être la seule chose que vous devez adapter pour python 2 (add from __future__ import division).

votre facteur maximum ne dépasse pas votre numéro, donc, disons que

def factors(n):
    factors = []
    for i in range(1, n//2+1):
        if n % i == 0:
            factors.append (i)
    factors.append(n)

    return factors

voilá!

La façon la plus simple de trouver des facteurs d'un nombre:

def factors(x):
    return [i for i in range(1,x+1) if x%i==0]

Utilisation set(...) rend le code un peu plus lent, et n'est vraiment nécessaire lorsque vous vérifiez la racine carrée. Voici ma version:

def factors(num):
    if (num == 1 or num == 0):
        return []
    f = [1]
    sq = int(math.sqrt(num))
    for i in range(2, sq):
        if num % i == 0:
            f.append(i)
            f.append(num/i)
    if sq > 1 and num % sq == 0:
        f.append(sq)
        if sq*sq != num:
            f.append(num/sq)
    return f

La condition de if sq*sq != num: est nécessaire pour les nombres comme 12, où la racine carrée est pas un nombre entier, mais le plancher de la racine carrée est un facteur.

Notez que cette version ne retourne pas le nombre lui-même, mais qui est une solution facile si vous le voulez. La sortie ne sont pas triées aussi.

je l'ai chronométré courir 10000 fois sur tous les numéros 1-200 et 100 fois sur tous les numéros 1-5000. Il surclasse toutes les autres versions I testés, y compris dansalmo de Jason Schorn, de son oxrock, agf de, de steveha et les solutions de eryksun, bien que ce oxrock est de loin le plus proche.

Utilisez quelque chose d'aussi simple que la compréhension de la liste suivante, en notant que nous ne avons pas besoin de tester 1 et le nombre que nous essayons de trouver:

def factors(n):
    return [x for x in range(2, n//2+1) if n%x == 0]

En ce qui concerne l'utilisation de la racine carrée, disons que nous voulons trouver des facteurs de 10. La partie entière du sqrt(10) = 4 donc range(1, int(sqrt(10))) = [1, 2, 3, 4] et tester jusqu'à 4 manque clairement 5.

À moins que je manque quelque chose que je suggère, si vous devez le faire de cette façon, en utilisant int(ceil(sqrt(x))). Bien sûr, cela produit un grand nombre d'appels inutiles aux fonctions.

Je pense que pour la lisibilité et la vitesse @ solution de oxrock est le meilleur, alors voici le code réécrite pour python 3 +:

def num_factors(n):
    results = set()
    for i in range(1, int(n**0.5) + 1):
        if n % i == 0: results.update([i,int(n/i)])
    return results

J'ai été très surpris quand j'ai vu cette question que personne ne numpy utilisé même lorsque numpy est façon plus rapide que les boucles de python. En mettant en œuvre la solution de @ avec faa numpy et il est apparu à la moyenne 8x plus rapide . Je belive que si vous mis en œuvre certaines des autres solutions numpy vous pourriez obtenir des temps incroyables.

Voici ma fonction:

import numpy as np
def b(n):
    r = np.arange(1, int(n ** 0.5) + 1)
    x = r[np.mod(n, r) == 0]
    return set(np.concatenate((x, n / x), axis=None))   

Notez que les nombres de l'axe x ne sont pas l'entrée des fonctions. L'entrée des fonctions est de 2 à nombre sur l'axe des x de moins 1. Alors, où dix est l'entrée serait 2 ** 10-1 = 1023

import 'dart:math';
generateFactorsOfN(N){
  //determine lowest bound divisor range
  final lowerBoundCheck = sqrt(N).toInt();
  var factors = Set<int>(); //stores factors
  /**
   * Lets take 16:
   * 4 = sqrt(16)
   * start from 1 ...  4 inclusive
   * check mod 16 % 1 == 0?  set[1, (16 / 1)]
   * check mod 16 % 2 == 0?  set[1, (16 / 1) , 2 , (16 / 2)]
   * check mod 16 % 3 == 0?  set[1, (16 / 1) , 2 , (16 / 2)] -> unchanged
   * check mod 16 % 4 == 0?  set[1, (16 / 1) , 2 , (16 / 2), 4, (16 / 4)]
   *
   *  ******************* set is used to remove duplicate
   *  ******************* case 4 and (16 / 4) both equal to 4
   *  return factor set<int>.. this isn't ordered
   */

  for(var divisor = 1; divisor <= lowerBoundCheck; divisor++){
    if(N % divisor == 0){
      factors.add(divisor);
      factors.add(N ~/ divisor); // ~/ integer division 
    }
  }
  return factors;
}
 import math

    '''
    I applied finding prime factorization to solve this. (Trial Division)
    It's not complicated
    '''


    def generate_factors(n):
        lower_bound_check = int(math.sqrt(n))  # determine lowest bound divisor range [16 = 4]
        factors = set()  # store factors
        for divisors in range(1, lower_bound_check + 1):  # loop [1 .. 4]
            if n % divisors == 0:
                factors.add(divisors)  # lower bound divisor is found 16 [ 1, 2, 4]
                factors.add(n // divisors)  # get upper divisor from lower [ 16 / 1 = 16, 16 / 2 = 8, 16 / 4 = 4]
        return factors  # [1, 2, 4, 8 16]


    print(generate_factors(12)) # {1, 2, 3, 4, 6, 12} -> pycharm output

 Pierre Vriens hopefully this makes more sense. this is an O(nlogn) solution. 

Je pense que c'est la façon de faire plus simple que:

    x = 23

    i = 1
    while i <= x:
      if x % i == 0:
        print("factor: %s"% i)
      i += 1
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top