Có tương đương trần của toán tử // trong Python không?


127

Tôi đã tìm hiểu về //toán tử trong Python mà trong Python 3 thực hiện phép chia với tầng.

Có toán tử nào phân chia bằng ceil không? (Tôi biết về /toán tử mà trong Python 3 thực hiện phép chia dấu phẩy động.)


1
Quan trọng: bạn muốn một kết quả int hay float?
smci

10
Bạn nên thay đổi câu trả lời được chấp nhận thành dlitz's. math.ceil dành cho float, nó không hoạt động với các int dài có độ chính xác tùy ý của Python.
endolith

2
@milllimoose Câu hỏi là hợp lệ, vì 1) "phép chia ceil" cũng dựa trên "phép chia theo mô đun", 2) phép toán không thực sự nói cái gì phổ biến và cái gì không, 3) bạn cần thao tác này cho "liên tục bin Bài toán đóng gói ", tức là cần có bao nhiêu hộp có kích thước $ k $ để đóng gói các mặt hàng $ n $.
Tomasz Gandor

Câu trả lời:


55

Không có toán tử nào phân chia bằng ceil. Bạn cần import mathvà sử dụngmath.ceil


vậy foobar = math.ceil (foo / bar)? Hmm, tôi có thể sống với điều đó, không biết bất cứ nơi nào tôi muốn sử dụng, chỉ là tò mò, nhờ
Cradam

37
–1 không sử dụng , điều này sẽ bắt đầu không thành công đối với các số nguyên rất lớn. Sử dụng thư viện số học nhiều độ chính xác hoặc ở trong miền số nguyên với cách tiếp cận này .
wim

5
chắc chắn ở trong miền số nguyên. điều đó gần như được đảm bảo sẽ hoạt động hiệu quả hơn ít đau đầu hơn.
Samy Bencherif

1
@David 天宇 Wong gmpy2 (được đề cập trong một câu trả lời khác ở đây) là tốt.
wim

1
Lưu ý rằng math.ceil được giới hạn ở độ chính xác 53 bit. Nếu bạn đang làm việc với các số nguyên lớn, bạn có thể không nhận được kết quả chính xác.
techkuz

290

Bạn chỉ có thể thực hiện chia tầng lộn ngược:

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

Điều này hoạt động vì toán tử phân chia của Python thực hiện phân chia tầng (không giống như trong C, trong đó phép chia số nguyên cắt ngắn phần phân số).

Điều này cũng hoạt động với các số nguyên lớn của Python, vì không có chuyển đổi dấu phẩy động (mất mát).

Đây là một minh chứng:

>>> 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 Tôi đồng ý rằng điều này rất thông minh, nhưng không dễ đọc và khó duy trì! Tôi đã quyết định nhập ceil từ toán học để khi một trong những đồng nghiệp của tôi đọc dòng mã của tôi, anh ta sẽ hiểu nó làm gì!
SlimCheney

2
@apadana Tôi không đồng ý. Câu hỏi đặt ra liệu có "có" toán tử cho điều này "trong" Python hay không. Dựa trên các câu trả lời, câu trả lời dường như là "không". Tôi ủng hộ câu trả lời của dlitz vì tính hữu ích của nó.
Ana Nimbus

12
@SlimCheney Chuyển phương pháp này vào một hàm được ghi lại và bạn đã sẵn sàng. Hiệu suất + khả năng đọc trong một chuyển động quét.
Samy Bencherif

