web-dev-qa-db-ja.com

数値が完全な正方形であるかどうかを確認します

数値が完全な正方形であるかどうかを確認するにはどうすればよいですか?

今のところ、速度は問題ではありません。

66
delete

浮動小数点計算(math.sqrt(x)、またはx**0.5)は、正確であることを実際に確認できないことです(十分に大きい整数xの場合、そうではなく、オーバーフローする可能性もあります)。幸いにも(急いでいない場合;-)以下のような多くの純粋な整数アプローチがあります...:

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)

ヒント:平方根の「バビロニアアルゴリズム」に基づいています。 wikipedia を参照してください。 doesは、計算を完了するために十分なメモリがある正の数に対して機能します;-)。

Edit:例を見てみましょう...

x = 12345678987654321234567 ** 2

for i in range(x, x+2):
   print i, is_square(i)

これは、必要に応じて(および妥当な時間内に)印刷します;-):

152415789666209426002111556165263283035677489 True
152415789666209426002111556165263283035677490 False

浮動小数点の中間結果に基づいて解決策を提案する前に、この単純な例で正しく動作することを確認してください-that難しくありません計算されたsqrtが少しずれている場合のいくつかの追加チェック)、少し注意を払っています。

そして、x**7そして、あなたが得る問題を回避する賢い方法を見つけてください。

OverflowError: long int too large to convert to float

もちろん、数が増え続けるにつれて、ますます賢くなる必要があります。

が急いでだった場合、もちろん gmpy -を使用しますが、その後、明らかに偏っています;-)。

>>> import gmpy
>>> gmpy.is_square(x**7)
1
>>> gmpy.is_square(x**7 + 1)
0

ええ、それはごまかしのように感じるのでとても簡単です(Python一般的に;-)に感じる感じ方が少し)-まったく賢くなく、完璧な直接性とシンプルさ(そして、gmpyの場合、まったくの速度;-)...

106
Alex Martelli

ニュートンの方法を使用して、最も近い整数の平方根をすばやくゼロに合わせ、それを平方して、それが自分の数値かどうかを確認します。 isqrt を参照してください。

Python≥3.8には _math.isqrt_ があります。古いバージョンのPythonを使用している場合は、「def isqrt(n)」実装を探してください here

_import math

def is_square(i: int) -> bool:
    return i == math.isqrt(i) ** 2
_
29
James K Polk

浮動小数点計算(平方根を計算するこれらの方法など)を処理する場合、正確な比較に依存することはできないため、エラーが発生しにくい実装は次のようになります。

_import math

def is_square(integer):
    root = math.sqrt(integer)
    return integer == int(root + 0.5) ** 2
_

integerが_9_であると想像してください。 math.sqrt(9)は_3.0_である可能性がありますが、_2.99999_または_3.00001_のようなものである可能性もあるため、結果をすぐに二乗することは信頼できません。 intがフロア値を取ることを知って、最初に_0.5_だけfloat値を増やすことは、floatがまだある範囲にある場合、探している値を取得することを意味します探している数値に近い数値を表すのに十分な解像度。

15
Mike Graham
_import math

def is_square(n):
    sqrt = math.sqrt(n)
    return (sqrt - int(sqrt)) == 0
_

完全な正方形とは、2つの等しい整数の積として表現できる数値です。 math.sqrt(number)floatを返します。 int(math.sqrt(number))は、結果をintにキャストします。

平方根が3などの整数の場合、math.sqrt(number) - int(math.sqrt(number))は0になり、ifステートメントはFalseになります。平方根が3.2のような実数である場合、それはTrueとなり、「完全な正方形ではない」と表示されます。

失敗する 152415789666209426002111556165263283035677490のような大きな非正方形。

11
0xPwn

あなたが興味を持っているなら、私は math stackexchange、「平方根を抽出するよりも速く完全な正方形を検出する」 で同様の質問に対する純粋な数学応答を持っています。

IsSquare(n)の私自身の実装は最良ではないかもしれませんが、私はそれが好きです。数学理論、デジタル計算、およびpythonプログラミング、他の貢献者との比較など)の研究に数か月かかって、この方法を実際にクリックしました。そのシンプルさと効率が気に入っています。良く見えませんでした。

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

かなり簡単です。最初に、整数があり、その整数が正であることを確認します。それ以外の場合は意味がありません。 0をTrueとしてスリップさせます(必要または次のブロックは無限ループです)。

次のコードブロックは、ビットシフトおよびビットロジック演算を使用して、非常に高速なサブアルゴリズムで4のべき乗を体系的に削除します。最終的には、元のnのisSquareではなく、可能であれば4の累乗で縮小されたk <nのisSquareを見つけています。これにより、作業対象の数値のサイズが小さくなり、バビロニア方式が実際に高速化されますが、他のチェックも高速になります。

