Gibt es in Python ein Höchstäquivalent zum Operator //?


125

Ich habe etwas über den //Operator in Python herausgefunden, der in Python 3 mit Floor dividiert.

Gibt es einen Operator, der stattdessen mit Ceil teilt? (Ich kenne den /Operator, der in Python 3 eine Gleitkommadivision durchführt.)


1
Wichtig: Möchten Sie ein int- oder float-Ergebnis?
smci

10
Sie sollten die akzeptierte Antwort auf dlitz ändern. math.ceil ist für Floats gedacht und funktioniert nicht mit Pythons Long Ints mit beliebiger Genauigkeit.
Endolith

2
@milllimoose Die Frage ist gültig, weil 1) "Ceil Division" auch auf "Division mit Modul" basiert, 2) Mathe nicht wirklich sagt, was gemeinsam ist und was nicht, 3) Sie benötigen diese Operation für "Continuous Bin" Verpackungsproblem ", dh wie viele Kartons der Größe $ k $ benötigt werden, um $ n $ Gegenstände zu verpacken.
Tomasz Gandor

Antworten:


54

Es gibt keinen Operator, der mit Ceil teilt. Sie müssen import mathund verwendenmath.ceil


also foobar = math.ceil (foo / bar)? Hmm, ich kann damit leben, weiß nicht, wo ich das verwenden wollte, war nur neugierig, danke
Cradam

37
–1 nicht verwenden , dies schlägt bei sehr großen Ganzzahlen fehl. Verwenden Sie entweder eine arithmetische Bibliothek mit mehrfacher Genauigkeit oder bleiben Sie bei diesem Ansatz in der Ganzzahldomäne .
wim

5
Bleiben Sie auf jeden Fall in der Integer-Domäne. Das ist fast garantiert leistungsfähiger und weniger Kopfschmerzen.
Samy Bencherif

1
@ David 天宇 Wong gmpy2 (in einer anderen Antwort hier erwähnt) ist gut.
wim

1
Beachten Sie, dass math.ceil auf 53 Bit Genauigkeit beschränkt ist. Wenn Sie mit großen Ganzzahlen arbeiten, erhalten Sie möglicherweise keine genauen Ergebnisse.
Techkuz

288

Sie können einfach eine verkehrte Bodenteilung durchführen:

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

Dies funktioniert, weil der Division-Operator von Python die Floor-Division durchführt (im Gegensatz zu C, wo die Integer-Division den Bruchteil abschneidet).

Dies funktioniert auch mit den großen Ganzzahlen von Python, da keine (verlustbehaftete) Gleitkommakonvertierung erfolgt.

Hier ist eine Demonstration:

>>> 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 Ich stimme zu, das ist sehr klug, aber nicht sehr lesbar und schwer zu pflegen! Ich habe beschlossen, Ceil aus der Mathematik zu importieren, damit einer meiner Kollegen, wenn er meine Codezeile liest, versteht, was sie bewirkt!
SlimCheney

2
@apadana Ich bin anderer Meinung. Die Frage stellte sich, ob es in Python einen Operator dafür gibt. Basierend auf den Antworten scheint die Antwort "Nein" zu sein. Ich stimme jedoch der Antwort von dlitz für ihre Nützlichkeit zu.
Ana Nimbus

11
@SlimCheney Werfen Sie diese Methode in eine dokumentierte Funktion und los geht's. Leistung + Lesbarkeit in einer einzigen Bewegung.
Samy Bencherif

