ตรวจสอบว่าตัวเลขเป็นกำลังสองสมบูรณ์หรือไม่


85

จะตรวจสอบได้อย่างไรว่าตัวเลขเป็นกำลังสองสมบูรณ์

ความเร็วไม่น่ากังวลตอนนี้ใช้งานได้


1
สำหรับตัวเลขที่มีขนาดใหญ่มากจะมีอัลกอริทึมการสุ่มอย่างรวดเร็ว: petr-mitrichev.blogspot.com/2017/12/a-quadratic-week.html
Thomas Ahle

คำตอบ:


117

ปัญหาในการใช้การคำนวณจุดลอยตัวใด ๆ ( 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)

คำแนะนำ: มันขึ้นอยู่กับ "อัลกอริทึมบาบิโลน" สำหรับรากที่ดูวิกิพีเดีย มันใช้งานได้กับจำนวนบวกใด ๆ ที่คุณมีหน่วยความจำเพียงพอสำหรับการคำนวณเพื่อดำเนินการให้เสร็จสมบูรณ์ ;-)

แก้ไข : มาดูตัวอย่าง ...

x = 12345678987654321234567 ** 2

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

พิมพ์ตามต้องการ (และในระยะเวลาที่เหมาะสมด้วย ;-):

152415789666209426002111556165263283035677489 True
152415789666209426002111556165263283035677490 False

โปรดก่อนที่คุณจะนำเสนอโซลูชั่นขึ้นอยู่กับจุดลอยผลกลางให้แน่ใจว่าพวกเขาทำงานอย่างถูกต้องในตัวอย่างง่ายๆนี้ - มันไม่ว่าหนัก (คุณเพียงแค่ต้องตรวจสอบพิเศษไม่กี่ในกรณี 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 ความเร็วที่แท้จริง ; -) ...


พูดในสิ่งที่คุณต้องการเกี่ยวกับผู้เขียน gmpy ดูเหมือนเป็นเครื่องมือที่ยอดเยี่ยมสำหรับงานนี้
Mike Graham

3
วิธีการของบาบิโลนใช้ได้ผลดี แต่คุณต้องมีกรณีพิเศษสำหรับ 0 และ 1 เพื่อหลีกเลี่ยงการหารด้วยศูนย์
mpenkov

2
โดยวิธีการset([x])={x}
Oscar Mederos

6
ไม่setโอเคหรือเปล่า? บาบิโลนไม่ได้มาบรรจบกันint(sqrt(x))ที่เราต้องตรวจสอบว่า prev != next?
Tomasz Gandor

1
"ฉันรู้ว่ามันง่ายมากที่ให้ความรู้สึกเหมือนการโกง (เป็นแบบที่ฉันรู้สึกต่อ Python โดยทั่วไป" ซูจริง;)
Arulx Z

39

ใช้วิธีของนิวตันเพื่อหาศูนย์ในจำนวนเต็มรากที่สองที่ใกล้ที่สุดอย่างรวดเร็วจากนั้นยกกำลังสองและดูว่าเป็นจำนวนของคุณหรือไม่ ดูisqrt

Python ≥ 3.8 มีmath.isqrt. ถ้าใช้รุ่นเก่าของงูใหญ่ให้มองหา " def isqrt(n)" การดำเนินงานที่นี่

import math

def is_square(i: int) -> bool:
    return i == math.isqrt(i) ** 2

20

เนื่องจากคุณไม่สามารถพึ่งพาการเปรียบเทียบที่แน่นอนเมื่อต้องจัดการกับการคำนวณทศนิยม (เช่นวิธีการคำนวณรากที่สองเหล่านี้) การใช้งานที่มีโอกาสเกิดข้อผิดพลาดน้อยกว่า

import math

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

ลองนึกภาพมีinteger อาจเป็นได้แต่ก็อาจเป็นบางอย่างเช่นหรือดังนั้นการยกกำลังสองผลลัพธ์ออกมาจึงไม่น่าเชื่อถือ การรู้ว่าใช้ค่าพื้นเพิ่มค่าลอยโดยวิธีแรกเราจะได้ค่าที่เรากำลังมองหาหากเราอยู่ในช่วงที่ยังมีความละเอียดดีพอที่จะแสดงตัวเลขที่อยู่ใกล้กับค่าที่เรากำลังมองหา .9math.sqrt(9)3.02.999993.00001int0.5float