2
@SamyBencherif: Không chỉ là hiệu suất + khả năng đọc, mà còn là tính đúng đắn cho các đầu vào lớn; dấu phẩy động có giới hạn về đại diện, trong khi Python intthì không (tốt, không có ý nghĩa nào; trên Python 64 bit, bạn bị giới hạn ở 30 * (2**63 - 1)số bit), và thậm chí chuyển đổi tạm thời floatcó thể làm mất thông tin. So sánh math.ceil((1 << 128) / 10)với -(-(1 << 128) // 10).
ShadowRanger

1
Điều này chỉ nên được đưa vào thư viện tiêu chuẩn
endolith

26

Bạn có thể làm (x + (d-1)) // dkhi chia xcho d, tức là (x + 4) // 5.


2
Đây là phương pháp cổ điển mà tôi đã sử dụng mãi mãi. Tuy nhiên, không hoạt động cho các ước số âm.
Mark Ransom vào

Nó tạo ra kết quả tương tự như math.ceil().
Abhijeet

3
@Abhijeet Vâng, đó là những gì câu hỏi yêu cầu. Ngoại trừ nó hoạt động tốt hơn cho các số nguyên lớn ở trên sys.float_info.maxvà nó không yêu cầu nhập.
Artyer

22

Giải pháp 1: Chuyển đổi sàn thành trần với phủ định

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

Gợi nhớ đến thủ thuật bay lên của Penn & Teller , trò chơi này "lật ngược thế giới (với phủ định), sử dụng phân chia tầng đơn giản (nơi trần và sàn đã được hoán đổi), sau đó lật ngược thế giới lên (với phủ định lại) "

Giải pháp 2: Để divmod () thực hiện công việc

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

Hàm divmod () cung cấp (a // b, a % b)cho các số nguyên (điều này có thể kém tin cậy hơn với các float do lỗi làm tròn). Bước với bool(r)thêm một vào thương bất cứ khi nào có phần dư khác 0.

Giải pháp 3: Điều chỉnh tử số trước phép chia

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

Dịch tử số lên trên để phân chia tầng làm tròn xuống trần dự định. Lưu ý, điều này chỉ hoạt động đối với số nguyên.

Giải pháp 4: Chuyển đổi thành float để sử dụng math.ceil ()

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

math.ceil () rất dễ hiểu, nhưng nó chuyển đổi từ ints thành float và back. Quá trình này diễn ra không nhanh và có thể có vấn đề làm tròn. Ngoài ra, nó dựa trên ngữ nghĩa Python 3 trong đó "phân chia thực sự" tạo ra một float và trong đó hàm ceil () trả về một số nguyên.


2
Trong các bài kiểm tra nhanh, # 1 là nhanh nhất ở đây, thậm chí so với -(-a // b)o_O
endolith

Xác nhận ở đây -(a // -b)là nhanh hơn -(-a // b), ít nhất là khi tính thời gian các ví dụ đồ chơi vớipython -m timeit ...
Jasha

19

Bạn luôn có thể làm điều đó trong dòng

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

Trong python3, đây chỉ là một thứ tự cường độ nhanh hơn so với việc buộc phân chia float và gọi ceil (), miễn là bạn quan tâm đến tốc độ. Điều bạn không nên làm, trừ khi bạn đã chứng minh thông qua việc sử dụng rằng bạn cần phải làm như vậy.

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

Tôi chỉ chạy những bài kiểm tra đó mà tôi nhận được khoảng 12,5 giây, ơm, tại sao tôi không quan tâm đến tốc độ khi nó là một sự khác biệt tốc độ lớn như vậy?
Cradam

3
@Cradam Lưu ý rằng anh ấy đang thực hiện 100 triệu cuộc gọi ( number=100000000). Mỗi cuộc gọi, sự khác biệt là khá không đáng kể.
Rushy Panchal

4
Bởi vì sự rõ ràng của mã vượt trội hơn tất cả. Rõ ràng là khách quan trong trường hợp này. Nhưng trước tiên bạn nên làm cho nó có thể đọc được / có thể bảo trì. Khi và chỉ khi bạn phát hiện ra một điểm kiểm tra hiệu suất, bạn mới có thể phá vỡ các quy tắc. Máy móc hiện đại quá nhanh, và thường thì tất cả những thứ khác mà chương trình của bạn đang thực hiện đều làm cho loại khác biệt này bị mất trong tiếng ồn.
Travis Griggs

6
@TravisGriggs sử dụng toán học số nguyên thay vì toán học dấu phẩy động không chỉ dành cho tốc độ. Đối với số nguyên đủ lớn toán phao đưa ra câu trả lời sai
endolith

1
Ví dụ, nếu foo = -8bar = -4, câu trả lời phải là 2, không phải 3, giống như -8 // -4. Phân chia tầng trong Python được định nghĩa là "phân chia toán học với hàm 'sàn' được áp dụng cho kết quả" và phân chia trần là điều tương tự nhưng với ceil()thay vì floor().
endolith

8

Lưu ý rằng math.ceil được giới hạn ở độ chính xác 53 bit. Nếu bạn đang làm việc với các số nguyên lớn, bạn có thể không nhận được kết quả chính xác.

Các gmpy2 THƯ VIỆN cung cấp một c_divchức năng trong đó sử dụng trần tròn.

Tuyên bố từ chối trách nhiệm: Tôi duy trì gmpy2.


3
Gói này sẽ hữu ích nếu tôi đang làm một cái gì đó thiên về toán học hoặc khoa học, tôi thích câu trả lời sử dụng thư viện lõi hơn. Tôi đang đưa ra một
phiếu ủng hộ

Wow, có thể xác nhận. python2 -c 'from math import ceil;assert ceil(11520000000000000102.9)==11520000000000000000'(cũng như thay thế python3) Cả hai đềuTrue
JamesTheAwesomeDude

-6

Giải pháp đơn giản: a // b + 1


2
Điều này là sai đối với bất kỳ thứ gì chia đều. a = 4, b = 2, v.v.
endolith
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.