Чи існує поточний еквівалент // оператора в Python?


127

Я дізнався про //оператора в Python, який в Python 3 робить поділ з підлогою.

Чи є натомість оператор, який ділиться з ceil? (Я знаю про /оператора, який в Python 3 робить поділ з плаваючою комою.)


1
Важливо: чи бажаєте ви результат int або float?
smci

10
Ви повинні змінити прийняту відповідь на відповіді dlitz. math.ceil призначений для поплавків, він не працює з довгими вставками Python з довільною точністю.
ендоліт

2
@milllimoose Питання справедливе, тому що 1) "поділ на стелі" також базується на "поділі з модулем", 2) математика насправді не говорить про те, що є загальним, а що ні, 3) вам потрібна ця операція для "безперервного біна" проблема упаковки ", тобто скільки ящиків розміром $ k $ потрібно для упаковки $ n $ елементів.
Томаш

Відповіді:


55

Немає жодного оператора, який ділиться зі стелею. Вам потрібно import mathі користуватисяmath.ceil


так foobar = math.ceil (foo / bar)? Гм, я можу з цим жити, не знаю, де б я хотів це використати, було просто цікаво, спасибі
Cradam

37
–1 не використовувати , це почне виходити з ладу для дуже великих цілих чисел. Або використовуйте арифметичну бібліотеку з багатоточною точністю або залишайтеся в цілій області при такому підході.
Вім

5
безумовно, залишайтеся в цілому домені. це майже гарантовано більш ефективно і менше болить голова.
Самі Бенчеріф

1
@David 天宇 Wong gmpy2 (згадується в іншій відповіді тут) - це добре.
Вім

1
Зауважте, що math.ceil обмежений 53 бітами точності. Якщо ви працюєте з великими цілими числами, ви можете не отримати точних результатів.
techkuz

291

Ви можете просто зробити поділ на підлогу вгору:

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

Це працює тому , що оператор ділення Python робить поділ на підлозі (на відміну від C, де ціле поділ обрізає дробову частину).

Це також працює з великими цілими числами Python, оскільки немає (втрачається) перетворення з плаваючою комою.

Ось демонстрація:

>>> 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 Я погоджуюся, що це дуже розумно, але не дуже читано і важко підтримувати! Я вирішив імпортувати стелі з математики, щоб коли хтось із колег прочитав мій рядок коду, він зрозуміє, що це робить!
SlimCheney

2
@apadana Я не згоден. Питання задавало питання, чи є "Python" оператор для цього ". Виходячи з відповідей, відповідь видається "ні". Хоча я підтримую відповідь дліца за його корисність.
Ана Німбус

12
@SlimCheney Вкиньте цей метод в документально підтверджену функцію, і ви добре рухаєтесь. Продуктивність + читабельність одним швидким рухом.
Самі Бенчеріф

2
@SamyBencherif: не тільки продуктивність + читаність, але й правильність для великих входів; плаваюча точка має обмеження в представленні, тоді як у Python intнемає (ну ніяких значущих; на 64-бітному Python ви обмежені 30 * (2**63 - 1)числом бітів), і навіть тимчасово перетворюючись на, floatможна втратити інформацію. Порівняйте math.ceil((1 << 128) / 10)з -(-(1 << 128) // 10).
ShadowRanger

1
Це просто слід включити до стандартної бібліотеки
ендоліт

26

Ви могли б зробити (x + (d-1)) // dпри діленні xна d, тобто (x + 4) // 5.


2
Це класичний метод, який я використовував назавжди. Не працює для негативних дільників, хоча.
Марк Рансом

Це дає такий же результат, як і 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 у плаваючі та назад. Це не дуже швидко, і це може мати проблеми з округленням. Крім того, він спирається на семантику Python 3, де "справжній поділ" створює float і де функція ceil () повертає ціле число.


2
У швидких тестах №1 тут найшвидший, навіть порівняно з -(-a // b)o_O
ендоліт

Підтвердження тут -(a // -b)швидше, ніж -(-a // b), принаймні, коли python -m timeit ...
викладає

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
Тому що чіткість коду перемагає всіх. Ясність в цьому випадку об'єктивна. Але завжди слід спочатку зробити читабельним / ремонтопридатним. Коли і лише тоді, коли ви виявили контрольну точку ефективності, ви можете порушити правила. Сучасні машини настільки швидкі, і тому часто всі інші речі, якими займається ваша програма, призводять до різниці, втраченої в шумі.
Тревіс Гріггс

6
@TravisGriggs, що використовує математику з цілим числом, а не математику з плаваючою комою не лише для швидкості. На досить великі цілі числа з плаваючою математикою дає неправильну відповідь
ендоліт

1
Якщо foo = -8і bar = -4, наприклад, відповідь має бути 2, а не 3, так само -8 // -4. Розділ підлоги Python визначається як "поділ математичного поділу з функцією" floor ", застосованою до результату", а поділ на стелі - це те саме, але ceil()замість цього floor().
ендоліт

8

Зауважте, що math.ceil обмежений 53 бітами точності. Якщо ви працюєте з великими цілими числами, ви можете не отримати точних результатів.

Gmpy2 libary забезпечує c_divфункцію , яка використовує стельове округлення.

Відмова: Я підтримую gmpy2.


3
Цей пакет був би корисним, якби я займався чимось сильно орієнтованим на математику чи науку, я вважаю за краще відповідь, яка використовує основні бібліотеки. Я даю заяву, хоча це є корисною відповіддю
Cradam

Нічого, можу підтвердити. python2 -c 'from math import ceil;assert ceil(11520000000000000102.9)==11520000000000000000'(а також заміщення python3) True
БОТИ

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.