Existe um teto equivalente a // operador em Python?


127

Eu descobri sobre o //operador em Python que em Python 3 faz divisão com chão.

Existe um operador que divide com teto em vez disso? (Eu sei sobre o /operador que no Python 3 faz a divisão de ponto flutuante.)


1
Importante: você deseja um resultado int ou float?
smci

10
Você deve alterar a resposta aceita para dlitz. math.ceil é para floats, ele não funciona com ints longos de precisão arbitrária do Python.
endolith de

2
@milllimoose A questão é válida, porque 1) "divisão de teto" também é baseada em "divisão com módulo", 2) a matemática realmente não diz o que é comum e o que não é, 3) você precisa desta operação para "bin contínuo problema de embalagem ", ou seja, quantas caixas de tamanho $ k $ são necessárias para embalar $ n $ itens.
Tomasz Gandor

Respostas:


55

Não há operador que divida com teto. Você precisa import mathe usarmath.ceil


então foobar = math.ceil (foo / bar)? Hmm, eu posso viver com isso, não sei em nenhum lugar que eu gostaria de usar isso, estava apenas curioso, obrigado
Cradam

37
–1 não use , isso começará a falhar para inteiros muito grandes. Use uma biblioteca aritmética de precisão múltipla ou permaneça no domínio inteiro com esta abordagem.
wim

5
definitivamente permanecerá no domínio inteiro. isso é quase garantido para ter mais desempenho e menos dor de cabeça.
Samy Bencherif

1
@David 天宇 Wong gmpy2 (mencionado em outra resposta aqui) é bom.
wim

1
Observe que math.ceil é limitado a 53 bits de precisão. Se você estiver trabalhando com números inteiros grandes, pode não obter resultados exatos.
techkuz

291

Você pode simplesmente fazer a divisão do piso de cabeça para baixo:

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

Isso funciona porque o operador de divisão do Python faz a divisão de piso (ao contrário de C, onde a divisão inteira trunca a parte fracionária).

Isso também funciona com os grandes inteiros do Python, porque não há conversão de ponto flutuante (com perdas).

Aqui está uma demonstração:

>>> 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 Eu concordo que isso é muito inteligente, mas não muito legível e difícil de manter! Decidi importar ceil da matemática para que, quando um de meus colegas ler minha linha de código, ele entenda o que ela faz!
SlimCheney

2
@apadana eu discordo. A pergunta feita se "existe" um operador para isso "em" Python. Com base nas respostas, a resposta parece ser "não". No entanto, estou apoiando a resposta de dlitz por sua utilidade.
Ana Nimbus

12
@SlimCheney Jogue este método em uma função documentada e você está pronto para ir. Desempenho + legibilidade em um movimento abrangente.
Samy Bencherif

2
@SamyBencherif: Não apenas desempenho + legibilidade, mas também correção para grandes entradas; o ponto flutuante tem limitações de representação, enquanto o Python intnão tem (bem, nenhuma significativa; no Python de 64 bits você está limitado a 30 * (2**63 - 1)números de bits), e mesmo a conversão temporária para floatpode perder informações. Compare math.ceil((1 << 128) / 10)com -(-(1 << 128) // 10).
ShadowRanger

1
Isso deve ser incluído na biblioteca padrão
endólito de

26

Você poderia fazer (x + (d-1)) // dao dividir xpor d, ou seja (x + 4) // 5.


2
Este é o método clássico que sempre usei. No entanto, não funciona para divisores negativos.
Mark Ransom

Produz o mesmo resultado que math.ceil().
Abhijeet

3
@Abhijeet Sim, é isso que a pergunta faz. Exceto que funciona melhor para números inteiros grandes acima sys.float_info.maxe não requer importação.
Artyer

22

Solução 1: converta chão em teto com negação

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

Uma reminiscência do truque de levitação de Penn & Teller , isso "vira o mundo de cabeça para baixo (com negação), usa divisão de piso simples (onde o teto e o piso foram trocados) e, em seguida, vira o mundo do lado direito (com negação novamente) "

Solução 2: deixe divmod () fazer o trabalho

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

A função divmod () fornece (a // b, a % b)para inteiros (isso pode ser menos confiável com flutuações devido ao erro de arredondamento). O passo com bool(r)adiciona um ao quociente sempre que houver um resto diferente de zero.

Solução 3: Ajuste o numerador antes da divisão

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

Transforme o numerador para cima para que a divisão do piso seja arredondada para baixo até o teto pretendido. Observe que isso só funciona para inteiros.

Solução 4: converta em flutuantes para usar math.ceil ()

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

O código math.ceil () é fácil de entender, mas converte de ints para floats e vice-versa. Isso não é muito rápido e pode ter problemas de arredondamento. Além disso, ele se baseia na semântica do Python 3, em que a "verdadeira divisão" produz um float e a função ceil () retorna um inteiro.


2
Em testes rápidos, # 1 é o mais rápido aqui, mesmo em comparação com -(-a // b)o_O
endolith

Confirmando aqui que -(a // -b)é mais rápido do que -(-a // b), pelo menos ao cronometrar exemplos de brinquedos compython -m timeit ...
Jasha

19

Você sempre pode fazer isso inline também

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

Em python3, isso é apenas uma ordem de magnitude mais rápido do que forçar a divisão flutuante e chamar ceil (), desde que você se preocupe com a velocidade. O que você não deveria fazer, a menos que tenha provado através do uso que você precisa.

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

acabei de executar esses testes eu mesmo recebo cerca de 12,5 segundos, ehrm, por que não me importaria com a velocidade quando é uma diferença de velocidade tão grande?
Cradam

3
@Cradam Observe que ele está fazendo 100 milhões de chamadas ( number=100000000). Por chamada, a diferença é bem insignificante.
Rushy Panchal

4
Porque a clareza do código supera tudo. A clareza é objetiva neste caso provavelmente. Mas você deve sempre torná-lo legível / sustentável primeiro. Quando, e somente quando, você descobrir um ponto de verificação de desempenho, poderá quebrar as regras. As máquinas modernas são tão rápidas, e com frequência todas as outras coisas que seu programa está fazendo renderizam esse tipo de diferença perdida no ruído.
Travis Griggs

6
@TravisGriggs usando matemática de inteiros em vez de matemática de ponto flutuante não é apenas para velocidade. Para números inteiros grandes o suficiente, a matemática flutuante dá a resposta errada
endolith

1
Se foo = -8e bar = -4, por exemplo, a resposta deve ser 2, não 3, exatamente como -8 // -4. A divisão do piso Python é definida como "a divisão matemática com a função 'piso' aplicada ao resultado" e a divisão do teto é a mesma coisa, mas com em ceil()vez de floor().
endolith

8

Observe que math.ceil é limitado a 53 bits de precisão. Se você estiver trabalhando com números inteiros grandes, pode não obter resultados exatos.

A biblioteca gmpy2 fornece uma c_divfunção que usa arredondamento de teto.

Isenção de responsabilidade: eu mantenho o gmpy2.


3
Este pacote seria útil se eu estivesse fazendo algo fortemente orientado para a matemática ou ciências, mas prefiro a resposta que usa bibliotecas centrais. No entanto, estou dando um
voto positivo

Nossa, posso confirmar. python2 -c 'from math import ceil;assert ceil(11520000000000000102.9)==11520000000000000000'(bem como a substituição python3) AMBOS sãoTrue
JamesTheAwesomeDude

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.