Comment puis-je vérifier si un nombre est un carré parfait?
La vitesse ne nous concerne pas, pour l'instant, je ne fais que travailler.
Le problème de s’appuyer sur un calcul en virgule flottante (math.sqrt(x)
, ou x**0.5
) est que vous ne pouvez pas vraiment être sûr qu’elle est exacte (pour des entiers suffisamment grands x
, ce ne sera pas le cas.) , et pourrait même déborder). Heureusement (si vous n'êtes pas pressé ;-), il existe de nombreuses approches purement entières, telles que les suivantes ...:
def is_square(apositiveint):
x = apositiveint // 2
seen = set([x])
while x * x != apositiveint:
x = (x + (apositiveint // x)) // 2
if x in seen: return False
seen.add(x)
return True
for i in range(110, 130):
print i, is_square(i)
Astuce: il est basé sur "l'algorithme babylonien" pour la racine carrée, voir wikipedia . Cela fonctionne pour tout nombre positif pour lequel vous disposez de suffisamment de mémoire pour que le calcul puisse aboutir ;-).
Edit : voyons un exemple ...
x = 12345678987654321234567 ** 2
for i in range(x, x+2):
print i, is_square(i)
cela imprime, comme vous le souhaitez (et dans un laps de temps raisonnable aussi ;-):
152415789666209426002111556165263283035677489 True
152415789666209426002111556165263283035677490 False
Avant de proposer des solutions basées sur des résultats intermédiaires en virgule flottante, assurez-vous qu’ils fonctionnent correctement avec cet exemple simple - ce n’est pas que difficile (il vous suffit de quelques vérifications supplémentaires au cas où le sqrt calculé est un peu en retrait), prend juste un peu de soin.
Et ensuite, essayez avec x**7
et trouvez un moyen intelligent de contourner le problème que vous rencontrerez,
OverflowError: long int too large to convert to float
vous devrez être de plus en plus intelligent, car le nombre ne cesse de croître, bien sûr.
Si je étais pressé , bien sûr, j'utiliserais gmpy - mais dans ce cas, je suis clairement partial ;-).
>>> import gmpy
>>> gmpy.is_square(x**7)
1
>>> gmpy.is_square(x**7 + 1)
0
Oui, je sais, c’est tellement facile de tricher (un peu ce que je ressens envers Python en général ;-) - pas d’intelligence du tout, mais une perfection et une simplicité parfaites (et cas de gmpy, vitesse rapide ;-) ...
Utilisez la méthode de Newton pour vous positionner rapidement sur la racine carrée entière la plus proche, puis la carré et voir si c'est votre nombre Voir isqrt .
Etant donné que vous ne pouvez jamais compter sur des comparaisons exactes lorsque vous utilisez des calculs en virgule flottante (tels que ces méthodes de calcul de la racine carrée), une implémentation moins sujette aux erreurs serait
import math
def is_square(integer):
root = math.sqrt(integer)
if int(root + 0.5) ** 2 == integer:
return True
else:
return False
Imagine integer
est 9
. math.sqrt(9)
pourrait être 3.0
, mais il pourrait aussi s'agir de 2.99999
ou 3.00001
, de sorte que le résultat immédiat n'est pas fiable. Sachant que int
prend la valeur plancher, augmenter la valeur flottante de 0.5
signifie d’abord que nous obtiendrons la valeur recherchée si nous sommes dans une plage où float
a toujours une résolution suffisamment fine pour représenter des nombres proches de celui pour lequel nous regardons.
import math
if (math.sqrt(number)-int(math.sqrt(number))):
print "it's not a perfect square"
Un carré parfait est un nombre qui peut être exprimé comme le produit de deux entiers égaux. math.sqrt(number)
renvoie une float
. int(math.sqrt(number))
attribue le résultat à int
.
Si la racine carrée est un entier, par exemple 3, alors math.sqrt(number) - int(math.sqrt(number))
sera 0 et l'instruction if
sera False
. Si la racine carrée était un nombre réel comme 3.2, alors ce sera True
et affichera "ce n'est pas un carré parfait".
Si cela vous intéresse, j’ai une réponse mathématique pure à une question similaire sur math stackexchange: "Détecter les carrés parfaits plus rapidement qu’en extrayant la racine carrée" .
Ma propre implémentation de isSquare (n) n'est peut-être pas la meilleure, mais je l'aime bien. Cela m'a pris plusieurs mois d'études en théorie des mathématiques, calcul numérique et programmation python, en me comparant à d'autres contributeurs, etc., pour vraiment cliquer avec cette méthode. J'aime sa simplicité et son efficacité cependant. Je n'ai pas vu mieux. Dis-moi ce que tu penses.
def isSquare(n):
## Trivial checks
if type(n) != int: ## integer
return False
if n < 0: ## positivity
return False
if n == 0: ## 0 pass
return True
## Reduction by powers of 4 with bit-logic
while n&3 == 0:
n=n>>2
## Simple bit-logic test. All perfect squares, in binary,
## end in 001, when powers of 4 are factored out.
if n&7 != 1:
return False
if n==1:
return True ## is power of 4, or even power of 2
## Simple modulo equivalency test
c = n%10
if c in {3, 7}:
return False ## Not 1,4,5,6,9 in mod 10
if n % 7 in {3, 5, 6}:
return False ## Not 1,2,4 mod 7
if n % 9 in {2,3,5,6,8}:
return False
if n % 13 in {2,5,6,7,8,11}:
return False
## Other patterns
if c == 5: ## if it ends in a 5
if (n//10)%10 != 2:
return False ## then it must end in 25
if (n//100)%10 not in {0,2,6}:
return False ## and in 025, 225, or 625
if (n//100)%10 == 6:
if (n//1000)%10 not in {0,5}:
return False ## that is, 0625 or 5625
else:
if (n//10)%4 != 0:
return False ## (4k)*10 + (1,9)
## Babylonian Algorithm. Finding the integer square root.
## Root extraction.
s = (len(str(n))-1) // 2
x = (10**s) * 4
A = {x, n}
while x * x != n:
x = (x + (n // x)) >> 1
if x in A:
return False
A.add(x)
return True
Assez simple. Premièrement, il vérifie que nous avons un entier et un positif en plus. Sinon, cela ne sert à rien. Il laisse 0 passer comme vrai (nécessaire ou sinon le bloc suivant est une boucle infinie).
Le bloc de code suivant supprime systématiquement les puissances de 4 dans un sous-algorithme très rapide utilisant des opérations de décalage de bits et de logique de bits. Nous ne trouvons pas finalement l’isSquare de notre n original mais d’un k <n qui a été réduit par des puissances de 4, si possible. Cela réduit la taille du nombre avec lequel nous travaillons et accélère réellement la méthode babylonienne, mais rend également les autres vérifications plus rapides.
Le troisième bloc de code effectue un simple test de logique binaire booléenne. Les trois chiffres les moins significatifs, en binaire, de tout carré parfait sont 001. Toujours. Sauf les zéros non significatifs résultant de puissances de 4, de toute façon, qui ont déjà été pris en compte. Si le test échoue, vous savez immédiatement que ce n'est pas un carré. Si cela passe, vous ne pouvez pas être sûr.
De plus, si nous obtenons un 1 pour une valeur de test, le numéro de test était à l'origine une puissance de 4, incluant peut-être 1 lui-même.
Comme le troisième bloc, le quatrième teste la valeur de position un en décimale à l'aide d'un simple opérateur de module, et a tendance à détecter les valeurs qui passent à travers le test précédent. Également un test mod 7, mod 8, mod 9 et mod 13.
Le cinquième bloc de code vérifie certains des motifs carrés parfaits bien connus. Les nombres se terminant par 1 ou 9 sont précédés d'un multiple de quatre. Et les numéros se terminant par 5 doivent se terminer par 5625, 0625, 225 ou 025. J'en ai inclus d'autres, mais j'ai réalisé qu'ils étaient redondants ou jamais utilisés.
Enfin, le sixième bloc de code ressemble beaucoup à la réponse du principal intervenant - Alex Martelli -. Trouve fondamentalement la racine carrée en utilisant l'ancien algorithme babylonien, mais en la limitant à des valeurs entières tout en ignorant les virgules flottantes. Fait à la fois pour la vitesse et l'extension des valeurs testables. J'ai utilisé des ensembles au lieu de listes parce que cela prend beaucoup moins de temps, j'ai utilisé des décalages de bits au lieu de la division par deux et j'ai intelligemment choisi une valeur de départ initiale beaucoup plus efficacement.
A propos, j’ai testé le numéro de test recommandé par Alex Martelli, ainsi que quelques chiffres beaucoup plus gros, tels que:
x=1000199838770766116385386300483414671297203029840113913153824086810909168246772838680374612768821282446322068401699727842499994541063844393713189701844134801239504543830737724442006577672181059194558045164589783791764790043104263404683317158624270845302200548606715007310112016456397357027095564872551184907513312382763025454118825703090010401842892088063527451562032322039937924274426211671442740679624285180817682659081248396873230975882215128049713559849427311798959652681930663843994067353808298002406164092996533923220683447265882968239141724624870704231013642255563984374257471112743917655991279898690480703935007493906644744151022265929975993911186879561257100479593516979735117799410600147341193819147290056586421994333004992422258618475766549646258761885662783430625 ** 2
for i in range(x, x+2):
print(i, isSquare(i))
imprimé les résultats suivants:
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890625 True
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890626 False
Et il l'a fait en 0,33 seconde.
À mon avis, mon algorithme fonctionne de la même façon que celui d’Alex Martelli, avec tous les avantages qui en découlent, mais présente un avantage supplémentaire: des rejets de test simple très efficaces qui permettent de gagner beaucoup de temps, sans parler de la réduction de la taille du nombre de tests par des puissances de 4, ce qui améliore la vitesse, l'efficacité, la précision et la taille des nombres pouvant être testés. Probablement particulièrement vrai dans les implémentations non-Python.
Environ 99% de tous les entiers sont rejetés comme non-carrés avant que l'extraction de la racine babylonienne ne soit même mise en œuvre, et dans les deux tiers du temps qu'il faudrait au Babylonien pour rejeter l'entier. Et bien que ces tests n'accélèrent pas le processus de manière significative, la réduction de tous les nombres de tests à un nombre impair en divisant tous les pouvoirs de 4 vraiment accélère le test babylonien.
J'ai fait un test de comparaison de temps. J'ai testé tous les nombres entiers de 1 à 10 millions successivement. En utilisant uniquement la méthode babylonienne en elle-même (avec mon estimation initiale spécialement adaptée), il a fallu en moyenne à ma Surface 3 165 secondes (avec une précision de 100%). En utilisant uniquement les tests logiques de mon algorithme (à l’exception du Babylonien), cela a pris 127 secondes, il a rejeté 99% de tous les entiers comme étant non-Square sans rejeter par erreur les carrés parfaits. Parmi les nombres entiers qui ont passé, seuls 3% étaient des carrés parfaits (une densité beaucoup plus élevée). En utilisant l'algorithme complet ci-dessus qui utilise à la fois les tests logiques et l'extraction de racine babylonienne, nous avons une précision de 100% et l'achèvement du test en seulement 14 secondes. Les 100 premiers millions d’entiers prennent environ 2 minutes 45 secondes à tester.
EDIT: J'ai été en mesure de réduire le temps plus loin. Je peux maintenant tester les nombres entiers de 0 à 100 millions en 1 minute 40 secondes. Nous perdons beaucoup de temps à vérifier le type de données et la positivité. Éliminez les deux premières vérifications et j'ai réduit l'expérience d'une minute. Il faut supposer que l'utilisateur est suffisamment intelligent pour savoir que les négatifs et les flottants ne sont pas des carrés parfaits.
Je suis nouveau sur Stack Overflow et j'ai rapidement parcouru le terrain pour trouver une solution. Je viens de poster une légère variation sur certains des exemples ci-dessus sur un autre fil ( Recherche de carrés parfaits ) et j'ai pensé inclure une légère variation de ce que j'ai posté ici (en utilisant nsqrt en tant que variable temporaire), au cas où c'est d'intérêt/utilisation:
import math
def is_perfect_square(n):
if not ( isinstance(n, (int, long)) and ( n >= 0 ) ):
return False
else:
nsqrt = math.sqrt(n)
return nsqrt == math.trunc(nsqrt)
Ceci peut être résolu en utilisant le module decimal
pour obtenir des racines carrées de précision arbitraires et des vérifications simples de "l'exactitude":
import math
from decimal import localcontext, Context, Inexact
def is_perfect_square(x):
# If you want to allow negative squares, then set x = abs(x) instead
if x < 0:
return False
# Create localized, default context so flags and traps unset
with localcontext(Context()) as ctx:
# Set a precision sufficient to represent x exactly; `x or 1` avoids
# math domain error for log10 when x is 0
ctx.prec = math.ceil(math.log10(x or 1)) + 1 # Wrap ceil call in int() on Py2
# Compute integer square root; don't even store result, just setting flags
ctx.sqrt(x).to_integral_exact()
# If previous line couldn't represent square root as exact int, sets Inexact flag
return not ctx.flags[Inexact]
Pour une démonstration avec des valeurs vraiment énormes:
# I just kept mashing the numpad for awhile :-)
>>> base = 100009991439393999999393939398348438492389402490289028439083249803434098349083490340934903498034098390834980349083490384903843908309390282930823940230932490340983098349032098324908324098339779438974879480379380439748093874970843479280329708324970832497804329783429874329873429870234987234978034297804329782349783249873249870234987034298703249780349783497832497823497823497803429780324
>>> sqr = base ** 2
>>> sqr ** 0.5 # Too large to use floating point math
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: int too large to convert to float
>>> is_perfect_power(sqr)
True
>>> is_perfect_power(sqr-1)
False
>>> is_perfect_power(sqr+1)
False
Si vous augmentez la taille de la valeur testée, cela devient finalement assez lent (cela prend près d'une seconde pour un carré de 200 000 bits), mais pour des nombres plus modérés (par exemple, 20 000 bits), c'est toujours plus rapide qu'un humain ne le remarquerait valeurs individuelles (~ 33 ms sur ma machine). Mais comme la vitesse n'était pas votre principale préoccupation, c'est un bon moyen de le faire avec les bibliothèques standard de Python.
Bien sûr, il serait beaucoup plus rapide d’utiliser gmpy2
et de simplement tester gmpy2.mpz(x).is_square()
, mais si les packages tiers ne vous conviennent pas, la procédure ci-dessus fonctionne assez bien.
Ma réponse serait:
def checkSquare(x):return x**.5%1==0
Cela correspond essentiellement à une racine carrée, puis modulo de 1 pour supprimer la partie entière et si le résultat est 0, renvoyer True
sinon renvoyer False
. Dans ce cas, x peut être un nombre important, mais pas aussi grand que le nombre flottant maximal que python peut gérer: 1.7976931348623157e + 308
Vous pouvez effectuer une recherche binaire pour la racine carrée arrondie. Ajustez le résultat pour voir s'il correspond à la valeur d'origine.
Vous êtes probablement mieux avec la réponse FogleBirds - mais attention, l'arithmétique en virgule flottante est approximative, ce qui peut gâcher cette approche. Vous pouvez en principe obtenir un faux positif à partir d'un grand entier qui est un plus qu'un carré parfait, par exemple, en raison de la perte de précision.
C'est ma méthode
int(n**0.5)**2 == int(n)
prenez la racine carrée du nombre converti en entier, puis le carré si les nombres sont égaux, alors c'est un carré parfait, sinon pas.
Cette réponse ne concerne pas votre question, mais une question implicite que je vois dans le code que vous avez posté, à savoir "comment vérifier si quelque chose est un entier?"
La première réponse que vous obtiendrez généralement à cette question est "ne pas!" Et il est vrai qu’en Python, la vérification de type n’est généralement pas la bonne chose à faire.
Pour ces rares exceptions, toutefois, au lieu de rechercher un point décimal dans la représentation sous forme de chaîne du nombre, vous devez utiliser la fonction isinstance :
>>> isinstance(5,int)
True
>>> isinstance(5.0,int)
False
Bien entendu, cela s'applique à la variable plutôt qu'à une valeur. Si je voulais déterminer si la valeur était un entier, je procéderais ainsi:
>>> x=5.0
>>> round(x) == x
True
Mais comme tout le monde l’a couvert en détail, la plupart des exemples non imaginaires de ce type de problèmes doivent être pris en compte.
Il s’agit d’une solution aussi naïve que possible. Cela fonctionne pour les petits nombres.
def is_perfect_square(n):
return (n ** .5).is_integer()
Il est évident qu’il échoue pour un grand nombre tel que 152415789666209426002111556165263283035677490.
Si vous souhaitez effectuer une boucle sur une plage et faire quelque chose pour chaque nombre qui n'est PAS un carré parfait, vous pouvez procéder comme suit:
def non_squares(upper):
next_square = 0
diff = 1
for i in range(0, upper):
if i == next_square:
next_square += diff
diff += 2
continue
yield i
Si vous voulez faire quelque chose pour chaque nombre qui IS est un carré parfait, le générateur est encore plus simple:
(n * n for n in range(upper))
Je pense que cela fonctionne et est très simple:
from math import sqrt
def is_perfect_square(num):
return int(sqrt(num)) == sqrt(num)
a = math.sqrt(n)
b = int(a)
a == b
Je ne suis pas sûr du Python, mais vous pourriez faire quelque chose comme:
function isSquare(x) = x == floor(sqrt(x) + 0.5)^2
En d’autres termes, prenez un nombre, recherchez la racine carrée, arrondissez-le au nombre entier le plus proche, mettez-le au carré et testez s’il est identique au nombre initial. (floor
et l'ajout de 0.5
est fait pour éviter des cas tels que sqrt(4)
renvoyant 1.9999999...
en raison de calculs en virgule flottante, comme l'a souligné Mike Graham.)
Si vous êtes intéressé, il y avait déjà eu une très bonne discussion sur le moyen le plus rapide de déterminer si la racine carrée d’un entier est un entier }.
Edité pour clarification.