5
มันจะดีกว่าเล็กน้อยif int(root + 0.5) ** 2 == integer:ถ้าintทำตามfloorตัวเลขที่เราสนใจ
David Johnstone

@David Johnstone ฉันเปลี่ยนโพสต์นี้เพื่อใช้การใช้งานนั้นซึ่งฉันยอมรับว่าดีกว่าวิธีเดิมที่ฉันมี ไม่ว่าในกรณีใดเทคนิคอื่น ๆ ที่คนอื่นกล่าวถึงในที่นี้จะดีกว่าและเชื่อถือได้มากกว่า
Mike Graham

ฉันเข้าใจว่า FP เป็นค่าประมาณ แต่จะเป็นไปได้math.sqrt(9)จริง2.99999เหรอ? floatแมปของ Python กับ C doubleแต่ฉันคิดว่าแม้แต่ประเภท FP 16 บิตก็มีความแม่นยำมากกว่านั้นดังนั้นบางทีถ้าคุณมีคอมไพเลอร์ C ที่ใช้ FP 8 บิต ("minifloats") เป็นdoubleประเภท? ฉันคิดว่ามันเป็นไปได้ในทางเทคนิค แต่ดูเหมือนว่าฉันไม่น่าจะเป็นเช่นนั้นในคอมพิวเตอร์ทุกเครื่องที่ใช้ Python ในปัจจุบัน
Ken

@ เคนฉันพูดว่า "บางอย่าง" เพื่อบ่งบอกว่าฉันได้รับแนวคิดพื้นฐาน ไม่รับประกันว่ามูลค่าที่คุณได้รับจะไม่น้อยกว่ามูลค่าที่แน่นอนเล็กน้อย ฉันนึกไม่ออกว่าmath.sqrt(9)จะกลับมา2.99999ในระบบใดระบบหนึ่ง แต่ผลลัพธ์ที่แท้จริงขึ้นอยู่กับระบบและไม่สามารถคาดหวังได้อย่างแน่นอน
Mike Graham

1
ฟังก์ชันนี้ไม่ถูกต้องสำหรับช่องสี่เหลี่ยมขนาดใหญ่เช่น 152415789666209426002111556165263283035677489
Acumenus

13

ถ้าคุณสนใจก็มีการตอบสนองบริสุทธิ์คณิตศาสตร์เพื่อคำถามที่คล้ายกันที่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 อย่างเป็นระบบในอัลกอริทึมย่อยที่รวดเร็วมากโดยใช้การดำเนินการกะบิตและตรรกะบิต ในที่สุดเราจะไม่พบ isSquare ของ n ดั้งเดิมของเรา แต่เป็น k <n ที่ถูกลดขนาดลงด้วยพาวเวอร์ 4 ถ้าเป็นไปได้ ซึ่งจะช่วยลดขนาดของจำนวนที่เรากำลังดำเนินการและเร่งวิธีการของบาบิโลน แต่ยังทำให้การตรวจสอบอื่น ๆ เร็วขึ้นด้วย

โค้ดบล็อกที่สามทำการทดสอบตรรกะบิตแบบบูลีนอย่างง่าย เลขสามหลักที่มีนัยสำคัญน้อยที่สุดในฐานสองของกำลังสองสมบูรณ์ใด ๆ คือ 001 เสมอ บันทึกสำหรับเลขศูนย์นำหน้าซึ่งเป็นผลมาจากอำนาจของ 4 อย่างไรก็ตามซึ่งได้รับการพิจารณาแล้ว หากไม่ผ่านการทดสอบคุณจะรู้ได้ทันทีว่าไม่ใช่สี่เหลี่ยมจัตุรัส ถ้ามันผ่านไปคุณไม่แน่ใจ

นอกจากนี้ถ้าเราลงท้ายด้วย 1 สำหรับค่าทดสอบแล้วหมายเลขทดสอบเดิมเป็นเลขยกกำลัง 4 รวมทั้งอาจเป็น 1 เองด้วย

เช่นเดียวกับบล็อกที่สามบล็อกที่สี่จะทดสอบค่าตัวที่เป็นทศนิยมโดยใช้ตัวดำเนินการโมดูลัสอย่างง่ายและมีแนวโน้มที่จะจับค่าที่ผ่านการทดสอบก่อนหน้านี้ นอกจากนี้ยังมีการทดสอบ mod 7, mod 8, mod 9 และ mod 13

