Есть ли в Python потолочный эквивалент оператора //?

128

Я узнал об //операторе в Python, который в Python 3 делает разделение с полом.

Есть ли вместо этого оператор деления на ceil? (Я знаю об /операторе, который в Python 3 выполняет деление с плавающей запятой.)

Cradam
источник
1
Важно: вы хотите получить результат типа int или float?
smci
10
Вы должны изменить принятый ответ на dlitz's. math.ceil предназначен для чисел с плавающей запятой, он не работает с длинными целыми числами произвольной точности Python.
Эндолит
2
@milllimoose Вопрос верный, потому что 1) "деление ceil" также основано на "делении по модулю", 2) математика на самом деле не говорит, что общего, а что нет, 3) вам нужна эта операция для "непрерывного бункера" проблема упаковки », т.е. сколько коробок размером $ k $ необходимо для упаковки $ n $ предметов.
Tomasz

Ответы:

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 08
292

Можно просто сделать перевернутое деление этажа:

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]
dlitz
источник
2
@apadana Я согласен, это очень умно, но не очень читабельно и сложно поддерживать! Я решил импортировать ceil из математики, чтобы, когда один из моих коллег прочитает мою строку кода, он понял, что она делает!
SlimCheney
2
@apadana Я не согласен. Был задан вопрос, есть ли в «Python» оператор для этого. Судя по ответам, ответ - «нет». Однако я поддерживаю ответ dlitz за его полезность.
Ana Nimbus
12
@SlimCheney Добавьте этот метод в документированную функцию, и все готово. Производительность + читаемость одним движением.
Samy Bencherif
2
@SamyBencherif: не только производительность + читаемость, но и корректность для больших входных данных; с плавающей запятой есть ограничения представления, а у Python - intнет (ну, никаких значимых; на 64-битном Python вы ограничены 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
Это классический метод, которым я пользовался всегда. Однако не работает для отрицательных делителей.
Марк Рэнсом
Результат такой же, как у 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 () прост для понимания, но он преобразует целые числа в числа с плавающей запятой и обратно. Это не очень быстро и может иметь проблемы с округлением. Кроме того, он опирается на семантику Python 3, где «истинное деление» производит число с плавающей запятой, а функция ceil () возвращает целое число.

Раймонд Хеттингер
источник
2
В быстрых тестах # 1 здесь самый быстрый даже по сравнению с -(-a // b)o_O
endolith
Подтверждая здесь, что -(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 использует целочисленную математику вместо математики с плавающей запятой не только для скорости. Для достаточно больших целых чисел математика с плавающей запятой дает неправильный ответ
endolith
1
Если foo = -8и bar = -4, например, ответ должен быть 2, а не 3, как -8 // -4. Разделение полов в Python определяется как «математическое деление с применением функции« пол »к результату», а деление потолка - это то же самое, но ceil()вместо floor().
Эндолит
8

Обратите внимание, что math.ceil ограничен 53 битами точности. Если вы работаете с большими целыми числами, вы можете не получить точных результатов.

Библиотека gmpy2 предоставляет c_divфункцию, которая использует округление потолка.

Отказ от ответственности: я поддерживаю gmpy2.

casevh
источник
3
Этот пакет был бы полезен, если бы я занимался чем-то сильно ориентированным на математику или науку, хотя я предпочитаю ответ, который использует основные библиотеки. Я даю положительный ответ, так как это полезный ответ
Крадам
Вау, могу подтвердить. python2 -c 'from math import ceil;assert ceil(11520000000000000102.9)==11520000000000000000'(а также заменяющие python3) ОБЕИTrue
JamesTheAwesomeDude
-6

Простое решение: a // b + 1

AL Verminburger
источник
2
Это неправильно для всего, что делится равномерно. a = 4, b = 2 и т. д.
эндолит