2008-12-05 5 views
60

Possible en double:
Fastest way to determine if an integer's square root is an integerQu'est-ce qu'un bon algorithme pour déterminer si une entrée est un carré parfait?

Qu'est-ce qu'un moyen de voir si un nombre est un perfect square?

bool IsPerfectSquare(long input) 
{ 
    // TODO 
} 

J'utilise C# mais c'est la langue agnostique. Bonus points pour la clarté et la simplicité (ceci n'est pas destiné à être le code-golf).


Edit: cela a beaucoup plus complexe que prévu! Il s'avère que les problèmes de double précision se manifestent de deux manières. Premièrement, Math.Sqrt prend un double qui ne peut pas tenir longtemps (merci Jon). Deuxièmement, la précision d'un double va perdre de petites valeurs (.000 ... 00001) lorsque vous avez un énorme carré presque parfait. par exemple, ma mise en œuvre a échoué à ce test pour Math.Pow (10,18) +1 (la mienne a déclaré true).

+0

Vous pouvez également google pour la méthode 'lsqrt' utilisée pour la racine carrée entière. – leppie

+0

Michael, Bill le Lézard a fait un bon point que c'est juste une question similaire, pas le doublon exact. Je ne pense pas que la question doive être close. Outre le problème du carré parfait est beaucoup plus complexe en termes pratiques que cela puisse paraître et les réponses ici apportent une grande contribution. –

+0

Pour la solution que vous choisissez, n'oubliez pas d'effectuer un contrôle rapide de négativité. – angus

Répondre

98
bool IsPerfectSquare(long input) 
{ 
    long closestRoot = (long) Math.Sqrt(input); 
    return input == closestRoot * closestRoot; 
} 

Cela peut se éloigner de certains des problèmes de juste vérifier « est la racine carrée d'un nombre entier » mais peut-être pas tout. Vous avez besoin potentiellement d'obtenir un peu plus funky:

bool IsPerfectSquare(long input) 
{ 
    double root = Math.Sqrt(input); 

    long rootBits = BitConverter.DoubleToInt64Bits(root); 
    long lowerBound = (long) BitConverter.Int64BitsToDouble(rootBits-1); 
    long upperBound = (long) BitConverter.Int64BitsToDouble(rootBits+1); 

    for (long candidate = lowerBound; candidate <= upperBound; candidate++) 
    { 
     if (candidate * candidate == input) 
     { 
      return true; 
     } 
    } 
    return false; 
} 

Icky, et inutile pour autre chose que des valeurs très grandes, mais je pense qu'il devrait travail ...

+0

On dirait que vous étiez plus rapide que moi ;-) Oups - votre solution est également meilleure, parce que «closestRoot» est un nom beaucoup plus précis. – Treb

+21

Aime le nombre de upvotes que j'ai eu * avant * de le corriger pour une solution plus à l'épreuve des balles;) –

+168

mec, vous êtes jon skeet. – Epaga

10
bool IsPerfectSquare(long input) 
{ 
    long SquareRoot = (long) Math.Sqrt(input); 
    return ((SquareRoot * SquareRoot) == input); 
} 
9

En Common Lisp, j'utilise ce qui suit:

(defun perfect-square-p (n) 
    (= (expt (isqrt n) 2) 
    n)) 
+0

Heh. En regardant http://stackoverflow.com/a/343862/31615, je devrais ajouter que Common Lisp a une pile de nombres "complète", donc ceci fonctionne juste pour n'importe quel nombre entier non négatif (limité par la mémoire de travail, bien sûr) . – Svante

+0

Sur SBCL, 'square' devrait être' expt': '(defun perfect-square-p (n) (= (expt (isqrt n) 2) n))'. – Ben

+0

@BenSima: En fait, puisque 'square' n'est pas défini dans la norme, vous devez le définir (ou le remplacer) dans n'importe quelle implémentation. Je modifie la réponse pour ne pas avoir une telle dépendance. – Svante