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.