โค้ดบล็อกที่ 5 จะตรวจสอบรูปแบบสี่เหลี่ยมจัตุรัสสมบูรณ์แบบที่รู้จักกันดี ตัวเลขที่ลงท้ายด้วย 1 หรือ 9 นำหน้าด้วยผลคูณของสี่ และตัวเลขที่ลงท้ายด้วย 5 ต้องลงท้ายด้วย 5625, 0625, 225 หรือ 025 ฉันรวมคนอื่นไว้ด้วย แต่รู้ว่ามันซ้ำซ้อนหรือไม่เคยใช้จริง

สุดท้ายรหัสบล็อกที่หกมีลักษณะคล้ายกับคำตอบของ Alex Martelli ซึ่งเป็นผู้ตอบสูงสุด โดยทั่วไปจะค้นหารากที่สองโดยใช้อัลกอริทึมของชาวบาบิโลนโบราณ แต่ จำกัด ไว้ที่ค่าจำนวนเต็มโดยไม่สนใจจุดลอยตัว ทำได้ทั้งเพื่อความเร็วและขยายขนาดของค่าที่ทดสอบได้ ฉันใช้ชุดแทนรายการเพราะใช้เวลาน้อยกว่ามากฉันใช้การเลื่อนบิตแทนการหารด้วยสองและฉันเลือกค่าเริ่มต้นเริ่มต้นอย่างชาญฉลาดได้อย่างมีประสิทธิภาพมากขึ้น

อย่างไรก็ตามฉันได้ทดสอบหมายเลขทดสอบที่แนะนำของ 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 จริงๆเร่งการทดสอบบาบิโลน

ฉันทำการทดสอบเปรียบเทียบเวลา ฉันทดสอบจำนวนเต็มตั้งแต่ 1 ถึง 10 ล้านต่อเนื่องกัน การใช้วิธีการแบบบาบิโลนด้วยตัวเอง (ด้วยการคาดเดาเริ่มต้นที่ปรับแต่งเป็นพิเศษของฉัน) ทำให้ Surface 3 ของฉันใช้เวลาเฉลี่ย 165 วินาที (มีความแม่นยำ 100%) ด้วยการใช้การทดสอบเชิงตรรกะในอัลกอริทึมของฉัน (ไม่รวมบาบิโลน) ใช้เวลา 127 วินาทีมันปฏิเสธ 99% ของจำนวนเต็มทั้งหมดว่าไม่ใช่กำลังสองโดยไม่ปฏิเสธกำลังสองที่สมบูรณ์แบบโดยไม่ตั้งใจ จากจำนวนเต็มเหล่านั้นที่ผ่านไปมีเพียง 3% เท่านั้นที่เป็นกำลังสองสมบูรณ์ (ความหนาแน่นสูงกว่ามาก) การใช้อัลกอริทึมเต็มรูปแบบด้านบนที่ใช้ทั้งการทดสอบเชิงตรรกะและการแยกรากแบบบาบิโลนเรามีความแม่นยำ 100% และทดสอบเสร็จสิ้นในเวลาเพียง 14 วินาที จำนวนเต็ม 100 ล้านตัวแรกใช้เวลาทดสอบประมาณ 2 นาที 45 วินาที

แก้ไข: ฉันสามารถลดเวลาลงได้อีก ตอนนี้ฉันสามารถทดสอบจำนวนเต็ม 0 ถึง 100 ล้านใน 1 นาที 40 วินาที เสียเวลามากในการตรวจสอบชนิดข้อมูลและความเป็นบวก กำจัดการตรวจสอบสองครั้งแรกและฉันลดการทดสอบลงหนึ่งนาที ต้องถือว่าผู้ใช้ฉลาดพอที่จะรู้ว่าเนกาทีฟและลอยไม่ใช่กำลังสองที่สมบูรณ์แบบ


สำหรับความเรียบง่ายนั้นยากที่จะเอาชนะคำตอบที่ยอมรับได้ ประสิทธิภาพที่ชาญฉลาดคุณควรจะดีกว่า ฉันไม่แน่ใจในคุณค่าของการลดเป้าหมายด้วยกำลังสองของช่วงเวลาเล็ก ๆ แต่การคำนวณสัญลักษณ์จาโคบีสำหรับช่วงเล็ก ๆ ควรจะชนะ และยิ่งตัวเลขมากก็ยิ่งได้เปรียบสำหรับคำตอบนี้
ประธาน James K. Polk

