Criação de população inicial para resolução do problema de partição de número com Algoritmo Genético codificado em Ruby



O problema de partição de números consiste em: dado um conjunto de N números, o objetivo é subdividi-lo em dois subconjuntos (chamados de partições) de tal forma que, a diferença entre os valores das somas dos números dessas duas partições seja a menor possível. Por exemplo, considere o seguinte conjunto com quatro números (23, 20, 56, 48). As partições (20,56) e (23,48) consistem no particionamento ótimo para este conjunto e, seu valor é 5. Apesar da simplicidade do enunciado, este é um problema de otimização combinatória que pertence à classe NP-difícil. Observe que, para um conjunto com N números têm-se 2N possíveis maneiras de subdividi-lo em duas partições.

 

Nosso problema consiste em dado uma seqüência [10,20,30,11,25,23,32,9,7,19,17,31,48,27,5,21,35,13,38,16,14,33,5] devemos criar um vetor de sinais (- ou +) para cada numero disposto, de modo a alcançarmos o menor valor (tendendo a 0) considerando o módulo do resultado das operações realizadas.

 

EX: para o vetor [10,20,30,11] geramos um vetor [1,1,0,0] considerei para esse exemplo que um representa + e 0 representa -, sendo assim nosso vetor seria [+,+,-,-]. O que geraria +10+20-30-11, resultando no valor 11, considerando seu módulo.

 

Entendido esse conceito vamos ao código.

 

Criaremos uma classe chamada Individuo com dois atributos @code e @value que recebem respectivamente o código binário (que representa os sinais) e o valor da função objetivo (ou em nosso caso o resultado da sentença) daquele individuo em questão.

 

[ruby]
class Individuo
attr_reader :code,:value
def initialize
@code = ((0+rand(8_388_608)).to_s(base=2)).rjust(23,"0")
@value = to_value(@code)
end

def to_value(binary)
a = [10,20,30,11,25,23,32,9,7,19,17,31,48,27,5,21,35,13,38,16,14,33,5]
sign = {"0"=>-1,"1"=>1}
value = index = 0
binary.each_char do |c|
value = value+a[index]*sign[c]
index+=1
end
(value<0 ? value*-1:value ) #MODULO
end

end
[/ruby]

E pronto, agora podemos gerar quantos indivíduos quisermos sem o menor esforço. Vou dar um exemplo de criação de 100 individuos.

[ruby]
população = 100.times.collect{Individuo.new}
[/ruby]

Não é tão difícil quanto parece. Podemos implementar uma segunda classe mais bonitinha para realizarmos nossos experimento abaixo um exemplo de implementação da classe Raca (Raça) que vai nos dar uma interface facilitadora para gerar nossa população.

[ruby]
require 'individuo'
class Raca

attr_reader :populacao,:quantidade,:populacao_ordenada
attr_accessor :valor_total

def initialize(quantidade)
@quantidade = quantidade
@populacao = criar_populacao(@quantidade)
end

#método que realiza a criação da população Inicial
def criar_populacao(quantidade)
quantidade.times.collect{Individuo.new}
end

#apenas ordena o vetor do melhor idividuo para o pior
def selecao_natural
@populacao.collect { |ind| [ind.value,ind.code] }.sort
end

#sobrescrevendo o to_s para uma visão melhor da população
def to_s
out =  "Valor".ljust(10)+"     Código Genético ".ljust(25)+"\n\n"
@populacao.each do |individuo|
out << "#{individuo.value.to_s.ljust(10)} #{individuo.code.ljust(25)}\n"
end
out
end
end
#rodando código
anfibios = Raca.new(12)
puts anfibios
[/ruby]

Esse código tem como resultado algo parecido com isso :

Valor          Código Genético  

1          11100010001111000001100 
73         00110110100011000001100 
29         10001011010011010011100 
237        01111011111110000111110 
5          11101000000110001101100 
99         01000010101001000011011 
173        01111110011110101001101 
35         11010011100010100010110 
109        10100010101000110001111 
23         10011001001101000110111 
135        01000111111101101011010 
119        10001100011001110010000

O Código está disponível aqui: [download id="6"]