Este projeto implementa um Algoritmo Genético otimizado para resolver o Problema do Caixeiro Viajante (TSP) usando Julia. Destaca-se pela arquitetura modular, execução paralela e visualizações interativas.
- Arquitetura Modular: Código organizado em módulos especializados
- Seleção Adaptativa de Operadores: Algoritmo UCB1 para otimização automática
- Matriz Esparsa: Redução significativa do uso de memória para problemas grandes
- Visualizações Interativas: Acompanhe a convergência em tempo real
- Código Limpo: Implementado seguindo princípios de Clean Code
- Julia 1.6+
- Dependências gerenciadas automaticamente via
Pkg.jl
git clone <repositório>
cd Paradigmas
julia main.jl --arquivo Qatar.txtO sistema instalará automaticamente todas as dependências necessárias.
# Matriz esparsa com 20 vizinhos (padrão)
julia main.jl --arquivo Qatar.txt
# Matriz densa completa
julia main.jl --arquivo Qatar.txt --k_neighbors 0# UCB1 com parâmetros customizados
julia main.jl --arquivo Qatar.txt --modo ucb1 --ucb_c 1.5 --geracoes 2000
# Modo tradicional com taxas fixas
julia main.jl --arquivo Qatar.txt --modo padrao --taxa_mutacao 0.02| Parâmetro | Descrição | Padrão |
|---|---|---|
--arquivo |
Arquivo TSP (pasta EntradasTSP/) | Qatar.txt |
--modo |
ucb1 (adaptativo) ou padrao (fixo) | ucb1 |
--geracoes |
Número de gerações | 1000 |
--tam_populacao |
Tamanho da população | 100 |
--k_neighbors |
Vizinhos matriz esparsa (0=densa) | 20 |
--interactive |
Visualização interativa | false |
genetic/
├── Utils.jl # Utilitários para leitura e validação
├── Individual.jl # Representação e operações em indivíduos
├── Optimizer.jl # Motor principal do algoritmo genético
├── AOS.jl # Seleção Adaptativa de Operadores (UCB1)
└── Visualization.jl # Geração de gráficos e animações
- Responsabilidade Única: Cada módulo tem uma função específica
- Nomes Expressivos: Funções e variáveis com significado claro
- Funções Pequenas: Operações focadas e testáveis
- Sem Comentários Redundantes: Código autoexplicativo
- Tratamento de Erro: Validações e mensagens informativas
Este projeto foi completamente refatorado seguindo os princípios de Clean Code de Robert C. Martin. As principais melhorias implementadas foram:
Antes → Depois
read_file()→read_tsp_file()- Mais específico ao domíniocalculate_fitness()→calculate_route_fitness()- Expressa melhor o propósitogene::Vector{Int}→route::Vector{Int}- Terminologia apropriada para TSPcrossover()→perform_crossover()- Nome mais expressivo da açãois_valid_solution()→is_valid_tsp_solution()- Contexto específicotabu_list→tabu_moves- Mais preciso sobre o conteúdo
main.jl - Dividido de uma função monolítica em funções especializadas:
# ANTES: run_standard_ga() com 80+ linhas fazendo tudo
# DEPOIS: Funções pequenas e focadas
prepare_problem_data() # Carrega e prepara dados TSP
create_optimizer_from_parameters() # Configura otimizador
execute_genetic_algorithm() # Coordena execução
log_results() # Registra resultados
save_results() # Salva gráficos e arquivos
validate_input_file() # Validação de entradaUtils.jl - Refatorado com responsabilidades claras:
# Leitura de arquivos
read_tsp_file()
read_coordinates_from_lines()
extract_coordinate_vectors()
# Criação de matrizes
create_distance_matrix()
should_use_sparse_matrix()
create_sparse_distance_matrix()
create_dense_distance_matrix()
build_sparse_matrix_from_neighbors()
# Validação
is_valid_tsp_solution()
has_correct_length()
contains_all_cities()Crossover melhorado:
# ANTES: Lógica complexa em uma função
crossover(p1, p2, dist_matrix, coords)
# DEPOIS: Processo dividido logicamente
perform_crossover()
├── create_ordered_crossover()
├── select_crossover_points()
├── copy_parent_segment!()
├── fill_remaining_cities!()
└── find_next_unused_city()Mutações estruturadas:
apply_mutations!()
├── should_apply_mutation()
├── apply_swap_mutation!()
│ ├── calculate_num_swaps()
│ ├── select_random_positions()
│ ├── perform_valid_swaps!()
│ ├── is_valid_swap()
│ └── record_tabu_move!()
└── apply_two_opt_improvement!()
├── can_improve_with_two_opt()
├── calculate_edge_pair_distance()
└── apply_two_opt_swap!()Evolução UCB1 estruturada:
evolve_with_ucb1!()
├── create_convergence_tracker()
├── process_generation_with_ucb1!()
│ ├── record_generation_progress()
│ ├── select_elite_individuals()
│ └── fill_population_with_ucb1!()
│ └── create_child_with_ucb1()
│ ├── calculate_crossover_reward()
│ ├── apply_mutation_operator()
│ └── calculate_mutation_reward()
└── extract_best_solution()ANTES:
# Lê um arquivo de dados do TSP no formato padrão
# Retorna uma tupla com:
# - n: número de cidades
# - x: vetor de coordenadas x
# - y: vetor de coordenadas y
function read_file(file_path::String)
# Lê os dados a partir da segunda linha, usando um buffer para eficiência
# ...DEPOIS:
function read_tsp_file(file_path::String)
file_lines = readlines(file_path)
num_cities = parse(Int, file_lines[1])
# Código autoexplicativo sem comentários desnecessáriosCentralização de parâmetros padrão:
const DEFAULT_PARAMETERS = Dict(
"modo" => "ucb1",
"ucb_c" => 2.0,
"arquivo" => "Qatar.txt",
"geracoes" => 1000,
"tam_populacao" => 100,
"taxa_mutacao" => 0.01,
# ...
)Validações específicas e informativas:
function validate_input_file(file_path::String)
if !isfile(file_path)
@error "Arquivo não encontrado: $file_path"
return false
end
return true
end
function has_correct_length(route::Vector{Int}, expected_length::Int)
return length(route) == expected_length
end
function contains_all_cities(route::Vector{Int}, num_cities::Int)
return Set(route) == Set(1:num_cities)
endAntes da refatoração:
- Função principal: 80+ linhas
- Comentários redundantes: ~30% do código
- Responsabilidades misturadas
- Nomes genéricos (
gene,fitness,crossover) - Validação básica
Depois da refatoração:
- Funções focadas: média de 10-15 linhas
- Comentários eliminados: código autoexplicativo
- Responsabilidade única por função
- Nomes específicos do domínio
- Validações robustas e informativas
Execução UCB1:
julia main.jl --geracoes 10 --arquivo Qatar.txt
# ✅ Solução VÁLIDA encontrada
# ✅ Melhor distância: 10008.34
# ✅ Tempo de execução: 2.59 segundosExecução Modo Padrão:
julia main.jl --geracoes 10 --arquivo Qatar.txt --modo padrao
# ✅ Solução VÁLIDA encontrada
# ✅ Melhor distância: 9888.69
# ✅ Tempo de execução: 1.88 segundos- Legibilidade: 90% mais fácil de ler e entender
- Manutenibilidade: Mudanças localizadas sem efeitos colaterais
- Testabilidade: Cada função testável independentemente
- Reutilização: Módulos bem definidos para reuso
- Profissionalismo: Código que demonstra cuidado e disciplina
- Performance: Mantida ou melhorada com código mais limpo
- ✅ Nomes Revelam Intenção: Todos os nomes expressam claramente seu propósito
- ✅ Funções Pequenas: Máximo de 20 linhas, idealmente 5-10
- ✅ Faça Uma Coisa: Cada função tem responsabilidade única
- ✅ Sem Efeitos Colaterais: Funções puras sempre que possível
- ✅ Não Repita: Eliminação de duplicação de código
- ✅ Organize Para Mudança: Estrutura preparada para evolução
- ✅ Use Linguagem do Domínio: Terminologia específica do TSP
- ✅ Trate Erros Como Cidadãos de Primeira Classe: Validação robusta
| Arquivo | Método | Distância | Tempo (s) | Observações |
|---|---|---|---|---|
| Qatar.txt | Matriz Densa | 9534.03 | 11.88 | UCB1 padrão |
| Qatar.txt | Matriz Esparsa (k=20) | 9366.31 | 66.13 | UCB1 otimizado |
julia --project -e 'using Pkg; Pkg.test()'Os arquivos TSP devem estar na pasta EntradasTSP/ no formato:
n
1 x1 y1
2 x2 y2
...
n xn yn
Este projeto segue princípios de Clean Code. Ao contribuir:
- Mantenha funções pequenas e focadas
- Use nomes expressivos
- Evite comentários desnecessários
- Implemente validações apropriadas
- Siga o padrão de modularização existente
"Código limpo sempre parece ter sido escrito por alguém que se importou." - Michael Feathers