Come posso verificare se un numero è un quadrato perfetto?
La velocità non interessa, per ora, funziona solo.
Risposte:
Il problema quando si fa affidamento su qualsiasi calcolo in virgola mobile ( math.sqrt(x)
, o x**0.5
) è che non si può essere veramente sicuri che sia esatto (per interi sufficientemente grandi x
, non lo sarà e potrebbe persino traboccare). Fortunatamente (se non si ha fretta ;-) ci sono molti approcci interi puri, come il seguente ...:
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)
Suggerimento: si basa sull '"algoritmo babilonese" per la radice quadrata, vedere wikipedia . E lo fa per qualsiasi numero positivo per il quale si dispone di memoria sufficiente per il calcolo di procedere al completamento ;-).
Modifica : vediamo un esempio ...
x = 12345678987654321234567 ** 2
for i in range(x, x+2):
print i, is_square(i)
questo stampa, come desiderato (e anche in un ragionevole lasso di tempo ;-):
152415789666209426002111556165263283035677489 True
152415789666209426002111556165263283035677490 False
Per favore, prima di proporre soluzioni basate su risultati intermedi in virgola mobile, assicurati che funzionino correttamente su questo semplice esempio - non è così difficile (hai solo bisogno di alcuni controlli extra nel caso in cui sqrt calcolato sia un po 'spento), richiede solo un un po 'di cura.
E poi prova x**7
e trova un modo intelligente per aggirare il problema che otterrai,
OverflowError: long int too large to convert to float
ovviamente dovrai diventare sempre più intelligente man mano che i numeri continuano a crescere.
Se io ero di fretta, naturalmente, mi piacerebbe utilizzare gmpy - ma poi, sto chiaramente di parte ;-).
>>> import gmpy
>>> gmpy.is_square(x**7)
1
>>> gmpy.is_square(x**7 + 1)
0
Sì, lo so, è così facile che sembra di barare (un po 'come mi sento nei confronti di Python in generale ;-) - nessuna intelligenza, solo perfetta immediatezza e semplicità (e, nel caso di gmpy, pura velocità ; -) ...
set([x])
={x}
set
ipotesi? Il babilonese non converge semplicemente a int(sqrt(x))
, dove dobbiamo solo controllare se prev != next
?
Usa il metodo di Newton per azzerare rapidamente la radice quadrata intera più vicina, quindi quadrare e vedere se è il tuo numero. Vedi isqrt .
Python ≥ 3.8 ha math.isqrt
. Se si utilizza una versione precedente di Python, cercare " def isqrt(n)
" l'implementazione qui .
import math
def is_square(i: int) -> bool:
return i == math.isqrt(i) ** 2
Poiché non si può mai fare affidamento su confronti esatti quando si ha a che fare con calcoli in virgola mobile (come questi modi di calcolare la radice quadrata), un'implementazione meno soggetta a errori sarebbe
import math
def is_square(integer):
root = math.sqrt(integer)
return integer == int(root + 0.5) ** 2
Immaginate integer
IS 9
. math.sqrt(9)
potrebbe essere 3.0
, ma potrebbe anche essere qualcosa come 2.99999
o 3.00001
, quindi quadrare immediatamente il risultato non è affidabile. Sapendo che int
prende il valore floor, aumentare il valore float per 0.5
prima cosa significa che otterremo il valore che stiamo cercando se siamo in un intervallo in cui float
ha ancora una risoluzione abbastanza fine da rappresentare i numeri vicini a quello che stiamo cercando .
if int(root + 0.5) ** 2 == integer:
se int
agisce come floor
per i numeri a cui teniamo.
math.sqrt(9)
davvero esserlo 2.99999
? Python è float
mappato a C double
, ma penso che anche un tipo FP a 16 bit abbia più precisione di quello, quindi forse se avessi un compilatore C che utilizza FP a 8 bit ("minifloats") come double
tipo? Suppongo che sia tecnicamente possibile, ma mi sembra improbabile che sia così su qualsiasi computer che esegue Python oggi.
math.sqrt(9)
tornerà 2.99999
su un sistema particolare, ma il risultato effettivo dipende dal sistema e non ci si può aspettare che sia esatto.
Se sei interessato, ho una risposta matematica pura a una domanda simile in math stackexchange, "Rilevamento dei quadrati perfetti più velocemente rispetto all'estrazione della radice quadrata" .
La mia implementazione di isSquare (n) potrebbe non essere la migliore, ma mi piace. Mi ci sono voluti diversi mesi di studio in teoria matematica, calcolo digitale e programmazione Python, confrontandomi con altri collaboratori, ecc., Per fare davvero clic con questo metodo. Mi piace però la sua semplicità ed efficienza. Non ho visto di meglio. Dimmi cosa ne pensi.
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
Abbastanza diretto. Innanzitutto controlla che abbiamo un numero intero e uno positivo. Altrimenti non ha senso. Lascia che 0 passi come True (necessario altrimenti il blocco successivo è un ciclo infinito).
Il successivo blocco di codice rimuove sistematicamente le potenze di 4 in un subalgoritmo molto veloce usando lo spostamento di bit e le operazioni logiche di bit. Alla fine non stiamo trovando isSquare del nostro n originale ma di un k <n che è stato ridimensionato da potenze di 4, se possibile. Questo riduce la dimensione del numero con cui stiamo lavorando e velocizza davvero il metodo babilonese, ma rende anche più veloci anche altri controlli.
Il terzo blocco di codice esegue un semplice test booleano di logica bit. Le tre cifre meno significative, in binarie, di ogni quadrato perfetto sono 001. Sempre. Ad ogni modo, salvo per gli zeri iniziali risultanti da potenze di 4, che sono già stati contabilizzati. Se fallisce il test, sai subito che non è un quadrato. Se passa, non puoi esserne sicuro.
Inoltre, se finiamo con un 1 per un valore di test, il numero di test era originariamente un potere di 4, incluso forse 1 stesso.
Come il terzo blocco, il quarto verifica il valore di una posizione in decimale utilizzando l'operatore modulo semplice e tende a catturare i valori che scivolano attraverso il test precedente. Anche un test mod 7, mod 8, mod 9 e mod 13.
Il quinto blocco di codice controlla alcuni dei ben noti schemi di quadrati perfetti. I numeri che terminano con 1 o 9 sono preceduti da un multiplo di quattro. E i numeri che terminano con 5 devono terminare con 5625, 0625, 225 o 025. Ne avevo inclusi altri ma mi resi conto che erano ridondanti o non venivano mai utilizzati.
Infine, il sesto blocco di codice assomiglia molto a quello che è il miglior risponditore - Alex Martelli -. Fondamentalmente trova la radice quadrata usando l'antico algoritmo babilonese, ma limitandola a valori interi ignorando il virgola mobile. Fatto sia per la velocità che per estendere le grandezze di valori verificabili. Ho usato set invece di elenchi perché ci vuole molto meno tempo, ho usato turni di bit invece della divisione per due e ho scelto in modo intelligente un valore iniziale iniziale molto più efficiente.
A proposito, ho testato il numero di test consigliato da Alex Martelli, così come alcuni numeri di grandezza di molti ordini più grandi, come:
x=1000199838770766116385386300483414671297203029840113913153824086810909168246772838680374612768821282446322068401699727842499994541063844393713189701844134801239504543830737724442006577672181059194558045164589783791764790043104263404683317158624270845302200548606715007310112016456397357027095564872551184907513312382763025454118825703090010401842892088063527451562032322039937924274426211671442740679624285180817682659081248396873230975882215128049713559849427311798959652681930663843994067353808298002406164092996533923220683447265882968239141724624870704231013642255563984374257471112743917655991279898690480703935007493906644744151022265929975993911186879561257100479593516979735117799410600147341193819147290056586421994333004992422258618475766549646258761885662783430625 ** 2
for i in range(x, x+2):
print(i, isSquare(i))
ha stampato i seguenti risultati:
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890625 True
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890626 False
E lo ha fatto in 0,33 secondi.
A mio parere, il mio algoritmo funziona allo stesso modo di quello di Alex Martelli, con tutti i suoi vantaggi, ma ha l'ulteriore vantaggio di rifiutare i semplici test altamente efficienti che fanno risparmiare molto tempo, per non parlare della riduzione delle dimensioni dei numeri di test da parte dei poteri di 4, che migliora la velocità, l'efficienza, la precisione e la dimensione dei numeri testabili. Probabilmente è particolarmente vero nelle implementazioni non Python.
Circa il 99% di tutti i numeri interi viene rifiutato come non quadrato prima ancora che venga implementata l'estrazione della radice babilonese, e in 2/3 del tempo che impiegherebbe il babilonese a rifiutare l'intero. E sebbene questi test non accelerino il processo in modo così significativo, la riduzione di tutti i numeri di test a un dispari dividendo tutte le potenze di 4 accelera davvero il test babilonese.
Ho fatto un test di confronto temporale. Ho testato tutti i numeri interi da 1 a 10 milioni in successione. Usando solo il metodo babilonese da solo (con la mia ipotesi iniziale su misura), il mio Surface 3 ha impiegato in media 165 secondi (con una precisione del 100%). Usando solo i test logici nel mio algoritmo (escluso il babilonese), ci sono voluti 127 secondi, ha rifiutato il 99% di tutti i numeri interi come non quadrati senza rifiutare erroneamente alcun quadrato perfetto. Di quei numeri interi passati, solo il 3% erano quadrati perfetti (una densità molto più alta). Utilizzando l'algoritmo completo di cui sopra che impiega sia i test logici che l'estrazione della radice babilonese, abbiamo una precisione del 100% e il completamento del test in soli 14 secondi. I primi 100 milioni di numeri interi richiedono circa 2 minuti e 45 secondi per essere testati.
EDIT: sono stato in grado di ridurre ulteriormente il tempo. Ora posso testare i numeri interi da 0 a 100 milioni in 1 minuto e 40 secondi. Si spreca molto tempo per controllare il tipo di dati e la positività. Elimino i primi due controlli e abbasso l'esperimento di un minuto. Si deve presumere che l'utente sia abbastanza intelligente da sapere che i negativi e i float non sono quadrati perfetti.
import math
def is_square(n):
sqrt = math.sqrt(n)
return (sqrt - int(sqrt)) == 0
Un quadrato perfetto è un numero che può essere espresso come il prodotto di due numeri interi uguali. math.sqrt(number)
restituire a float
. int(math.sqrt(number))
trasmette il risultato aint
.
Se la radice quadrata è un numero intero, come 3, ad esempio, math.sqrt(number) - int(math.sqrt(number))
sarà 0 e l' if
istruzione sarà False
. Se la radice quadrata fosse un numero reale come 3.2, allora lo saràTrue
e stampato "non è un quadrato perfetto".
Non riesce per un grande non quadrato come 152415789666209426002111556165263283035677490.
if (math.sqrt(number)-int(math.sqrt(number))):
per a=math.sqrt(number)
poi un'altra linea per: if a-int(a):
. Questo perché deve calcolare la radice quadrata solo una volta, che imo per grande n è significativo
La mia risposta è:
def is_square(x):
return x**.5 % 1 == 0
Fondamentalmente fa una radice quadrata, quindi modulo per 1 per rimuovere la parte intera e se il risultato è 0 ritorna True
altrimenti ritornaFalse
. In questo caso x può essere un numero qualsiasi, ma non tanto quanto il numero float massimo che Python può gestire: 1.7976931348623157e + 308
Non è corretto per un grande non quadrato come 152415789666209426002111556165263283035677490.
Questo può essere risolto utilizzando il decimal
modulo per ottenere radici quadrate di precisione arbitraria e facili controlli di "esattezza":
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]
Per dimostrazioni con valori davvero enormi:
# 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
Se aumenti la dimensione del valore testato, questo alla fine diventa piuttosto lento (impiega quasi un secondo per un quadrato di 200.000 bit), ma per numeri più moderati (diciamo 20.000 bit), è ancora più veloce di quanto un essere umano noterebbe per valori individuali (~ 33 ms sulla mia macchina). Ma poiché la velocità non era la tua preoccupazione principale, questo è un buon modo per farlo con le librerie standard di Python.
Certo, sarebbe molto più veloce da usare gmpy2
e da testare gmpy2.mpz(x).is_square()
, ma se i pacchetti di terze parti non fanno per te, quanto sopra funziona abbastanza bene.
Ho appena pubblicato una leggera variazione su alcuni degli esempi sopra su un altro thread ( Trovare quadrati perfetti ) e ho pensato di includere una leggera variazione di ciò che ho pubblicato qui (usando nsqrt come variabile temporanea), nel caso sia di interesse / uso:
import math
def is_square(n):
if not (isinstance(n, int) and (n >= 0)):
return False
else:
nsqrt = math.sqrt(n)
return nsqrt == math.trunc(nsqrt)
Non è corretto per un grande non quadrato come 152415789666209426002111556165263283035677490.
Questo è il mio metodo:
def is_square(n) -> bool:
return int(n**0.5)**2 == int(n)
Prendi la radice quadrata del numero. Converti in numero intero. Prendi la piazza. Se i numeri sono uguali, allora è un quadrato perfetto, altrimenti no.
Non è corretto per una piazza grande come 152415789666209426002111556165263283035677489.
È possibile eseguire una ricerca binaria per la radice quadrata arrotondata. Piazza il risultato per vedere se corrisponde al valore originale.
Probabilmente stai meglio con la risposta di FogleBirds, anche se fai attenzione, poiché l'aritmetica in virgola mobile è approssimativa, il che può annullare questo approccio. In linea di principio, potresti ottenere un falso positivo da un intero grande che è uno in più di un quadrato perfetto, ad esempio, a causa della perdita di precisione.
Se il modulo (resto) rimanente dalla divisione per la radice quadrata è 0, allora è un quadrato perfetto.
def is_square(num: int) -> bool:
return num % math.sqrt(num) == 0
L'ho confrontato con un elenco di quadrati perfetti fino a 1000.
Questa risposta non riguarda la tua domanda dichiarata, ma una domanda implicita che vedo nel codice che hai pubblicato, cioè "come verificare se qualcosa è un numero intero?"
La prima risposta che generalmente riceverai a questa domanda è "Non farlo!" Ed è vero che in Python, il controllo del tipo di solito non è la cosa giusta da fare.
Per quelle rare eccezioni, però, invece di cercare un punto decimale nella rappresentazione di stringa del numero, la cosa da fare è usare la funzione isinstance :
>>> isinstance(5,int)
True
>>> isinstance(5.0,int)
False
Ovviamente questo si applica alla variabile piuttosto che a un valore. Se volessi determinare se il valore era un numero intero, lo farei:
>>> x=5.0
>>> round(x) == x
True
Ma come tutti gli altri hanno trattato in dettaglio, ci sono problemi in virgola mobile da considerare nella maggior parte degli esempi non giocattolo di questo genere di cose.
Se vuoi eseguire il ciclo su un intervallo e fare qualcosa per ogni numero che NON è un quadrato perfetto, potresti fare qualcosa del genere:
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
Se vuoi fare qualcosa per ogni numero che È un quadrato perfetto, il generatore è ancora più semplice:
(n * n for n in range(upper))
Penso che funzioni ed è molto semplice:
import math
def is_square(num):
sqrt = math.sqrt(num)
return sqrt == int(sqrt)
Non è corretto per un grande non quadrato come 152415789666209426002111556165263283035677490.
set
Quando x in seen
è True
:
x
sequenza di 511, 256, 129, 68, 41, 32, 31 , 31 ;Quindi, è sufficiente fermarsi non appena la corrente x
è maggiore o uguale a quella precedente:
def is_square(n):
assert n > 1
previous = n
x = n // 2
while x * x != n:
x = (x + (n // x)) // 2
if x >= previous:
return False
previous = x
return True
x = 12345678987654321234567 ** 2
assert not is_square(x-1)
assert is_square(x)
assert not is_square(x+1)
Equivalenza con l'algoritmo originale testato per 1 <n <10 ** 7. Nello stesso intervallo, questa variante leggermente più semplice è circa 1,4 volte più veloce.
a=int(input('enter any number'))
flag=0
for i in range(1,a):
if a==i*i:
print(a,'is perfect square number')
flag=1
break
if flag==1:
pass
else:
print(a,'is not perfect square number')
L'idea è di eseguire un ciclo da i = 1 a floor (sqrt (n)) quindi verificare se il quadrato fa n.
bool isPerfectSquare(int n)
{
for (int i = 1; i * i <= n; i++) {
// If (i * i = n)
if ((n % i == 0) && (n / i == i)) {
return true;
}
}
return false;
}
import math
def is_square(n):
sqrt = math.sqrt(n)
return sqrt == int(sqrt)
Non riesce per un grande non quadrato come 152415789666209426002111556165263283035677490.