¿Existe un techo equivalente al operador // en Python?

127

Me enteré del //operador en Python que en Python 3 hace división con piso.

¿Existe un operador que divide con ceil en su lugar? (Sé sobre el /operador que en Python 3 hace la división de punto flotante).

Cradam
fuente
1
Importante: ¿quieres un resultado int o float?
smci
10
Debe cambiar la respuesta aceptada a la de dlitz. math.ceil es para flotantes, no funciona con las entradas largas de precisión arbitraria de Python.
endolito
2
@milllimoose La pregunta es válida, porque 1) "división ceil" también se basa en "división con módulo", 2) las matemáticas realmente no dicen qué es común y qué no, 3) necesitas esta operación para "bin continuo problema de embalaje ", es decir, cuántas cajas de tamaño $ k $ se necesitan para embalar $ n $ artículos.
Tomasz Gandor

Respuestas:

55

No hay operador que divida con ceil. Necesitas import mathy usamath.ceil

Carlos Salvia
fuente
entonces foobar = math.ceil (foo / bar)? Hmm, puedo vivir con eso, no sé de ningún lugar donde quisiera usar eso, solo tenía curiosidad, gracias
Cradam
37
–1 no se usa , esto comenzará a fallar para enteros muy grandes. Utilice una biblioteca aritmética de precisión múltiple o permanezca en el dominio de enteros con este enfoque.
wim
5
definitivamente permanecer en el dominio de enteros. eso es casi garantizado para ser más eficiente y menos dolor de cabeza.
Samy Bencherif
1
@David 天宇 Wong gmpy2 (mencionado en otra respuesta aquí) es bueno.
wim
1
Tenga en cuenta que math.ceil está limitado a 53 bits de precisión. Si trabaja con números enteros grandes, es posible que no obtenga resultados exactos.
techkuz
291

Puede hacer la división del piso al revés:

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

Esto funciona porque el operador de división de Python realiza la división de piso (a diferencia de C, donde la división de enteros trunca la parte fraccionaria).

Esto también funciona con los grandes enteros de Python, porque no hay conversión de punto flotante (con pérdida).

He aquí una demostración:

