Quel est le moyen le plus efficace de trouver tous les facteurs d'un nombre en Python?

142

Quelqu'un peut-il m'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 à produire un résultat pour un grand nombre.

Adnan
la source
3
Je ne connais pas python. Mais cette page peut vous être utile en.wikipedia.org/wiki/Integer_factorization
Stan
3
Que diriez-vous d'utiliser primefac? pypi.python.org/pypi/primefac
Zubo

Réponses:

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

Cela renverra tous les facteurs, très rapidement, d'un certain nombre n.

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

sqrt(x) * sqrt(x) = x. Donc, si les deux facteurs sont identiques, ils sont tous les deux la racine carrée. Si vous augmentez un facteur, vous devez réduire l'autre facteur. Cela signifie que l'un des deux sera toujours inférieur ou égal à sqrt(x), il vous suffit donc de rechercher jusqu'à ce point pour trouver l'un des deux facteurs correspondants. Vous pouvez ensuite utiliser x / fac1pour obtenir fac2.

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

Le [i, n/i] for i in range(1, int(sqrt(n)) + 1) if n % i == 0renvoie une paire de facteurs si le reste lorsque vous divisez npar le plus petit est égal à zéro (il n'a pas besoin de vérifier le plus grand aussi; il l'obtient simplement en divisant npar le plus petit.)

L' set(...)extérieur élimine les doublons, ce qui ne se produit que pour les carrés parfaits. Car n = 4, cela reviendra 2deux fois, alors setdébarrassez-vous de l'un d'eux.

agf
la source
1
J'ai copié-collé ceci à partir d'une liste d'algorithmes sur mon ordinateur, tout ce que j'ai fait a été d'encapsuler le sqrt- c'est probablement avant que les gens ne pensent vraiment à prendre en charge Python 3. Je pense que le site sur lequel je l'ai obtenu a essayé __iadd__et c'était plus rapide . Je semble me souvenir de quelque chose sur le fait d' x**0.5être plus rapide qu'à sqrt(x)un moment donné - et c'est plus infaillible de cette façon.
agf
7
Semble être 15% plus rapide si j'utilise à la if not n % iplace deif n % i == 0
dansalmo
3
@sthzg Nous voulons qu'il renvoie un entier, pas un float, et sur Python 3 /retournera un float même si les deux arguments sont des entiers et qu'ils sont exactement divisables, c'est-à-dire 4 / 2 == 2.0non 2.
agf
7
Je sais que c'est une vieille question, mais dans Python 3.x, vous devez ajouter from functools import reducepour que cela fonctionne.
anonymoose le
5
@unseen_rider: Cela ne semble pas correct. Pouvez-vous fournir quelque chose pour le sauvegarder?
Ry-
55

La solution présentée par @agf est excellente, mais on peut atteindre un temps d'exécution d'environ 50% plus rapide pour un nombre impair arbitraire en vérifiant la parité. Comme les facteurs d'un nombre impair sont toujours eux-mêmes impairs, il n'est pas nécessaire de les vérifier lorsqu'il s'agit de nombres impairs.

Je viens de commencer à résoudre moi-même les énigmes du Projet Euler . Dans certains problèmes, une vérification de diviseur est appelée à l'intérieur de deux forboucles imbriquées , et les performances de cette fonction sont donc essentielles.

En combinant ce fait avec l'excellente solution d'agf, je me suis retrouvé 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, sur de petits nombres (~ <100), la surcharge supplémentaire de cette modification peut entraîner la fonction de prendre plus de temps.

J'ai fait quelques tests afin de vérifier la vitesse. Voici le code utilisé. Pour produire les différentes parcelles, j'ai modifié le en X = range(1,100,1)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 = plage (1,100,1) X = plage (1,100,1)

Pas de différence significative ici, mais avec des nombres plus importants, l'avantage est évident:

X = intervalle (1,100000,1000) (uniquement les nombres impairs) X = intervalle (1,100000,1000) (uniquement les nombres impairs)

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

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