コードの3番目のブロックは、単純なブールビット論理テストを実行します。完全な正方形の最下位3桁(バイナリ)は001です。常に。とにかく、すでに説明されている4の累乗から生じる先行ゼロを節約します。テストに失敗した場合、正方形ではないことがすぐにわかります。合格した場合、あなたは確信できません。

また、テスト値が1になった場合、テスト番号はもともと4のべき乗でしたが、おそらく1自体を含んでいました。

3番目のブロックと同様に、4番目は単純なモジュラス演算子を使用して10進数の1桁の値をテストし、前のテストをすり抜ける値をキャッチする傾向があります。また、mod 7、mod 8、mod 9、およびmod 13のテスト。

コードの5番目のブロックは、いくつかの有名な完全な正方形パターンをチェックします。 1または9で終わる番号の前には、4の倍数が付きます。そして、5で終わる番号は、5625、0625、225、または025で終わる必要があります。他のものも含めましたが、それらは冗長であるか、実際には使用されていません。

最後に、コードの6番目のブロックは、トップアンサーのAlex Martelliの答えと非常によく似ています。基本的に古代バビロニアアルゴリズムを使用して平方根を見つけますが、浮動小数点を無視して整数値に制限します。速度とテスト可能な値の大きさを拡張するために行われます。リストの代わりにセットを使用したのは、時間がかからないため、2で割る代わりにビットシフトを使用し、初期開始値をより効率的に賢く選択したためです。

ところで、Alex Martelliが推奨するテスト番号と、次のような数桁大きい数個のテストを行いました。

x=1000199838770766116385386300483414671297203029840113913153824086810909168246772838680374612768821282446322068401699727842499994541063844393713189701844134801239504543830737724442006577672181059194558045164589783791764790043104263404683317158624270845302200548606715007310112016456397357027095564872551184907513312382763025454118825703090010401842892088063527451562032322039937924274426211671442740679624285180817682659081248396873230975882215128049713559849427311798959652681930663843994067353808298002406164092996533923220683447265882968239141724624870704231013642255563984374257471112743917655991279898690480703935007493906644744151022265929975993911186879561257100479593516979735117799410600147341193819147290056586421994333004992422258618475766549646258761885662783430625 ** 2
for i in range(x, x+2):
    print(i, isSquare(i))

次の結果を印刷しました。

1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890625 True
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890626 False

そして、0.33秒でこれを行いました。

私の意見では、私のアルゴリズムはAlex Martelliのアルゴリズムと同じように機能しますが、そのすべての利点はありますが、非常に効率的な単純なテスト拒否により多くの時間を節約でき、テスト数の大きさの削減は言うまでもありません4、速度、効率、精度、およびテスト可能な数値のサイズを改善します。おそらく、Python以外の実装で特に当てはまります。

すべての整数の約99%は、バビロニアのルート抽出が実装される前に非正方形として拒否され、2/3でバビロニアが整数を拒否するのにかかる時間です。そして、これらのテストはプロセスを大幅にスピードアップしませんが、4のすべての累乗reallyによってすべてのテスト数を奇数に減らすとバビロニアが加速しますテスト。

時間比較テストを行いました。 100万から1000万のすべての整数を連続してテストしました。バビロニアの方法だけを(特別に調整された最初の推測で)単独で使用すると、Surface 3に平均165秒(100%の精度)を要しました。私のアルゴリズム(バビロニアを除く)の論理テストだけを使用すると、127秒かかり、完全な正方形を誤って拒否することなく、すべての整数の99%を非正方形として拒否しました。通過した整数のうち、3%のみが完全な正方形(はるかに高い密度)でした。論理テストとバビロニアのルート抽出の両方を使用する上記の完全なアルゴリズムを使用すると、100%の精度が得られ、テストはわずか14秒で完了します。最初の1億個の整数は、テストに約2分45秒かかります。

編集:さらに時間を短縮することができました。これで、1分40秒で0から1億の整数をテストできます。データ型と陽性を確認するのに多くの時間が無駄になります。最初の2つのチェックを削除し、実験を1分短縮しました。ユーザーは、ネガとフロートが完全な正方形ではないことを十分に理解していると想定しなければなりません。

8

上記の例のいくつかに別のスレッドでわずかなバリエーションを投稿しました( Finding perfect squares )そして、ここに投稿したもののわずかなバリエーションを含めると思いました(nsqrtを一時変数として使用) 、興味のある場合/使用する場合:

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)

152415789666209426002111556165263283035677490のような大きな非正方形の場合は正しくありません