>>> 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
fuente
2
@apadana Estoy de acuerdo en que esto es muy inteligente, ¡pero no muy legible y difícil de mantener! ¡He decidido importar ceil de matemáticas para que cuando uno de mis colegas lea mi línea de código entienda lo que hace!
SlimCheney
2
@apadana No estoy de acuerdo. La pregunta fue si "hay" un operador para esto "en" Python. Según las respuestas, la respuesta parece ser "no". Sin embargo, estoy votando a favor de la respuesta de dlitz por su utilidad.
Ana Nimbus
12
@SlimCheney Mezcle este método en una función documentada y estará listo. Rendimiento + legibilidad en un movimiento de barrido.
Samy Bencherif
2
@SamyBencherif: No solo rendimiento + legibilidad, sino también corrección para entradas grandes; el punto flotante tiene limitaciones de representación, mientras que Python intno las tiene (bueno, no significativas; en Python de 64 bits está limitado a 30 * (2**63 - 1)números de bits), e incluso convertir temporalmente a floatpuede perder información. Comparar math.ceil((1 << 128) / 10)con -(-(1 << 128) // 10).
ShadowRanger
1
Esto debería incluirse en la biblioteca estándar
endolith
26

Podrías hacer (x + (d-1)) // dal dividir xpor d, es decir (x + 4) // 5.

dar un toque
fuente
2
Este es el método clásico que he usado desde siempre. Sin embargo, no funciona para divisores negativos.
Mark Ransom
Produce el mismo resultado que math.ceil().
Abhijeet
3
@Abhijeet Sí, eso es lo que pide la pregunta. Excepto que funciona mejor para los números enteros grandes anteriores sys.float_info.maxy no requiere una importación.
Artyer
22

Solución 1: convertir piso a techo con negación

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

Con reminiscencias del truco de levitación de Penn & Teller , esto "pone el mundo patas arriba (con negación), utiliza una división de piso simple (donde el techo y el piso se han intercambiado) y luego gira el mundo hacia arriba (con la negación nuevamente) "

Solución 2: deje que divmod () haga el trabajo

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

La función divmod () proporciona (a // b, a % b)números enteros (esto puede ser menos confiable con flotantes debido a un error de redondeo). El paso con bool(r)agrega uno al cociente siempre que haya un resto distinto de cero.

Solución 3: ajusta el numerador antes de la división

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

Traslade el numerador hacia arriba para que la división del piso se redondee hacia abajo hasta el techo previsto. Tenga en cuenta que esto solo funciona para números enteros.

Solución 4: convierta a flotantes para usar math.ceil ()

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

El código math.ceil () es fácil de entender, pero se convierte de ints a floats y viceversa. Esto no es muy rápido y puede tener problemas de redondeo. Además, se basa en la semántica de Python 3 donde la "división verdadera" produce un flotador y donde la función ceil () devuelve un número entero.

Raymond Hettinger
fuente
2
En pruebas rápidas, el n. ° 1 es el más rápido aquí, incluso en comparación con -(-a // b)o_O
endolito
Confirmando que aquí -(a // -b)es más rápido que -(-a // b), al menos cuando ejemplos de juguete con el tiempopython -m timeit ...
Jasha
19

Siempre puedes hacerlo en línea también

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

En python3, esto es apenas un orden de magnitud más rápido que forzar la división flotante y llamar a ceil (), siempre que le importe la velocidad. Lo cual no debería, a menos que haya probado a través del uso que lo necesita.

>>> timeit.timeit("((5 - 1) // 4) + 1", number = 100000000)
1.7249219375662506
>>> timeit.timeit("ceil(5/4)", setup="from math import ceil", number = 100000000)
12.096064013894647
Travis Griggs
fuente
Acabo de ejecutar esas pruebas yo mismo obtengo unos 12,5 segundos, ehrm, ¿por qué no me importaría la velocidad cuando hay una diferencia de velocidad tan grande?
Cradam
3
@Cradam Tenga en cuenta que está usando 100 millones de llamadas ( number=100000000). Por llamada, la diferencia es bastante insignificante.
Rushy Panchal
4
Porque la claridad del código triunfa sobre todo. Probablemente la claridad sea objetiva en este caso. Pero siempre debe hacer legible / mantenible primero. Cuando, y solo cuando, ha descubierto un punto de control de rendimiento, puede romper las reglas. Las máquinas modernas son tan rápidas, y muy a menudo todas las otras cosas que hace su programa hacen que este tipo de diferencia se pierda en el ruido.
Travis Griggs
6
@TravisGriggs usar matemáticas enteras en lugar de matemáticas de punto flotante no es solo para la velocidad. Para enteros lo suficientemente grandes, las matemáticas flotantes dan la respuesta incorrecta
endolito
1
Si foo = -8y bar = -4, por ejemplo, la respuesta debería ser 2, no 3, como -8 // -4. La división de piso de Python se define como "la división matemática con la función 'piso' aplicada al resultado" y la división de techo es lo mismo pero con en ceil()lugar de floor().
endolito
8

Tenga en cuenta que math.ceil está limitado a 53 bits de precisión. Si trabaja con números enteros grandes, es posible que no obtenga resultados exactos.

La biblioteca gmpy2 proporciona una c_divfunción que utiliza redondeo de techo.

Descargo de responsabilidad: mantengo gmpy2.

casevh
fuente
3
Este paquete sería útil si estuviera haciendo algo muy orientado a las matemáticas o la ciencia, aunque prefiero la respuesta que usa bibliotecas centrales. Sin embargo, estoy dando un
voto a favor,
Vaya, puedo confirmar. python2 -c 'from math import ceil;assert ceil(11520000000000000102.9)==11520000000000000000'(además de sustituir python3) AMBOS sonTrue
JamesTheAwesomeDude
-6

Solución simple: a // b + 1

AL Verminburger
fuente
2
Esto está mal para cualquier cosa que se divida uniformemente. a = 4, b = 2, etc.
endolito