Steinar Lima
la source
28

La réponse d'agf est vraiment très cool. Je voulais voir si je pouvais le réécrire pour éviter de l'utiliser reduce(). Voici ce que j'ai proposé:

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 également essayé une version qui utilise des fonctions de générateur délicates:

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)

Je l'ai chronométré en calculant:

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

Je l'ai exécuté une fois pour laisser Python le compiler, puis je l'ai exécuté trois fois sous la commande time (1) et j'ai gardé le meilleur temps.

  • version réduite: 11,58 secondes
  • Version itertools: 11,49 secondes
  • version délicate: 11,12 secondes

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

  • Version iterools (liste): 11,62 secondes

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

Steveha
la source
2
vous pouvez simplifier la "version délicate" (supprimer inutile for tup in):factors = lambda n: {f for i in range(1, int(n**0.5)+1) if n % i == 0 for f in [i, n//i]}
jfs
11

Une approche alternative à la réponse d'agf:

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
Eryk Sun
la source
1
Pouvez-vous expliquer la partie div, mod?
Adnan
3
divmod (x, y) renvoie ((xx% y) / y, x% y), c'est-à-dire le quotient et le reste de la division.
c4757p
Cela ne gère pas bien les facteurs en double - essayez 81 par exemple.
phkahler
Votre réponse est plus claire, donc j'ai été capable de la gogner juste assez pour me méprendre. Je pensais à la factorisation principale où vous voudriez appeler plusieurs 3. Cela devrait être bien, car c'est ce que le PO a demandé.
phkahler
J'ai tout rassemblé en une seule ligne parce que la réponse d'agf l'a fait. J'étais intéressé de voir si reduce()c'était beaucoup plus rapide, alors j'ai fait à peu près tout autre que la reduce()partie de la même manière qu'AGF. Pour la lisibilité, ce serait bien de voir un appel de fonction comme is_even(n)plutôt qu'une expression comme n % 2 == 0.
steveha
9

Voici une alternative à la solution de @ agf qui implémente 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 en Python 2 et Python 3 sans importation et est beaucoup plus lisible. Je n'ai pas testé les performances de cette approche, mais asymptotiquement, elle devrait être la même, et si les performances sont un problème sérieux, aucune des deux solutions n'est optimale.

julien
la source
7

Il existe un algorithme de pointe 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 bascule entre un cocktail de méthodes. Voir la documentation liée ci-dessus.

Compte tenu de tous les facteurs premiers, tous les autres facteurs peuvent être construits facilement.


Notez que même si la réponse acceptée a été autorisée à s'exécuter assez longtemps (c'est-à-dire une éternité) pour factoriser le nombre ci-dessus, pour certains grands nombres, elle échouera, comme l'exemple suivant. Cela est dû à la négligence int(n**0.5). Par exemple, quand n = 10000000000000079**2, nous avons

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

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

Evgeni Sergeev
la source
2
Il ne trouve pas tous les diviseurs mais seulement les facteurs premiers donc ce n'est pas vraiment une réponse. Vous devriez montrer comment tous les autres facteurs peuvent être construits, pas seulement dire que c'est facile! À propos, sympy.divisors peut être un meilleur choix pour répondre à cette question.
Colin Pitrat
Et notez que sympy.divisors n'est pas beaucoup plus rapide que la solution acceptée.
Colin Pitrat
@ColinPitrat: Je doute que ce sympy.divisorsne soit pas beaucoup plus rapide, en particulier pour les nombres avec peu de diviseurs. Vous avez des repères?
Ry-
@Ry J'en ai fait un quand j'ai écrit ce commentaire il y a un an. Il faut 2 minutes pour en écrire un, alors n'hésitez pas à vérifier.
Colin Pitrat
3
@ColinPitrat: vérifié. Comme prévu, la réponse acceptée est à peu près la même vitesse que sympy.divisorspour 100 000 et plus lente pour tout ce qui est plus élevé (lorsque la vitesse compte réellement). (Et, bien sûr, sympy.divisorsfonctionne sur des nombres comme 10000000000000079**2.)
Ry-
7

Pour n jusqu'à 10 ** 16 (peut-être même un peu plus), voici une solution rapide pure 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))
Bruno Astrolino
la source
6

Poursuite de l'amélioration de la solution afg & eryksun. Le morceau de code suivant renvoie une liste triée de tous les facteurs sans modifier la complexité asymptotique au moment de l'exécution:

    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 de la complexité à nlog (n); Il est beaucoup plus rapide d'utiliser list.reverse () sur l2 qui prend une complexité O (n). (C'est ainsi que python est fait.) Après l2.reverse (), l2 peut être ajouté à l1 pour obtenir la liste triée des facteurs.