2
@SamyBencherif: Nicht nur Leistung + Lesbarkeit, sondern auch Korrektheit für große Eingaben; Gleitkomma hat Darstellungsbeschränkungen, während Pythons intdies nicht tut (naja, keine aussagekräftigen; bei 64-Bit-Python sind Sie auf Bitnummern beschränkt 30 * (2**63 - 1)), und selbst das vorübergehende Konvertieren in floatkann zu Informationsverlust führen. Vergleiche math.ceil((1 << 128) / 10)mit -(-(1 << 128) // 10).
ShadowRanger

1
Dies sollte nur in der Standardbibliothek enthalten sein
Endolith

26

Sie tun können , (x + (d-1)) // dwenn Teilung xdurch d, dh (x + 4) // 5.


2
Dies ist die klassische Methode, die ich für immer verwendet habe. Funktioniert jedoch nicht für negative Teiler.
Mark Ransom

Es ergibt das gleiche Ergebnis wie math.ceil().
Abhijeet

3
@Abhijeet Ja, das ist es, was die Frage stellt. Außer es funktioniert besser für große Ganzzahlen oben sys.float_info.maxund es erfordert keinen Import.
Artyer

20

Lösung 1: Konvertieren Sie den Boden zur Decke mit Negation

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

Erinnert an den Penn & Teller-Levitationstrick , stellt dies "die Welt auf den Kopf (mit Negation), verwendet eine einfache Bodenteilung (wo Decke und Boden vertauscht wurden) und dreht dann die Welt mit der rechten Seite nach oben (wieder mit Negation). ""

Lösung 2: Lassen Sie divmod () die Arbeit machen

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

Die Funktion divmod () gibt (a // b, a % b)für ganze Zahlen an (dies kann bei Floats aufgrund eines Rundungsfehlers weniger zuverlässig sein). Der Schritt mit bool(r)addiert eins zum Quotienten, wenn ein Rest ungleich Null vorliegt.

Lösung 3: Stellen Sie den Zähler vor der Division ein

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

Verschieben Sie den Zähler nach oben, sodass die Bodenteilung auf die vorgesehene Decke abgerundet wird. Beachten Sie, dass dies nur für Ganzzahlen funktioniert.

Lösung 4: In float konvertieren, um math.ceil () zu verwenden

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

Der Code math.ceil () ist leicht zu verstehen, wird jedoch von Ints in Floats und zurück konvertiert. Dies ist nicht sehr schnell und kann zu Rundungsproblemen führen. Außerdem stützt es sich auf die Python 3-Semantik, bei der "true Division" einen Float erzeugt und bei der die Funktion lid () eine Ganzzahl zurückgibt.


1
In Schnelltests ist # 1 hier am schnellsten, selbst im Vergleich zu -(-a // b)o_O
Endolith

19

Sie können es auch immer einfach inline tun

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

In Python3 ist dies nur eine Größenordnung schneller als das Erzwingen der Float-Division und das Aufrufen von Ceil (), vorausgesetzt, Sie kümmern sich um die Geschwindigkeit. Was Sie nicht sollten, es sei denn, Sie haben durch die Verwendung bewiesen, dass Sie müssen.

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

Ich habe gerade diese Tests selbst durchgeführt. Ich bekomme ungefähr 12,5 Sekunden, ehrm, warum sollte mir die Geschwindigkeit nicht wichtig sein, wenn es ein so großer Geschwindigkeitsunterschied ist?
Cradam

3
@Cradam Beachten Sie, dass er 100 Millionen Anrufe tätigt ( number=100000000). Bei einem einzelnen Anruf ist der Unterschied ziemlich unbedeutend.
Rushy Panchal

4
Weil die Klarheit des Codes alle übertrifft. Klarheit ist in diesem Fall wahrscheinlich objektiv. Sie sollten jedoch immer zuerst lesbar / wartbar machen. Wenn und nur wenn Sie einen Leistungsprüfpunkt entdeckt haben, können Sie gegen die Regeln verstoßen. Moderne Maschinen sind so schnell und so oft führt all die anderen Dinge, die Ihr Programm tut, dazu, dass diese Art von Unterschied im Rauschen verloren geht.
Travis Griggs

6
@TravisGriggs, die Ganzzahlmathematik anstelle von Gleitkommamathematik verwenden, dienen nicht nur der Geschwindigkeit. Für ausreichend große ganze Zahlen gibt float math die falsche Antwort
Endolith

1
Wenn foo = -8und bar = -4zum Beispiel sollte die Antwort 2 sein, nicht 3, genau wie -8 // -4. Die Python-Bodenteilung ist definiert als "die mathematische Division mit der auf das Ergebnis angewendeten 'Boden'-Funktion", und die Deckenteilung ist dasselbe, jedoch mit ceil()statt floor().
Endolith

8

Beachten Sie, dass math.ceil auf 53 Bit Genauigkeit beschränkt ist. Wenn Sie mit großen Ganzzahlen arbeiten, erhalten Sie möglicherweise keine genauen Ergebnisse.

Die Bibliothek gmpy2 bietet eine c_divFunktion, die die Deckenrundung verwendet.

Haftungsausschluss: Ich pflege gmpy2.


3
Dieses Paket wäre nützlich, wenn ich etwas stark mathematisch oder naturwissenschaftlich orientiertes tun würde. Ich bevorzuge jedoch die Antwort, bei der Kernbibliotheken verwendet werden. Ich gebe jedoch eine Gegenstimme, da dies eine nützliche Antwort ist
Cradam

Wow, kann bestätigen. python2 -c 'from math import ceil;assert ceil(11520000000000000102.9)==11520000000000000000'(sowie zu ersetzen python3) BEIDE sindTrue
JamesTheAwesomeDude

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.