Czy istnieje odpowiednik operatora // w Pythonie?


127

Dowiedziałem się o //operatorze w Pythonie, który w Pythonie 3 dzieli się na podłogę.

Czy istnieje operator, który zamiast tego dzieli się na ceil? (Wiem o /operatorze, który w Pythonie 3 wykonuje dzielenie zmiennoprzecinkowe).


1
Ważne: czy chcesz otrzymać wynik int czy float?
— smci

10
Powinieneś zmienić zaakceptowaną odpowiedź na dlitz's. math.ceil jest dla liczb zmiennoprzecinkowych, nie działa z długimi intami Pythona o dowolnej precyzji.
— endolit

2
@milllimoose Pytanie jest poprawne, ponieważ 1) "podział górny" jest również oparty na "dzieleniu z modułem", 2) matematyka tak naprawdę nie mówi, co jest powszechne, a co nie, 3) potrzebujesz tej operacji dla "ciągłego kosza problem z pakowaniem ”, tj. ile pudełek o rozmiarze $ k $ potrzeba do zapakowania $ n $ przedmiotów.
— Tomasz Gandor

Odpowiedzi:


55

Nie ma operatora, który dzieli górą. Musisz import mathi używaćmath.ceil


więc foobar = math.ceil (foo / bar)? Hmm, mogę z tym żyć, nie wiem, gdzie chciałbym tego użyć, byłem po prostu ciekawy, dzięki
— Cradam,

37
–1 nie używaj , zakończy się niepowodzeniem dla bardzo dużych liczb całkowitych. Albo użyj biblioteki arytmetycznej o wielokrotnej precyzji, albo pozostań w domenie liczb całkowitych dzięki temu podejściu.
— wim

5
zdecydowanie pozostań w domenie liczb całkowitych. to prawie na pewno będzie bardziej wydajne i mniej bolesne.
— Samy Bencherif,

1
@David 天宇 Wong gmpy2 (wspomniany w innej odpowiedzi) jest dobry.
— wim

1
Zauważ, że math.ceil jest ograniczona do 53 bitów precyzji. Jeśli pracujesz z dużymi liczbami całkowitymi, możesz nie uzyskać dokładnych wyników.
— techkuz

291

Możesz po prostu wykonać podział piętra do góry nogami:

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

Działa to, ponieważ operator dzielenia w Pythonie wykonuje dzielenie na podłogę (w przeciwieństwie do C, gdzie dzielenie całkowite obcina część ułamkową).

Działa to również z dużymi liczbami całkowitymi Pythona, ponieważ nie ma (stratnej) konwersji zmiennoprzecinkowej.

Oto demonstracja:

>>> 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 Zgadzam się, że jest to bardzo inteligentne, ale niezbyt czytelne i trudne w utrzymaniu! Zdecydowałem się zaimportować ceil z matematyki, aby jeden z moich kolegów przeczytał mój wiersz kodu, zrozumiał, co robi!
— SlimCheney

2
@apadana Nie zgadzam się. Pytanie dotyczyło tego, czy "istnieje" operator tego "w" Pythonie. Na podstawie odpowiedzi wydaje się, że odpowiedź brzmi „nie”. Popieram jednak odpowiedź dlitza za jej użyteczność.
— Ana Nimbus,

12
@SlimCheney Wrzuć tę metodę do udokumentowanej funkcji i gotowe. Wydajność + czytelność jednym szerokim ruchem.
— Samy Bencherif,