Remarquez que l1 contient des i -s qui augmentent. l2 contient q -s qui sont décroissants. C'est la raison derrière l'utilisation de l'idée ci-dessus.

Pranjal Mittal
la source
À peu près sûr list.reverseest O (n) pas O (1), pas que cela change la complexité globale.
agf
Oui c'est vrai. J'ai fait une erreur. Devrait être O (n). (J'ai mis à jour la réponse maintenant à la bonne)
Pranjal Mittal
C'est environ 2 fois plus lent que les solutions de @ steveha ou @ agf.
jfs
Vous pouvez obtenir une petite amélioration de la vitesse (2-3%) en retournant l1 + l2.reversed()plutôt qu'en inversant la liste en place.
Rakurai
6

J'ai essayé la plupart de ces merveilleuses réponses avec le temps pour comparer leur efficacité à ma fonction simple et pourtant je vois constamment les miennes surpasser celles énumérées ici. J'ai pensé que je le partagerais et verrais ce que vous en 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, vous devrez importer les mathématiques pour tester, mais remplacer math.sqrt (n) par n **. 5 devrait fonctionner aussi bien. Je ne prends pas la peine de perdre du temps à vérifier les doublons, car les doublons ne peuvent pas exister dans un ensemble de toute façon.

oxrock
la source
Super truc! Si vous mettez int (math.sqrt (n)) + 1 en dehors de la boucle for, vous devriez obtenir un peu plus de performances car vous n'aurez pas à le recalculer à chaque itération de la boucle for
Tristan Forward
3
@TristanForward: Ce n'est pas ainsi que les boucles for fonctionnent en Python. xrange(1, int(math.sqrt(n)) + 1)est évalué une fois.
Ry-
5

Voici une autre alternative sans réduction qui fonctionne bien avec de grands nombres. Il utilise sumpour 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], []))
dansalmo
la source
1
Ce n'est pas le cas, c'est inutilement le temps quadratique. N'utilisez pas sumou reduce(list.__add__)pour aplatir une liste.
juanpa.arrivillaga
4

