Esses dias saí pra descontrair e jogar um bilhar com meu amigo Romane.
No meio do jogo, ele soltou:
“Eu que estava me achando porque fiz uma função de busca genética!”
Achei que ele estava brincando... até me contar que criou uma função chamada buscaGenetica()
que ajudava a resolver um problema real: pagar boletos com um valor limitado.
💸 O problema dos boletos
“Imagine que o usuário informa um array de boletos, cada um com
id
evalor
, e diz que possui R\$1.000 para pagar o maior número possível deles.”
Ele usava algoritmos genéticos para encontrar a melhor combinação possível respeitando esse limite.
🧬 Como funciona essa tal de busca genética?
A explicação dele foi tão lúdica que parecia até um roteiro de ficção científica:
- 💑 Os boletos “casavam” entre si e geravam novas combinações.
- 🧟 Algumas gerações vinham com mutações, tipo boletos trocados por outros — o que ele chamava carinhosamente de “anomalias genéticas” (ou "cânceres" 😅).
- 👑 Boas combinações sobreviviam direto para a próxima geração.
- 🧠 E tudo isso se repetia por 100 gerações, até encontrar a solução ideal: o maior número de boletos pagos com até R\$1.000.
Fiquei vidrado. A ideia era poderosa.
⏰ O meu problema real: disponibilidade de horários
Na volta pra casa, a conversa não saía da minha cabeça.
Eu estava justamente quebrando a cabeça com um problema no meu sistema:
Tenho vários profissionais, com diferentes habilidades (banho, tosa, trimming), e preciso preencher os horários da agenda com os melhores profissionais, sem sobreposição.
E aí veio o clique:
E se eu aplicasse o algoritmo genético do Romane para montar essa agenda?
Transformei a lógica dele em um serviço PHP chamado GeneticAlgorithmService
.
💻 A implementação com PHP
A classe GeneticAlgorithmService
é genérica e recebe um objeto de configuração. Ela cuida de toda a evolução genética por trás da solução:
use App\Services\GeneticAlgorithmService; $config = [ "items" => [ ["id" => 1, "peso" => 10, "altura" => 50, "valor" => 60], ["id" => 2, "peso" => 20, "altura" => 60, "valor" => 100], ["id" => 3, "peso" => 30, "altura" => 70, "valor" => 120], ], "constraints" => [ ["attribute" => "peso", "max" => 50], ["attribute" => "altura", "max" => 110], ], "objective" => ["attribute" => "valor", "goal" => "maximize"], "populationSize" => 50, "generations" => 100, "mutationRate" => 0.05, "eliteRate" => 0.2, ]; $configObject = json_decode(json_encode($config), false); $service = new GeneticAlgorithmService($configObject); $result = $service->run(); echo "Melhor combinação encontrada:\n"; print_r($result);
📥 Entrada (configuração)
Você define:
-
items
: conjunto de dados (horários, boletos, tarefas, etc) -
constraints
: restrições (ex: peso máximo, limite de tempo, altura) -
objective
: qual atributo será maximizado ou minimizado - Parâmetros genéticos: tamanho da população, número de gerações, taxa de mutação e taxa de elite
📤 Saída
A função run()
retorna, por exemplo:
[ ['id' => 2, 'peso' => 20, 'altura' => 60, 'valor' => 100], ['id' => 1, 'peso' => 10, 'altura' => 50, 'valor' => 60] ]
Ou seja, a melhor combinação possível segundo os critérios definidos.
⚙️ O que o algoritmo faz por trás
Internamente, o GeneticAlgorithmService
executa:
- Geração da população inicial (combinações aleatórias de itens)
- Avaliação de "fitness" (quão boa é cada combinação)
- Seleção dos melhores indivíduos (elitismo)
- Cruzamento entre candidatos (reprodução)
- Mutação aleatória em descendentes
- Repetição por várias gerações
✅ Por que isso funciona tão bem?
Porque é flexível. Com esse padrão, você pode resolver:
- Agendas de atendimento
- Seleção de tarefas com prioridades
- Escalonamento de recursos
- Qualquer problema de otimização combinatória
⚙️ O algoritmo completo
Abaixo está o código completo da classe GeneticAlgorithmService
:
use Illuminate\Support\Collection; class GeneticAlgorithmService { private array $items; private array $constraints; private string $objectiveAttribute; private string $objectiveGoal; // 'maximize' ou 'minimize' private int $populationSize; private int $generations; private float $mutationRate; private float $eliteRate; private array $population = []; public function __construct(object $config) { $this->items = $config->items; $this->constraints = $config->constraints; $this->objectiveAttribute = $config->objective->attribute; $this->objectiveGoal = $config->objective->goal; $this->populationSize = $config->populationSize; $this->generations = $config->generations; $this->mutationRate = $config->mutationRate; $this->eliteRate = $config->eliteRate; } // Executa o algoritmo e retorna a lista de itens selecionados public function run(): array { $this->initializePopulation(); for ($gen = 0; $gen < $this->generations; $gen++) { $fitnessScores = $this->evaluatePopulation(); $elite = $this->selectElite($fitnessScores); $newPopulation = $elite; while (count($newPopulation) < $this->populationSize) { $parent1 = $this->tournamentSelection($fitnessScores); $parent2 = $this->tournamentSelection($fitnessScores); $child = $this->crossover($parent1, $parent2); $this->mutate($child); if ($this->checkConstraints($child)) { $newPopulation[] = $child; } } $this->population = $newPopulation; } $finalFitness = $this->evaluatePopulation(); $bestIndex = $this->getBestIndex($finalFitness); return $this->decodeChromosome($this->population[$bestIndex]); } // Inicializa população com cromossomos aleatórios private function initializePopulation(): void { $this->population = []; $numItems = count($this->items); for ($i = 0; $i < $this->populationSize; $i++) { $chromosome = []; for ($j = 0; $j < $numItems; $j++) { // 0 ou 1 para representar se o item está selecionado $chromosome[] = rand(0, 1); } // Garante que o cromossomo respeite as restrições if ($this->checkConstraints($chromosome)) { $this->population[] = $chromosome; } else { // Caso não respeite, tenta gerar outro até encontrar válido $i--; } } } // Avalia fitness de cada indivíduo na população private function evaluatePopulation(): array { $fitnessScores = []; foreach ($this->population as $chromosome) { $fitnessScores[] = $this->fitness($chromosome); } return $fitnessScores; } // Função fitness que calcula a pontuação do cromossomo private function fitness(array $chromosome): float { $totalObjective = 0; foreach ($chromosome as $index => $gene) { if ($gene === 1) { $totalObjective += $this->items[$index][$this->objectiveAttribute]; } } return $totalObjective; } // Seleciona os melhores indivíduos (elite) para a próxima geração private function selectElite(array $fitnessScores): array { $numElite = (int) round($this->populationSize * $this->eliteRate); // Associa fitness ao índice para ordenar $indexedFitness = array_map(fn($score, $i) => ['score' => $score, 'index' => $i], $fitnessScores, array_keys($fitnessScores)); usort($indexedFitness, function ($a, $b) { if ($this->objectiveGoal === 'maximize') { return $b['score'] <=> $a['score']; } return $a['score'] <=> $b['score']; }); $elite = []; for ($i = 0; $i < $numElite; $i++) { $elite[] = $this->population[$indexedFitness[$i]['index']]; } return $elite; } // Torneio para selecionar um cromossomo (parent) private function tournamentSelection(array $fitnessScores): array { $tournamentSize = 3; $candidates = []; $populationCount = count($this->population); for ($i = 0; $i < $tournamentSize; $i++) { $randomIndex = rand(0, $populationCount - 1); $candidates[] = ['chromosome' => $this->population[$randomIndex], 'fitness' => $fitnessScores[$randomIndex]]; } usort($candidates, function ($a, $b) { if ($this->objectiveGoal === 'maximize') { return $b['fitness'] <=> $a['fitness']; } return $a['fitness'] <=> $b['fitness']; }); return $candidates[0]['chromosome']; } // Crossover simples: um ponto de corte private function crossover(array $parent1, array $parent2): array { $length = count($parent1); $cut = rand(1, $length - 2); return array_merge( array_slice($parent1, 0, $cut), array_slice($parent2, $cut) ); } // Mutação com probabilidade definida private function mutate(array &$chromosome): void { for ($i = 0; $i < count($chromosome); $i++) { if (rand() / getrandmax() < $this->mutationRate) { $chromosome[$i] = $chromosome[$i] === 1 ? 0 : 1; } } } // Verifica se o cromossomo respeita as restrições private function checkConstraints(array $chromosome): bool { foreach ($this->constraints as $constraint) { $attribute = $constraint->attribute; $max = $constraint->max; $total = 0; foreach ($chromosome as $index => $gene) { if ($gene === 1) { $total += $this->items[$index][$attribute]; } } if ($total > $max) { return false; } } return true; } // Retorna o índice do melhor cromossomo baseado no fitness private function getBestIndex(array $fitnessScores): int { if ($this->objectiveGoal === 'maximize') { return array_keys($fitnessScores, max($fitnessScores))[0]; } return array_keys($fitnessScores, min($fitnessScores))[0]; } // Decodifica o cromossomo para retornar os itens selecionados private function decodeChromosome(array $chromosome): array { $selected = []; foreach ($chromosome as $index => $gene) { if ($gene === 1) { $selected[] = $this->items[$index]; } } return $selected; } }
🤖 Feito em PHP. Mas a inteligência é universal.
Embora eu tenha feito tudo isso com PHP, a lógica do algoritmo genético é agnóstica.
Se quiser usar JavaScript, Python, Go, Rust ou qualquer outra linguagem, você pode traduzir o código manualmente ou usar uma IA como o ChatGPT para converter tudo.
A inteligência está na ideia. A linguagem é só o meio. 😉
🧠 Pronto para evoluir?
Se você já precisou montar uma escala, agenda ou lista de prioridades com muitas variáveis cruzadas, sabe que usar força bruta é lento ou inviável. Algoritmos genéticos simulam a evolução natural para encontrar soluções surpreendentemente boas.
E o melhor: com PHP puro, de forma testável, limpa e extensível.
Top comments (0)