2
@SamyBencherif: Nie tylko wydajność + czytelność, ale także poprawność dla dużych danych wejściowych; zmiennoprzecinkowe mają ograniczenia reprezentacji, podczas gdy Python intnie (no cóż, żadnych znaczących; w 64-bitowym Pythonie jesteś ograniczony do 30 * (2**63 - 1)liczb bitowych), a nawet tymczasowa konwersja na floatmoże utracić informacje. Porównaj math.ceil((1 << 128) / 10)z -(-(1 << 128) // 10).
— ShadowRanger

1
Powinno to być po prostu uwzględnione w standardowej bibliotece
— endolith

26

Można to zrobić (x + (d-1)) // ddzieląc xprzez d, tj (x + 4) // 5.


2
To jest klasyczna metoda, której używałem od zawsze. Nie działa jednak w przypadku ujemnych dzielników.
— Mark Ransom,

Daje taki sam wynik jak math.ceil().
— Abhijeet

3
@Abhijeet Tak, o to chodzi w tym pytaniu. Z wyjątkiem tego, że działa lepiej dla dużych liczb całkowitych powyżej sys.float_info.maxi nie wymaga importu.
— Artyer

22

Rozwiązanie 1: Zamień podłogę na sufit z negacją

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

Przypomina sztuczkę lewitacji Penn & Teller , która „odwraca świat do góry nogami (z negacją), wykorzystuje zwykły podział podłogi (gdzie zamieniono sufit i podłogę), a następnie obraca świat w prawą stronę (ponownie z negacją) "

Rozwiązanie 2: Pozwól divmod () wykonać pracę

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

Funkcja divmod () podaje (a // b, a % b)liczby całkowite (może to być mniej niezawodne w przypadku wartości zmiennoprzecinkowych z powodu błędu zaokrąglenia). Krok z bool(r)dodaje jedynkę do ilorazu za każdym razem, gdy pozostaje niezerowa reszta.

Rozwiązanie 3: Dostosuj licznik przed dzieleniem

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

Przesuń licznik w górę, aby podział podłogi został zaokrąglony w dół do przewidywanego sufitu. Uwaga, działa to tylko dla liczb całkowitych.

Rozwiązanie 4: Konwertuj na elementy zmiennoprzecinkowe, aby używać funkcji math.ceil ()

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

Kod math.ceil () jest łatwy do zrozumienia, ale konwertuje z liczb całkowitych na zmiennoprzecinkowe iz powrotem. Nie jest to zbyt szybkie i może powodować problemy z zaokrąglaniem. Opiera się również na semantyce Pythona 3, gdzie „prawdziwy podział” tworzy liczbę zmiennoprzecinkową, a funkcja ceil () zwraca liczbę całkowitą.


2
W szybkich testach # 1 jest tutaj najszybszy, nawet w porównaniu do -(-a // b)o_O
— endolith

Potwierdzam tutaj, że -(a // -b)jest szybszy niż -(-a // b), przynajmniej jeśli chodzi o synchronizację przykładów zabawek zpython -m timeit ...
— Jasha

19

Zawsze możesz to również zrobić w trybie inline

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

W pythonie3 jest to o rząd wielkości szybsze niż wymuszanie dzielenia typu float i wywoływanie ceil (), pod warunkiem, że zależy Ci na szybkości. Czego nie powinieneś, chyba że udowodniłeś poprzez użycie, że musisz.

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

po prostu sam przeprowadziłem te testy, mam około 12,5 sekundy, ehrm, dlaczego nie miałbym przejmować się szybkością, skoro jest tak duża różnica prędkości?
— Cradam,

3
@Cradam Zauważ, że korzysta z wykonywania 100 milionów połączeń ( number=100000000). W przypadku pojedynczego połączenia różnica jest niewielka.
— Rushy Panchal

4
Ponieważ przejrzystość kodu jest ważniejsza od wszystkiego. W tym przypadku jasność jest prawdopodobnie obiektywna. Ale zawsze powinieneś najpierw uczynić czytelnym / łatwym do utrzymania. Kiedy i tylko wtedy, gdy odkryjesz punkt kontrolny wydajności, możesz złamać zasady. Nowoczesne maszyny są tak szybkie i tak często wszystkie inne rzeczy, które wykonuje twój program, powodują utratę tego rodzaju różnicy w szumie.
— Travis Griggs

6
@TravisGriggs używa matematyki całkowitoliczbowej zamiast matematyki zmiennoprzecinkowej nie tylko dla szybkości. Dla wystarczająco dużych liczb całkowitych matematyka zmiennoprzecinkowa daje złą odpowiedź
— endolit

1
Jeśli foo = -8i bar = -4, na przykład, odpowiedź powinna wynosić 2, a nie 3, tak jak -8 // -4. Podział podłogi w Pythonie jest definiowany jako „podział matematyczny z funkcją„ podłogi ”zastosowaną do wyniku”, a podział sufitu jest tym samym, ale z ceil()zamiast floor().
— endolit

8

Zauważ, że math.ceil jest ograniczona do 53 bitów precyzji. Jeśli pracujesz z dużymi liczbami całkowitymi, możesz nie uzyskać dokładnych wyników.

Gmpy2 Libary zapewnia c_divfunkcję zaokrąglania, który wykorzystuje na suficie.

Zastrzeżenie: utrzymuję gmpy2.


3
Ten pakiet byłby przydatny, gdybym zajmował się czymś mocno matematycznym lub zorientowanym na naukę, wolę jednak odpowiedź, która korzysta z podstawowych bibliotek. Głosuję jednak za, ponieważ jest to przydatna odpowiedź
— Cradam,

Wow, mogę potwierdzić. python2 -c 'from math import ceil;assert ceil(11520000000000000102.9)==11520000000000000000'(jak również zastępowanie python3) True
— OBA

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.