1
การลดด้วยพลังของช่วงเวลาเล็ก ๆ เป็นสิ่งจำเป็นสำหรับการคำนวณสัญลักษณ์จาโคบีเพื่อให้ได้ผลลัพธ์ที่กำหนด มิฉะนั้นจะมีความน่าจะเป็นที่ดีที่สุดหรือกำหนดไว้สำหรับความไม่กำลังสอง แต่ไม่ได้ตรวจสอบความเป็นกำลังสอง นั่นคือเหตุผลบางส่วนที่ฉันแยกตัวประกอบด้วยพลังของกำลังสอง สัญลักษณ์จาโคบีเดียวที่ฉันคำนวณนั้นใช้สำหรับช่วงเวลาเล็ก ๆ ที่ฉันแยกตัวออกมา ฉันยังทำเพียงเพื่อลดขนาดของหมายเลขทดสอบเพื่อให้วิธีการของชาวบาบิโลนที่ใช้ในภายหลังเร็วขึ้นเล็กน้อย (แต่ก็เป็นที่ถกเถียงกัน)
CogitoErgoCogitoSum

แน่นอนว่ามันเป็นคำตอบที่ดีและไม่เหมือนใครและถ้าฉันมีเวลาในอนาคตฉันอยากจะเล่นกับสิ่งนี้ลองกำหนดเวลาที่แตกต่างกันไปตามจำนวนช่วงเวลาเล็ก ๆ เพื่อดูว่าจำนวนที่เหมาะสมที่สุดสามารถพบได้ในขนาดบิตที่กำหนดหรือไม่ .
ประธาน James K. Polk

1
โดยทั้งหมดทดสอบรหัสของฉัน ทำลายมัน ฉันไม่ใช่โปรแกรมเมอร์โดยการค้าฉันเป็นวิชาเอกคณิตศาสตร์ Python เป็นเพียงงานอดิเรก ไม่อยากรู้ว่ามันมีประสิทธิภาพมากกว่าโดยเฉลี่ยหรือไม่
CogitoErgoCogitoSum

1
หากคุณมีความสนใจในการมียังคงเป็นหลักคำถามที่ซ้ำกันที่นี่มีคำตอบที่น่าสนใจโดยเฉพาะอย่างยิ่งคำตอบ A.Rex ของ
ประธาน James K. Polk

12
import math

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

กำลังสองสมบูรณ์คือจำนวนที่สามารถแสดงเป็นผลคูณของจำนวนเต็มสองจำนวนที่เท่ากัน math.sqrt(number)กลับกfloat. ปลดเปลื้องผลการint(math.sqrt(number))int

หากรากที่เป็นจำนวนเต็มเช่น 3 เช่นนั้นmath.sqrt(number) - int(math.sqrt(number))จะเป็น 0 และคำสั่งจะเป็นif Falseถ้ารากที่สองเป็นจำนวนจริงเช่น 3.2 มันจะเป็นTrueและพิมพ์ "มันไม่ใช่กำลังสองสมบูรณ์"

มันล้มเหลวสำหรับ non-square ขนาดใหญ่เช่น 152415789666209426002111556165263283035677490


เปลี่ยนif (math.sqrt(number)-int(math.sqrt(number))):เป็นa=math.sqrt(number)บรรทัดอื่นสำหรับ: if a-int(a):. เนื่องจากมันต้องคำนวณรากที่สองเพียงครั้งเดียวซึ่ง imo สำหรับ n ขนาดใหญ่นั้นมีนัยสำคัญ
unseen_rider

@JamesKPolk ทำไมเป็นอย่างนั้น?
user1717828

ฉันค่อนข้างแน่ใจว่า sqrt - int (sqrt) เหมือนกับ sqrt% 1 ฟังก์ชันทั้งหมดของคุณอาจเป็นเพียงแค่ return math.sqrt (n)% 1 == 0
CogitoErgoCogitoSum

6

คำตอบของฉันคือ:

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

มันเป็นพื้นไม่รากแล้ว modulo โดยที่ 1 ที่จะตัดส่วนจำนวนเต็มและหากผลที่ได้คือ 0 ผลตอบแทนอย่างอื่นกลับมาTrue Falseในกรณีนี้ x อาจเป็นจำนวนมากก็ได้ แต่ไม่มากเท่ากับจำนวนลูกลอยสูงสุดที่ python สามารถจัดการได้: 1.7976931348623157e + 308

ไม่ถูกต้องสำหรับ non-square ขนาดใหญ่เช่น 152415789666209426002111556165263283035677490


