Divisores E Multiplos - MÚltiplos e divisores – Artofit
MÚltiplos e divisores – Artofit

O problema que todo mundo encontra na hora H

Eu estava revisando código de um colega e ele tinha uma função que encontrava todos os divisores de um número inteiro verificando cada valor de 1 até N. Para números pequenos funciona, mas quando o input sobe para 10^12 o script leva minutos. A solução é mais simples do que parece. Basta iterar só até a raiz quadrada de N. Se i divide N, então N/i também é divisor. Isso reduz a complexidade de O(N) para O(N), o que muda completamente a prática. Divisores e múltiplos são conceitos que aparecem em praticamente qualquer situação que envolva números inteiros, mas a maioria das pessoas só os usa de forma superficial. Entender como funcionam na prática é o que separa quem resolve um problema rápido de quem fica recalculando até desistir.

Por que divisores e múltiplos importam no dia a dia técnico

Vamos começar por algo que não está em nenhum livro didático: a diferença entre saber a definição e saber aplicar. Um divisor de N é qualquer inteiro d tal que N mod d = 0. Um múltiplo de N é qualquer inteiro m tal que m = N × k para algum inteiro k. Isso é trivial. O que as pessoas não entendem é como essas definições se conectam com outras estruturas matemáticas que você vai encontrar em programação, criptografia e engenharia. Pegando um exemplo concreto. Digamos que você precisa sincronizar dois processos que rodam em intervalos diferentes: um a cada 12 segundos e outro a cada 18 segundos. Quando eles vão rodar juntos pela primeira vez? A resposta é o MMC(12, 18) = 36. Você poderia listar os múltiplos manualmente, mas isso não escala. O algoritmo correto usa o MDC (máximo divisor comum) via algoritmo de Euclides: MMC(a,b) = (a × b) / MDC(a,b). O algoritmo de Euclides é O(log(min(a,b))), basicamente instantâneo mesmo para números enormes.

Eu já vi engenheiros calcularemMMC manualmente fatorialmente e perderem duas horas porque não perceberam que podiam usar a relação com o MDC. O próprio Euclides era conhecido na antiguidade, então não tem desculpa para reinventar a roda.

Como calcular divisores de forma eficiente

O algoritmo ingênuo para encontrar todos os divisores de N é testar todos os números de 1 a N. Isso é O(N) e é inviável para qualquer número que tenha mais de alguns milhões de dígitos. O método prático que eu uso é: Para cada i de 1 até N: se N mod i == 0, então i é divisor e N/i também é divisor. Isso encontra todos os divisores em O(N) com no máximo duas descobertas por iteração.

Um detalhe importante que muita gente perde: se N for um quadrado perfeito, i == N/i quando i == N, então você não deve contar esse divisor duas vezes. Eu costumava colocar um flag dentro do loop pra verificar isso antes de adicionar ao resultado. Sem essa verificação, listas de divisores de quadrados perfeitos ficam com elementos duplicados e qualquer função que dependa dessa lista — como cálculo de soma de divisores ou fatoração — quebra silenciosamente. Outro ponto que merece atenção: fatoração prima é o passo fundamental por trás de tudo isso. Se você conhece a fatoração prima de N = p1^a1 × p2^a2 × ... × pk^ak, o número total de divisores é simplesmente (a1+1)(a2+1)...(ak+1). E os divisores podem ser gerados combinando as potências de forma sistemática. Isso transforma um problema que seria exponencial em um problema tratável, desde que a fatoração em si seja viável.

Aqui entra uma limitação séria que poucas pessoas mencionam: fatorar números grandes é computacionalmente difícil. Para números com mais de 20 dígitos, métodos como trial division e até o Quadratic Sieve já ficam inviáveis em tempo razoável. A criptografia RSA inteira se baseia nisso. Se você precisa trabalhar com números desse porte, não tente fatorar. Use algoritmos especializados como o General Number Field Sieve, que é o melhor conhecido atualmente, mas mesmo assim só funciona para números muito específicos e com infraestrutura dedicada.

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

Cálculo de MDC e MMC na prática

O algoritmo de Euclides para MDC é extremamente simples de implementar e funciona com números enormes sem esforço. A versão recursiva em Python é literalmente três linhas: def md c(a, b): return a if b == 0 else mdc(b, a % b)