Assurez-vous de saisir le nombre plus grand que sqrt(number_to_factor)pour les nombres inhabituels 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
mbowden
la source
1
Il ne produit pas tous les facteurs d'un nombre. Il calcule les facteurs premiers d'un nombre par exemple, pour x=8attendu[1, 2, 4, 8][2, 2, 2]
:,
11 est trouvé lorsque 9 est comupted dans le code donné par @agf. `i = 9 -> 99% 9 == 0 -> 9 et 99/9 = 11 est ajouté.
Steinar Lima
4

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

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

Voici un exemple si vous souhaitez utiliser le nombre premier 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)]
Pierre Thibault
la source
J'ai créé un projet sur Github: github.com/Pierre-Thibault/Factor .
Pierre Thibault
2

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

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

il s'agit bien sûr toujours de la division de première instance et rien de plus sophistiqué. et donc toujours très limité dans son efficacité (surtout pour les grands nombres sans petits diviseurs).

c'est python3; la division //devrait être la seule chose à adapter pour python 2 (ajouter from __future__ import division).

protagoniste de hiro
la source
1

L'utilisation set(...)rend le code légèrement plus lent et n'est vraiment nécessaire que 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 if sq*sq != num:condition est nécessaire pour les nombres comme 12, où la racine carrée n'est pas un entier, mais le plancher de la racine carrée est un facteur.

Notez que cette version ne renvoie pas le numéro lui-même, mais c'est une solution facile si vous le souhaitez. La sortie n'est pas non plus triée.

Je l'ai chronométré 10000 fois sur tous les numéros 1 à 200 et 100 fois sur tous les numéros 1-5000. Il surpasse toutes les autres versions que j'ai testées, y compris les solutions de dansalmo, Jason Schorn, oxrock, agf, steveha et eryksun, bien qu'oxrock soit de loin la plus proche.

HamsterBoo
la source
1

votre facteur maximum n'est pas supérieur à votre nombre, alors disons

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á!

Polina Novikova
la source
1
 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. 
Tangang Atanga
la source
0

Utilisez quelque chose d'aussi simple que la compréhension de liste suivante, en notant que nous n'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 référence à l'utilisation de la racine carrée, disons que nous voulons trouver des facteurs de 10. La partie entière du sqrt(10) = 4donc range(1, int(sqrt(10))) = [1, 2, 3, 4]et des tests jusqu'à 4 manque clairement 5.

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

Jason Schorn
la source
Le problème avec cette solution est qu'elle vérifie de nombreux nombres qui ne peuvent pas être des facteurs - et elle vérifie le plus élevé de chaque paire de facteurs séparément lorsque vous savez déjà qu'il s'agit d'un facteur après avoir trouvé le plus petit de la paire de facteurs.
agf
1
@JasonSchorn: Lorsque vous trouvez 2, vous savez immédiatement que 10/2 = 5 est également un diviseur, pas besoin de vérifier 5 séparément! :)
Moberg
0

Je pense que pour la lisibilité et la vitesse, la solution @ oxrock est la meilleure, voici donc le code réécrit 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
Nic Scozzaro
la source
0

J'ai été assez surpris quand j'ai vu cette question que personne n'utilisait numpy même lorsque numpy est beaucoup plus rapide que les boucles python. En implémentant la solution de @ agf avec numpy, cela s'est avéré en moyenne 8 fois plus rapide . Je crois que si vous implémentiez certaines des autres solutions dans numpy, vous pourriez obtenir des moments 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 des x ne sont pas l'entrée des fonctions. L'entrée des fonctions est de 2 au nombre sur l'axe des x moins 1. Donc, où dix est l'entrée serait 2 ** 10-1 = 1023

Résultats des tests de performance de l'utilisation de numpy au lieu de boucles for.

Benedikt Vilji Magnússon
la source
1
Si vous comptez utiliser une bibliothèque, autant en faire la bonne: SymPy, comme le montre la réponse d'Evgeni Sergeev.
Ry-
0
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;
}
Tangang Atanga
la source
Presque tous les algorithmes ici se limitent à la plage au nombre * .5, mais en réalité cette plage est beaucoup plus petite. c'est en fait sqrt du nombre. si nous avons le diviseur inférieur, nous pouvons facilement obtenir le diviseur supérieur. puisque c'est juste le nombre / diviseur. pour 16 j'obtiens 4 pour le sqrt, puis boucle de 1 à 4. puisque 2 est un diviseur inférieur de 16 nous prenons 16/2 pour obtenir 8. si nous avons 1 alors pour obtenir 16 est (16/1). J'ai découvert cela en apprenant la factorisation des nombres premiers, donc je ne sais pas si elle est publiée ailleurs, mais cela fonctionne même pour de grands nombres. Je peux fournir une solution python.
Tangang Atanga
-4

Je pense que c'est le moyen le plus simple de le faire:

    x = 23

    i = 1
    while i <= x:
      if x % i == 0:
        print("factor: %s"% i)
      i += 1
DeDude
la source
Votre réponse, tout en donnant le bon résultat, est très inefficace. Jetez un œil à la réponse acceptée. Une explication de la façon dont il résout le problème aide toujours une réponse à être plus utile.
Nick