5

นี้สามารถแก้ไขได้โดยใช้โมดูลที่จะได้รับโดยพลรากแม่นยำตารางและการตรวจสอบง่ายสำหรับ "ความถูกต้อง"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 บิต) แต่สำหรับตัวเลขที่ปานกลาง (เช่น 20,000 บิต) ก็ยังเร็วกว่าที่มนุษย์จะสังเกตเห็น แต่ละค่า (~ 33 ms บนเครื่องของฉัน) แต่เนื่องจากความเร็วไม่ใช่ข้อกังวลหลักของคุณนี่เป็นวิธีที่ดีในการทำกับไลบรารีมาตรฐานของ Python

แน่นอนว่ามันจะเร็วกว่ามากในการใช้gmpy2และทดสอบgmpy2.mpz(x).is_square()แต่ถ้าแพ็คเกจของบุคคลที่สามไม่ใช่ของคุณสิ่งที่กล่าวมาก็ใช้ได้ดีทีเดียว


5

ฉันเพิ่งโพสต์รูปแบบเล็กน้อยในบางตัวอย่างด้านบนในเธรดอื่น (การค้นหากำลังสองที่สมบูรณ์แบบ ) และคิดว่าฉันจะรวมรูปแบบเล็กน้อยของสิ่งที่ฉันโพสต์ไว้ที่นี่ (โดยใช้ 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)

ไม่ถูกต้องสำหรับ non-square ขนาดใหญ่เช่น 152415789666209426002111556165263283035677490


2

นี่คือวิธีการของฉัน:

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

หารากที่สองของจำนวน แปลงเป็นจำนวนเต็ม ใช้สี่เหลี่ยม ถ้าตัวเลขเท่ากันแสดงว่าเป็นกำลังสองสมบูรณ์อย่างอื่นไม่ได้

ไม่ถูกต้องสำหรับสี่เหลี่ยมขนาดใหญ่เช่น 152415789666209426002111556165263283035677489


ใช้ไม่ได้กับจำนวนลบ แต่ก็ยังเป็นทางออกที่ดี!
Rick M.

1

คุณสามารถค้นหาไบนารีสำหรับรากที่สองที่มน ยกกำลังสองผลลัพธ์เพื่อดูว่าตรงกับค่าเดิมหรือไม่

คุณน่าจะดีกว่ากับคำตอบของ FogleBirds แม้ว่าจะต้องระวังเนื่องจากเลขคณิตลอยตัวเป็นค่าประมาณซึ่งอาจทำให้แนวทางนี้หลุดออกไปได้ โดยหลักการแล้วคุณสามารถรับผลบวกลวงจากจำนวนเต็มขนาดใหญ่ซึ่งมากกว่ากำลังสองสมบูรณ์ตัวอย่างเช่นเนื่องจากสูญเสียความแม่นยำ


1

ถ้าโมดูลัส (เศษที่เหลือ) ที่เหลือจากการหารด้วยรากที่สองเป็น 0 แสดงว่ามันเป็นกำลังสองที่สมบูรณ์แบบ

def is_square(num: int) -> bool:
    return num % math.sqrt(num) == 0

ฉันตรวจสอบสิ่งนี้กับรายการกำลังสองสมบูรณ์ที่มีมากถึง 1,000


0
  1. ตัดสินใจว่าตัวเลขจะยาวแค่ไหน
  2. ใช้เดลต้า 0.000000000000 ....... 000001
  3. ดูว่า (sqrt (x)) ^ 2 - x มากกว่า / เท่ากับ / เล็กกว่าเดลต้าหรือไม่และตัดสินใจตามข้อผิดพลาดเดลต้า

0

คำตอบนี้ไม่เกี่ยวข้องกับคำถามที่คุณระบุไว้ แต่สำหรับคำถามโดยปริยายที่ฉันเห็นในโค้ดที่คุณโพสต์กล่าวคือ "จะตรวจสอบได้อย่างไรว่าบางสิ่งเป็นจำนวนเต็ม"

คำตอบแรกที่คุณจะได้รับจากคำถามนั้นคือ "อย่า!" และเป็นความจริงที่ว่าใน Python นั้นการพิมพ์ดีดมักไม่ใช่สิ่งที่ควรทำ

สำหรับข้อยกเว้นที่หายากเหล่านั้นแทนที่จะมองหาจุดทศนิยมในการแสดงสตริงของตัวเลขสิ่งที่ต้องทำคือใช้ฟังก์ชันisinstance :

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