Para MMC, a relação com MDC evita a armadilha do estouro de intermediário se você dividir antes de multiplicar: mmc(a,b) = (a // mdc(a,b)) * b. Note a divisão inteira primeiro. Fazer (a * b) // mdc(a,b) pode estourar a memória em linguagens com tipos fixos. Um caso edge que eu encontrei recentemente: quando você trabalha com bibliotecas de números grandes em linguagens como Python, o MDC de dois números de milhares de dígitos ainda é rápido porque o algoritmo de Euclides converge exponencialmente. Mas se você passar muitos pares consecutivos num loop sem otimização, o custo acumulado aparece. Eu otimizei um script que calculava MMC de uma sequência de 50.000 números ajustando para calcular o MDC de forma acumulativa, reduzindo o tempo de execução de cerca de 45 minutos para 3 minutos no mesmo hardware.

Erros comuns que você provavelmente está cometendo

O primeiro erro clássico é confundir divisor com fator primo. Todo fator primo é divisor, mas nem todo divisor é fator primo. Por exemplo, 6 é divisor de 12, mas 6 não é fator primo de 12. Os fatores primos de 12 são apenas 2 e 3. Essa confusão causa erros em problemas de contagem e combinatoriais. O segundo erro é achar que MMC sempre é maior que MDC. Tecnicamente, para quaisquer dois inteiros positivos a e b, MMC(a,b) max(a,b) e MDC(a,b) min(a,b). A desigualdade estrita MMC > MDC só falha quando a = b, onde ambos são iguais ao próprio número. Fora isso, o MMC é sempre maior ou igual ao maior dos dois números, e o MDC é sempre menor ou igual ao menor deles. Isso pode parecer óbvio, mas em implementações apressadas eu já vi gente assumir MMC(a,b) = a × b sem verificar se a e b são coprimos — o que só é verdade quando MDC(a,b) = 1.

Um terceiro erro, mais sutil, é não considerar negative numbers nas definições. Matematicamente, divisores podem ser negativos também. O conjunto completo de divisores de 12 inclui {-12, -6, -4, -3, -2, -1, 1, 2, 3, 4, 6, 12}. Na prática de programação, quase sempre nos restringimos aos divisores positivos, mas se seu problema exige o conjunto completo, esqueça isso e você vai ter bugs difíceis de rastrear.

Quando divisores e múltiplos não são a solução certa

Nem todo problema que parece exigir divisores e múltiplos realmente exige. Se você está lidando com números reais ou frações, o conceito de divisibilidade não se aplica da mesma forma. Divisores são definidos para inteiros. Tentar estender a noção para racionais gera ambiguidade — todo número racional divide todo outro número racional, o que torna o conceito vazio nesse contexto. Em problemas de scheduling ou sincronização com intervalos não inteiros, a abordagem de MMC não se aplica diretamente. Você precisa converter para uma unidade comum mínima (por exemplo, converter segundos e milissegundos para uma base unitária) antes de aplicar qualquer cálculo de múltiplos. Já perdi tempo debugando um sistema de agendamento que falhava porque os intervalos vinham em formatos mistos (segundos, minutos, horas) e eu tentava aplicar MMC diretamente sem unificar as unidades primeiro.

Se o seu problema envolve divisibilidade em anéis mais gerais — como polinômios ou números Gaussianos — as regras mudam completamente. Divisores em Z[i] (números inteiros Gaussianos) têm propriedades radicalmente diferentes dos inteiros comuns. Conheço desenvolvedores que tentaram adaptar algoritmos de divisores inteiros para esse contexto e tiveram resultados nonsense porque as definições de "primo" e "divisível" são distintas em anéis diferentes. O que funciona na prática, dependendo do cenário, é escolher a ferramenta certa. Para números pequenos (até ~10^9), o algoritmo de N é suficiente. Para fatoração de números médios (até ~10^18), trial division otimizada com pré-calculo de primos até 10^6 funciona. Para números grandes, você precisa de algoritmos especializados e, muitas vezes, aceitar que o problema pode ser intratável sem recursos computacionais significativos.

A regra geral que eu sigo é: entenda qual é o tamanho do seu input antes de escrever qualquer código. Um algoritmo O(N) pode ser perfeitamente aceitável para N até 10^6, mas catastrophicamente lento para N = 10^12. A diferença entre esses dois casos é a diferença entre rodar em milissegundos e rodar por dias. Não adianta ter a teoria correta se a implementação não leva em conta as restrições reais do problema.