มีเพดานเทียบเท่ากับ // โอเปอเรเตอร์ใน Python หรือไม่?


128

ฉันพบข้อมูลเกี่ยวกับตัว//ดำเนินการใน Python ซึ่งใน Python 3 ทำการหารด้วยพื้น

มีตัวดำเนินการที่หารด้วย ceil แทนหรือไม่? (ฉันรู้เกี่ยวกับตัว/ดำเนินการซึ่งใน Python 3 ทำการหารทศนิยม)


1
สำคัญ: คุณต้องการผลลัพธ์ int หรือ float?
smci

10
คุณควรเปลี่ยนคำตอบที่ยอมรับเป็นของ dlitz math.ceil ใช้สำหรับการลอยซึ่งไม่สามารถใช้ได้กับ ints ยาวที่มีความแม่นยำโดยพลการของ Python
endolith

2
@milllimoose คำถามนี้ใช้ได้เพราะ 1) "การหารเพดาน" ขึ้นอยู่กับ "การหารด้วยโมดูลัส" ด้วย 2) คณิตศาสตร์ไม่ได้บอกว่าอะไรธรรมดาและอะไรไม่เหมือนกัน 3) คุณต้องการการดำเนินการนี้สำหรับ "ถังต่อเนื่อง ปัญหาการบรรจุ "กล่าวคือต้องมีกล่องขนาด $ k $ กี่กล่องเพื่อบรรจุสินค้า $ n $
Tomasz Gandor

คำตอบ:


55

ไม่มีตัวดำเนินการที่หารด้วย ceil คุณจำเป็นต้องimport mathใช้math.ceil


ดังนั้น foobar = math.ceil (foo / bar)? อืมฉันอยู่กับมันได้ไม่รู้ว่าจะใช้ที่ไหนดีแค่อยากรู้ขอบคุณ
Cradam

37
–1 ไม่ใช้สิ่งนี้จะเริ่มล้มเหลวสำหรับจำนวนเต็มขนาดใหญ่มาก ไม่ว่าจะใช้หลายแม่นยำห้องสมุดคณิตศาสตร์หรืออยู่ในโดเมนจำนวนเต็มด้วยนี้วิธีการ
Wim

5
อยู่ในโดเมนจำนวนเต็ม เกือบจะรับประกันได้ว่าจะมีประสิทธิภาพมากขึ้นและปวดหัวน้อยลง
Samy Bencherif

1
@David 天宇 Wong gmpy2 (กล่าวไว้ในคำตอบอื่นที่นี่) เป็นสิ่งที่ดี
Wim

1
โปรดทราบว่า math.ceil ถูก จำกัด ไว้ที่ 53 บิตของความแม่นยำ หากคุณกำลังทำงานกับจำนวนเต็มจำนวนมากคุณอาจไม่ได้ผลลัพธ์ที่แน่นอน
techkuz

292

คุณสามารถแบ่งพื้นแบบกลับหัวได้:

def ceildiv(a, b):
    return -(-a // b)

สิ่งนี้ได้ผลเนื่องจากตัวดำเนินการหารของ Python ทำการแบ่งพื้น (ไม่เหมือนใน C ซึ่งการหารจำนวนเต็มจะตัดทอนส่วนที่เป็นเศษส่วน)

นอกจากนี้ยังใช้ได้กับจำนวนเต็มขนาดใหญ่ของ Python เนื่องจากไม่มีการแปลงทศนิยม (lossy)

นี่คือการสาธิต:

>>> from __future__ import division   # a/b is float division
>>> from math import ceil
>>> b = 3
>>> for a in range(-7, 8):
...     print(["%d/%d" % (a, b), int(ceil(a / b)), -(-a // b)])
... 
['-7/3', -2, -2]
['-6/3', -2, -2]
['-5/3', -1, -1]
['-4/3', -1, -1]
['-3/3', -1, -1]
['-2/3', 0, 0]
['-1/3', 0, 0]
['0/3', 0, 0]
['1/3', 1, 1]
['2/3', 1, 1]
['3/3', 1, 1]
['4/3', 2, 2]
['5/3', 2, 2]
['6/3', 2, 2]
['7/3', 3, 3]

2
@apadana ฉันยอมรับว่านี่ฉลาดมาก แต่อ่านไม่ออกและดูแลรักษายาก! ฉันได้ตัดสินใจที่จะนำเข้า ceil จากคณิตศาสตร์ดังนั้นเมื่อเพื่อนร่วมงานคนหนึ่งของฉันอ่านบรรทัดรหัสของฉันเขาจะเข้าใจว่ามันทำอะไร!
SlimCheney

2
@apadana ฉันไม่เห็นด้วย คำถามถามว่า "มี" ตัวดำเนินการสำหรับสิ่งนี้ "ใน" Python หรือไม่ จากคำตอบคำตอบดูเหมือนจะเป็น "ไม่" ฉันโหวตคำตอบของ dlitz สำหรับประโยชน์ของมัน
Ana Nimbus

12
@SlimCheney โยนวิธีนี้ลงในฟังก์ชันที่เป็นเอกสารและคุณก็พร้อมที่จะไป ประสิทธิภาพ + ความสามารถในการอ่านในการเคลื่อนไหวแบบกวาดครั้งเดียว
Samy Bencherif

2
@SamyBencherif: ไม่ใช่แค่ประสิทธิภาพ + ความสามารถในการอ่านเท่านั้น แต่ยังรวมถึงความถูกต้องสำหรับอินพุตขนาดใหญ่ด้วย จุดลอยตัวมีข้อ จำกัด ในการเป็นตัวแทนในขณะที่ Python intไม่ได้ (เช่นกันไม่มีความหมายใน Python 64 บิตคุณจะถูก จำกัด ไว้ที่30 * (2**63 - 1)ตัวเลขบิต) และแม้แต่การแปลงชั่วคราวfloatก็อาจสูญเสียข้อมูลได้ เปรียบเทียบmath.ceil((1 << 128) / 10)กับ-(-(1 << 128) // 10).
ShadowRanger

1
สิ่งนี้ควรรวมอยู่ในไลบรารีมาตรฐาน
endolith

26

คุณสามารถทำ(x + (d-1)) // dเมื่อหารxโดยคือd(x + 4) // 5


2
นี่เป็นวิธีคลาสสิกที่ฉันใช้ตลอดกาล ใช้ไม่ได้กับตัวหารลบ
Mark Ransom

มันให้ผลลัพธ์เช่นเดียวกับmath.ceil().
Abhijeet

3
@Abhijeet ใช่นั่นคือสิ่งที่ถาม ยกเว้นว่าจะทำงานได้ดีกว่าสำหรับจำนวนเต็มใหญ่ด้านบนsys.float_info.maxและไม่จำเป็นต้องนำเข้า
Artyer

23

โซลูชันที่ 1: แปลงพื้นเป็นเพดานด้วยการปฏิเสธ

def ceiling_division(n, d):
    return -(n // -d)

ชวนให้นึกถึงเคล็ดลับการลอยตัวของเพนน์แอนด์เทลเลอร์ "พลิกโลกกลับหัว (ด้วยการปฏิเสธ) ใช้การแบ่งพื้นแบบธรรมดา (ซึ่งมีการสลับเพดานและพื้น) แล้วหมุนโลกให้กลับด้านขึ้น (ด้วยการปฏิเสธอีกครั้ง) "

โซลูชันที่ 2: ปล่อยให้ divmod () ทำงาน

def ceiling_division(n, d):
    q, r = divmod(n, d)
    return q + bool(r)

divmod ()ฟังก์ชั่นให้(a // b, a % b)สำหรับจำนวนเต็ม (นี้อาจจะมีความน่าเชื่อถือน้อยลอยเนื่องจากข้อผิดพลาดปัดเศษ) ขั้นตอนที่bool(r)เพิ่มหนึ่งในผลหารเมื่อใดก็ตามที่มีเศษเหลือที่ไม่ใช่ศูนย์

โซลูชันที่ 3: ปรับตัวเศษก่อนการหาร

def ceiling_division(n, d):
    return (n + d - 1) // d

แปลตัวเศษขึ้นเพื่อให้การแบ่งพื้นปัดลงไปที่เพดานที่ต้องการ หมายเหตุสิ่งนี้ใช้ได้กับจำนวนเต็มเท่านั้น

โซลูชันที่ 4: แปลงเป็นลอยเพื่อใช้ math.ceil ()

def ceiling_division(n, d):
    return math.ceil(n / d)

รหัสmath.ceil ()เข้าใจง่าย แต่จะแปลงจาก ints เป็น float และ back ไม่เร็วมากและอาจมีปัญหาในการปัดเศษ นอกจากนี้ยังอาศัยความหมายของ Python 3 โดยที่ "การหารจริง" จะสร้างค่าลอยและโดยที่ฟังก์ชันceil ()ส่งกลับจำนวนเต็ม


2
ในการทดสอบอย่างรวดเร็ว # 1 จะเร็วที่สุดที่นี่แม้เทียบกับ-(-a // b)o_O
endolith

ยืนยันที่นี่-(a // -b)เร็วกว่า-(-a // b)อย่างน้อยที่สุดเมื่อจับเวลาตัวอย่างของเล่นด้วยpython -m timeit ...
Jasha

19

คุณสามารถทำแบบอินไลน์ได้ตลอดเวลาเช่นกัน

((foo - 1) // bar) + 1

ใน python3 นี่เป็นเพียงแค่ลำดับความสำคัญที่เร็วกว่าการบังคับให้ส่วนลอยและเรียก ceil () หากคุณสนใจเรื่องความเร็ว สิ่งที่คุณไม่ควรทำเว้นแต่คุณจะได้พิสูจน์ผ่านการใช้งานที่คุณต้องการ

>>> timeit.timeit("((5 - 1) // 4) + 1", number = 100000000)
1.7249219375662506
>>> timeit.timeit("ceil(5/4)", setup="from math import ceil", number = 100000000)
12.096064013894647

เพิ่งทำการทดสอบด้วยตัวเองฉันใช้เวลาประมาณ 12.5 วินาทีเอ๊ะทำไมฉันไม่สนใจเรื่องความเร็วในเมื่อมันต่างกันมากขนาดนี้?
Cradam

3
@Cradam สังเกตว่าเขาใช้การโทร 100 ล้านครั้ง ( number=100000000) ต่อการโทรเพียงครั้งเดียวความแตกต่างนั้นค่อนข้างไม่มีนัยสำคัญ
Rushy Panchal

4
เพราะความชัดเจนของโค้ดสำคัญกว่าทั้งหมด ความชัดเจนเป็นวัตถุประสงค์ในกรณีนี้อาจ แต่คุณควรทำให้อ่านได้ / บำรุงรักษาก่อนเสมอ คุณได้ค้นพบจุดตรวจประสิทธิภาพเมื่อใดและเมื่อใดเท่านั้นคุณจะทำผิดกฎได้หรือไม่ เครื่องจักรสมัยใหม่นั้นเร็วมากและบ่อยครั้งสิ่งอื่น ๆ ทั้งหมดที่โปรแกรมของคุณทำอยู่ทำให้เกิดความแตกต่างประเภทนี้ที่หายไปในเสียงรบกวน
Travis Griggs

6
@TravisGriggs ใช้คณิตศาสตร์จำนวนเต็มแทนเลขทศนิยมไม่ได้มีไว้เพื่อความเร็วเท่านั้น สำหรับจำนวนเต็มจำนวนเต็มมากพอจะให้คำตอบที่ผิด
endolith

1
ถ้าfoo = -8และbar = -4ยกตัวอย่างเช่นคำตอบที่ควรจะเป็น 2, 3 -8 // -4ไม่ได้เช่นเดียวกับ ส่วนชั้นหลามถูกกำหนดให้เป็น"ว่าในส่วนของคณิตศาสตร์กับ 'ชั้น' ฟังก์ชั่นที่ใช้กับผลที่ได้"และการแบ่งเพดานเป็นสิ่งเดียวกัน แต่มีแทนceil() floor()
endolith

8

โปรดทราบว่า math.ceil ถูก จำกัด ไว้ที่ 53 บิตของความแม่นยำ หากคุณกำลังทำงานกับจำนวนเต็มจำนวนมากคุณอาจไม่ได้ผลลัพธ์ที่แน่นอน

gmpy2ไลบรารียังมีc_divฟังก์ชั่นที่ใช้ในการปัดเศษเพดาน

คำเตือน: ฉันรักษา gmpy2


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

ว้าวยืนยันได้เลย python2 -c 'from math import ceil;assert ceil(11520000000000000102.9)==11520000000000000000'(เช่นเดียวกับการเปลี่ยนตัวpython3) ทั้งสองคือTrue
JamesTheAwesomeDude

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