5
gumption

これは decimalモジュール を使用して解決でき、任意の精度の平方根と「正確さ」の簡単なチェックを取得します。

_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]
_

本当に大きな価値を持つデモンストレーションの場合:

_# 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
_

テストする値のサイズを大きくすると、最終的にはかなり遅くなります(200,000ビットの正方形では1秒近くかかります)が、より穏やかな数値(たとえば20,000ビット)では、人間が気づくよりも速い個々の値(私のマシンでは〜33 ms)。しかし、速度は主な関心事ではなかったので、これはPythonの標準ライブラリで速度を上げる良い方法です。

もちろん、 _gmpy2_ を使用してgmpy2.mpz(x).is_square()をテストする方がはるかに高速になりますが、サードパーティのパッケージがあなたのものでなければ、上記は非常にうまく機能します。

5
ShadowRanger

私の答えは:

def is_square(x):
    return x**.5 % 1 == 0

基本的に平方根を行い、1を法として整数部分を取り除き、結果が0の場合はTrueを返し、そうでない場合はFalseを返します。この場合、xは、pythonが処理できる最大浮動小数点数ほど大きくない、任意の大きな数値にすることができます:1.7976931348623157e + 308

間違っています 152415789666209426002111556165263283035677490などの大きな非正方形の場合.

4
Lasagnenator

これは私の方法です:

def is_square(n) -> bool:
    return int(n**0.5)**2 == int(n)

数値の平方根を取ります。整数に変換します。正方形を取る。数が等しい場合、それ以外は完全な正方形です。

不正解です 152415789666209426002111556165263283035677489などの大きな正方形の場合.

2
Archu Sm

これは、数値的には可能な限り単純な解決策です。それは小さな数で動作します。

def is_perfect_square(n):
    return (n ** .5).is_integer()

152415789666209426002111556165263283035677490などの多数の場合、明らかに失敗します。

1
A-B-B

丸められた平方根をバイナリ検索できます。結果を二乗して、元の値と一致するかどうかを確認します。

おそらく、FogleBirdsの回答をお勧めします。ただし、浮動小数点演算は近似であるため、このアプローチは失敗する可能性があるので注意してください。たとえば、精度が失われたために、原則として、完全な正方形より1つ大きい整数から偽陽性を得る可能性があります。

1
Steve314

この応答は、あなたが述べた質問には関係しませんが、あなたが投稿したコードにある暗黙の質問、つまり「何かが整数かどうかを確認する方法」に関係しています。

通常、この質問に対する最初の答えは「しないでください」です。そして、Pythonでは、型チェックは通常正しいことではないのは事実です。

ただし、これらのまれな例外については、数値の文字列表現で小数点を探す代わりに、isinstance関数を使用します。

>>> isinstance(5,int)
True
>>> isinstance(5.0,int)
False

もちろん、これは値ではなく変数に適用されます。 valueが整数であるかどうかを判断したい場合は、次のようにします。

>>> x=5.0
>>> round(x) == x
True

しかし、他のすべての人が詳細に説明しているように、この種のもののほとんどのおもちゃではない例で考慮されるべき浮動小数点の問題があります。

0
Vicki Laidler

範囲をループして、完全な正方形ではないすべての数値に対して何かを実行したい場合は、次のようなことができます。

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

IS完全な正方形であるすべての数に対して何かをしたい場合、ジェネレーターはさらに簡単です:

(n * n for n in range(upper))
0
Moberg
a = math.sqrt(n)
b = int(a) 
a == b 
0

Pythonについてはわかりませんが、次のようなことができます。

_function isSquare(x) = x == floor(sqrt(x) + 0.5)^2
_

つまり、数値を取得し、平方根を見つけ、最も近い整数に丸め、平方し、元の数値と同じかどうかをテストします。 (floorと_0.5_の追加は、Mike Grahamが指摘したように、sqrt(4)のようなケースが浮動小数点演算により_1.9999999..._を返すのを防ぐために行われます。)

興味がある場合は、 整数の平方根が整数であるかどうかを判断する最も速い方法 について非常に良い議論がありました。

説明のために編集されました。

0
David Johnstone
  1. 数字の長さを決定します。
  2. デルタ0.000000000000を取得します............ 000001
  3. (sqrt(x))^ 2-xがデルタよりも大きい/等しい/小さいかどうかを確認し、デルタエラーに基づいて決定します。

私はこれが機能し、非常に簡単だと思います:

import math

def is_square(num):
    sqrt = math.sqrt(num)
    return sqrt == int(sqrt)

間違っています 152415789666209426002111556165263283035677490などの大きな非正方形の場合.

0