Todos Os Numeros Pares - Determine o conjunto P, constituído de todos os números pares, de 120 a ...
Determine o conjunto P, constituído de todos os números pares, de 120 a ...

Gerando sequências de números pares em Python

Todo mundo que já precisou listar números pares em algum projeto sabe que a resposta mais óbvia nem sempre é a melhor. Começar pelo método básico e depois ajustar conforme a situação é o caminho mais seguro. Vou explicar como eu lido com isso na prática, com os problemas que encontrei e as soluções que funcionaram.

A rotina básica com todos os numeros pares

O jeito mais direto é usar range com step 2. range(0, 100, 2) já entrega uma sequência limpa sem esforço. Isso funciona bem para listas curtas, digamos até 10 mil números. A memória consumida é ridícula, quase insignificante. Mas aí veio meu primeiro problema real. Estava processando dados de uma Planilha de Controle Financeiro que precisava agrupar transações por período par. O intervalo era de 0 a 50 milhões. Quando executei range(0, 50_000_000, 2) e tentei converter para lista com list(), o script simplesmente travou. Memória RAM estourou em cerca de 3 segundos. A solução foi parar de materializar a lista e usar o próprio range como iterável. Em Python 3, range é lazy por padrão, ou seja, ele não guarda tudo na memória. Só gera o próximo número quando você pede. Aí o mesmo intervalo de 50 milhões rodou sem estourar nada, usando menos de 2 MB de RAM.

Outro detalhe que as pessoas costumam perder: range começa sempre no start que você definir. Se precisar de números pares positivos a partir de 2, use range(2, limite, 2). Se usar range(0, limite, 2), o zero entra na conta, o que em contextos de indexação pode causar bugs silenciosos. Já vi gente passar duas horas debugando um loop que processava um elemento a mais porque o zero estava sendo tratado como dado válido.

👉 Clique no botão abaixo para saber mais sobre o assunto!

Alternativas para cenários maiores

Quando o intervalo ultrapassa 100 milhões ou quando você precisa múltiplas iterações sobre os mesmos números pares, uma lista simples ainda é ruim. Nesse caso, numpy.genfromtxt ou um gerador próprio fazem mais sentido. Um gerador com yield é trivial de escrever e consome ainda menos memória que range em iterações repetidas, porque não cria nem a estrutura interna de um objeto range quando combinado com outras operações. Também vale considerar que em projetos que usam SQL, gerar números pares no banco costuma ser mais eficiente do que trazê-los para a aplicação. Uma CTE recursiva simples com número par pode processar milhões de linhas sem sair do banco. O downside é que isso depende do SGBD e da sua capacidade de escrever queries que não gerem full table scan. Em PostgreSQL, uma subconsulta com generate_series funciona bem. Em MySQL antigo, você precisa criar uma tabela de sequência manualmente.

Pegadinhas comuns

A primeira é confundir números pares com divisibilidade por 4. Todo número par é divisível por 2, mas nem todo é divisível por 4. Se seu critério é "pares que são também múltiplos de 4", o step muda para 4. range(0, 100, 4). Confundir isso leva a resultados errados que parecem certos porque a lógica visual é similar. A segunda é esquecer que números negativos também podem ser pares. range(-10, 10, 2) produz [-10, -8, -6, -4, -2, 0, 2, 4, 6, 8]. Em alguns contextos, como criptografia ou geração de hash, ignorar os negativos gera dados incompletos. É um erro técnico, não conceptual, mas custa tempo descobrir.

A terceira, e mais importante, é performance em loops aninhados. Se você está verificando se cada número de um conjunto maior é par usando uma função externa ao invés de operador módulo direto, o tempo de execução escala mal. isinstance(x, int) and x % 2 == 0 é mais lento que apenas x & 1 == 0 em ciclos intensos. A diferença é pequena por iteração, mas em milhões de chamadas vira minutos.

Quando isso não funciona

Geradores e range não resolvem tudo. Se você precisa acessar números pares por índice aleatório, tipo o décimo milhãoésimo número par de uma sequência, um gerador comum vai iterar do início até lá. Nesses casos, uma fórmula direta é melhor: o enésimo número par positivo é simplesmente n * 2. Sem iterar, sem memória, O(1) absoluto. Use isso quando a aleatoriedade de acesso for frequente. Também não adianta insistir em soluções puramente em Python quando o volume é enorme e o ambiente permite Rust ou C. Eu já medi diferença de 40x entre um loop em Python e o mesmo em Rust para processamento de sequências de bilhões de pares. Se o projeto permite mudar de linguagem, vale o investimento. Se não permite, ajuste as expectativas de tempo.