แน่นอนว่าสิ่งนี้ใช้กับตัวแปรมากกว่าค่า ถ้าฉันต้องการตรวจสอบว่าค่าเป็นจำนวนเต็มหรือไม่ฉันจะทำสิ่งนี้:

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

แต่อย่างที่คนอื่น ๆ ได้กล่าวถึงในรายละเอียดมีประเด็นลอยตัวที่ต้องพิจารณาในตัวอย่างที่ไม่ใช่ของเล่นส่วนใหญ่ของสิ่งประเภทนี้


1
"สิ่งนี้ใช้กับตัวแปรแทนที่จะเป็นค่า" หมายความว่าอย่างไร คุณสามารถใช้ round (5.0) == 5.0 และ isinstance (x, int) ได้โดยไม่มีปัญหา (และ OOWTDI แค่เรียกใช้ x.is_integer ())
Veky

0

หากคุณต้องการวนซ้ำในช่วงและทำอะไรบางอย่างกับตัวเลขทุกตัวที่ไม่ใช่กำลังสองสมบูรณ์คุณสามารถทำสิ่งนี้:

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

หากคุณต้องการทำบางสิ่งบางอย่างสำหรับตัวเลขทุกตัวที่เป็นกำลังสองสมบูรณ์ตัวสร้างจะง่ายยิ่งขึ้น

(n * n for n in range(upper))

0

ฉันคิดว่ามันใช้งานได้และง่ายมาก:

import math

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

ไม่ถูกต้องสำหรับ non-square ขนาดใหญ่เช่น 152415789666209426002111556165263283035677490


เช่นเดียวกับคำตอบข้างต้น
Ekrem Dinçel

0

รูปแบบของโซลูชันของ @Alex Martelli ที่ไม่มี set

เมื่อx in seenเป็นTrue:

  • ในกรณีส่วนใหญ่จะเป็นการเพิ่มลำดับสุดท้ายเช่น 1022 สร้างxลำดับของ 511, 256, 129, 68, 41, 32, 31 , 31 ;
  • ในบางกรณี (เช่นสำหรับรุ่นก่อนของสี่เหลี่ยมที่สมบูรณ์แบบ) มันเป็นครั้งที่สองไปสุดท้ายหนึ่งเพิ่มเช่น 1023 ผลิต 511, 256, 129, 68, 41, 32 , 31, 32

ดังนั้นจึงเพียงพอที่จะหยุดทันทีที่กระแสxมากกว่าหรือเท่ากับกระแสก่อนหน้า:

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)

ความเท่าเทียมกับอัลกอริทึมดั้งเดิมที่ทดสอบสำหรับ 1 <n <10 ** 7 ในช่วงเวลาเดียวกันตัวแปรที่ง่ายกว่านี้จะเร็วกว่าประมาณ 1.4 เท่า


0
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')

แม้ว่ารหัสนี้อาจจะแก้ปัญหาได้คำตอบที่ดีก็ควรอธิบายสิ่งที่รหัสไม่และวิธีการที่จะช่วยให้
BDL

0

แนวคิดคือการรันลูปจาก i = 1 ถึงพื้น (sqrt (n)) จากนั้นตรวจสอบว่ากำลังสองทำให้ 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; 
} 

-2
import math

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

มันล้มเหลวสำหรับ non-square ขนาดใหญ่เช่น 152415789666209426002111556165263283035677490


2
นี่เป็นคำตอบเฉพาะรหัส โปรดระบุเหตุผลเล็กน้อย
hotzst

คุณไม่สามารถหาเหตุผลผ่านทาง @hotzst ได้หรือไม่? มันสมเหตุสมผลดีและฉันไม่ได้เชี่ยวชาญเรื่องงูหลาม ไม่ใช่การทดสอบที่ยิ่งใหญ่ที่สุด แต่ใช้ได้ทั้งในทางทฤษฎีและสำหรับกรณีเล็ก ๆ
CogitoErgoCogitoSum

1
@CogitoErgoCogitoSum: คุณไม่เข้าใจ คำตอบที่ใช้รหัสเท่านั้นจะไม่พบจากการค้นหาโดยใช้เครื่องมือค้นหาเช่น Google ไม่ว่าใครจะเข้าใจคำตอบนั้นไม่เกี่ยวข้อง
ประธาน James K. Polk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.