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.
primefac
? pypi.python.org/pypi/primefacRéponses:
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 utiliserx / fac1
pour obtenirfac2
.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 == 0
renvoie une paire de facteurs si le reste lorsque vous divisezn
par le plus petit est égal à zéro (il n'a pas besoin de vérifier le plus grand aussi; il l'obtient simplement en divisantn
par le plus petit.)L'
set(...)
extérieur élimine les doublons, ce qui ne se produit que pour les carrés parfaits. Carn = 4
, cela reviendra2
deux fois, alorsset
débarrassez-vous de l'un d'eux.la source
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.if not n % i
place deif n % i == 0
/
retournera un float même si les deux arguments sont des entiers et qu'ils sont exactement divisables, c'est-à-dire4 / 2 == 2.0
non2
.from functools import reduce
pour que cela fonctionne.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
for
boucles 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:
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.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 = plage (2,100000,100) (uniquement les nombres pairs)
X = plage (1,100000,1001) (parité alternée)
la source
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é:J'ai également essayé une version qui utilise des fonctions de générateur délicates:
Je l'ai chronométré en calculant:
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.
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:
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.
la source
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]}
Une approche alternative à la réponse d'agf:
la source
reduce()
c'était beaucoup plus rapide, alors j'ai fait à peu près tout autre que lareduce()
partie de la même manière qu'AGF. Pour la lisibilité, ce serait bien de voir un appel de fonction commeis_even(n)
plutôt qu'une expression commen % 2 == 0
.Voici une alternative à la solution de @ agf qui implémente le même algorithme dans un style plus pythonique:
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.
la source
Il existe un algorithme de pointe dans SymPy appelé factorint :
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, quandn = 10000000000000079**2
, nous avonsPuisque 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.
la source
sympy.divisors
ne soit pas beaucoup plus rapide, en particulier pour les nombres avec peu de diviseurs. Vous avez des repères?sympy.divisors
pour 100 000 et plus lente pour tout ce qui est plus élevé (lorsque la vitesse compte réellement). (Et, bien sûr,sympy.divisors
fonctionne sur des nombres comme10000000000000079**2
.)Pour n jusqu'à 10 ** 16 (peut-être même un peu plus), voici une solution rapide pure Python 3.6,
la source
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:
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.
la source
list.reverse
est O (n) pas O (1), pas que cela change la complexité globale.l1 + l2.reversed()
plutôt qu'en inversant la liste en place.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.
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.
la source
xrange(1, int(math.sqrt(n)) + 1)
est évalué une fois.Voici une autre alternative sans réduction qui fonctionne bien avec de grands nombres. Il utilise
sum
pour aplatir la liste.la source
sum
oureduce(list.__add__)
pour aplatir une liste.Assurez-vous de saisir le nombre plus grand que
sqrt(number_to_factor)
pour les nombres inhabituels comme 99 qui a 3 * 3 * 11 etfloor sqrt(99)+1 == 10
.la source
x=8
attendu[1, 2, 4, 8]
[2, 2, 2]
La façon la plus simple de trouver les facteurs d'un nombre:
la source
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.
la source
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: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 (ajouterfrom __future__ import division
).la source
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: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.
la source
votre facteur maximum n'est pas supérieur à votre nombre, alors disons
voilá!
la source
la source
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:
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) = 4
doncrange(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.la source
Je pense que pour la lisibilité et la vitesse, la solution @ oxrock est la meilleure, voici donc le code réécrit pour python 3+:
la source
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:
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
la source
la source
Je pense que c'est le moyen le plus simple de le faire:
la source