# 8.6. `bisect` — Algoritmo de bisseção de vetor

Código Fonte: Lib/bisect.py

Este módulo fornece suporte para manter uma lista em ordem de classificação sem ter que classificar a lista após cada inserção. Para longas listas de itens com operações de comparação custosas, isso pode ser uma melhoria em relação à abordagem mais comum. O módulo é denominado `bisect` porque usa um algoritmo de bisseção básico para fazer seu trabalho. O código-fonte pode ser mais útil como um exemplo funcional de algoritmo (as condições fronteiriças já estão certas!).

As seguintes funções são fornecidas:

`bisect.``bisect_left`(a, x, lo=0, hi=len(a))

  Localiza o ponto de inserção de x em a para manter a ordem de classificação. Os parâmetros lo e hi podem ser usados para especificar um subconjunto da lista que deve ser considerado; por padrão, toda a lista é usada. Se x já estiver presente em a, o ponto de inserção estará antes (à esquerda) de qualquer entrada existente. O valor de retorno é adequado para uso como o primeiro parâmetro para `list.insert()` supondo que a já esteja ordenado.

  O ponto de inserção retornado i particiona o vetor a em duas metades de modo que `all(val < x for val in a[lo:i])` para o lado esquerdo e `all(val >= x for val in a[i:hi])` para o lado direito.

`bisect.``bisect_right`(a, x, lo=0, hi=len(a))
`bisect.``bisect`(a, x, lo=0, hi=len(a))

  Semelhante a `bisect_left()`, mas retorna um ponto de inserção que vem depois (à direita de) qualquer entrada existente de x em a.

  O ponto de inserção retornado i particiona o vetor a em duas metades de modo que `all(val <= x for val in a[lo:i])` para o lado esquerdo e `all(val > x for val in a[i:hi])` para o lado direito.

`bisect.``insort_left`(a, x, lo=0, hi=len(a))

  Insere x em a na ordem de classificação. Isso é equivalente a `a.insert(bisect.bisect_left(a, x, lo, hi), x)` supondo que a já esteja ordenado. Lembre-se de que a pesquisa O(log n) é dominada pela lenta etapa de inserção O(n).

`bisect.``insort_right`(a, x, lo=0, hi=len(a))
`bisect.``insort`(a, x, lo=0, hi=len(a))

  Semelhante a `insort_left()`, mas inserindo x em a após qualquer entrada existente de x.

Ver também

Receita de SortedCollection que usa bisect para construir uma classe de coleção completa com métodos de pesquisa diretos e suporte para uma função chave. As chaves são pré-calculadas para economizar em chamadas desnecessárias para a função chave durante as pesquisas.

## 8.6.1. Pesquisando em listas ordenadas

As funções `bisect()` acima são úteis para encontrar pontos de inserção, mas podem ser complicadas ou difíceis de usar para tarefas comuns de pesquisa. As cinco funções a seguir mostram como transformá-las nas pesquisas padrão para listas ordenadas:

    def index(a, x):
        'Locate the leftmost value exactly equal to x'
        i = bisect_left(a, x)
        if i != len(a) and a[i] == x:
            return i
        raise ValueError

    def find_lt(a, x):
        'Find rightmost value less than x'
        i = bisect_left(a, x)
        if i:
            return a[i-1]
        raise ValueError

    def find_le(a, x):
        'Find rightmost value less than or equal to x'
        i = bisect_right(a, x)
        if i:
            return a[i-1]
        raise ValueError

    def find_gt(a, x):
        'Find leftmost value greater than x'
        i = bisect_right(a, x)
        if i != len(a):
            return a[i]
        raise ValueError

    def find_ge(a, x):
        'Find leftmost item greater than or equal to x'
        i = bisect_left(a, x)
        if i != len(a):
            return a[i]
        raise ValueError

## 8.6.2. Outros Exemplos

A função `bisect()` pode ser útil para pesquisas em tabelas numéricas. Este exemplo usa `bisect()` para pesquisar uma nota em letra para uma pontuação de exame (digamos) com base em um conjunto de pontos de interrupção numéricos ordenados: 90 e acima é um “A”, 80 a 89 é um “B” e por aí vai:

    >>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
    ...     i = bisect(breakpoints, score)
    ...     return grades[i]
    ...
    >>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
    ['F', 'A', 'C', 'C', 'B', 'A', 'A']

Ao contrário da função `sorted()`, não faz sentido que as funções `bisect()` tenham argumentos key ou reversed porque isso levaria a um design ineficiente (chamadas sucessivas para funções bisect não “lembrariam” de todas as pesquisas de chave anteriores).

Em vez disso, é melhor pesquisar uma lista de chaves pré-computadas para encontrar o índice do registro em questão:

    >>> data = [('red', 5), ('blue', 1), ('yellow', 8), ('black', 0)]
    >>> data.sort(key=lambda r: r[1])
    >>> keys = [r[1] for r in data]         # precomputed list of keys
    >>> data[bisect_left(keys, 0)]
    ('black', 0)
    >>> data[bisect_left(keys, 1)]
    ('blue', 1)
    >>> data[bisect_left(keys, 5)]
    ('red', 5)
    >>> data[bisect_left(keys, 8)]
    ('yellow', 8)