sexta-feira, 18 de julho de 2008

Google Code Jam - Train Timetable

from Google Code Jam - july 2008....

Problem

A train line has two stations on it, A and B. Trains can take trips from A to B or from B to A multiple times during a day. When a train arrives at B from A (or arrives at A from B), it needs a certain amount of time before it is ready to take the return journey - this is the turnaround time. For example, if a train arrives at 12:00 and the turnaround time is 0 minutes, it can leave immediately, at 12:00.

A train timetable specifies departure and arrival time of all trips between A and B. The train company needs to know how many trains have to start the day at A and B in order to make the timetable work: whenever a train is supposed to leave A or B, there must actually be one there ready to go. There are passing sections on the track, so trains don't necessarily arrive in the same order that they leave. Trains may not travel on trips that do not appear on the schedule.

Input

The first line of input gives the number of cases, N. N test cases follow.

Each case contains a number of lines. The first line is the turnaround time, T, in minutes. The next line has two numbers on it, NA and NB. NA is the number of trips from A to B, and NB is the number of trips from B to A. Then there are NA lines giving the details of the trips from A to B.

Each line contains two fields, giving the HH:MM departure and arrival time for that trip. The departure time for each trip will be earlier than the arrival time. All arrivals and departures occur on the same day. The trips may appear in any order - they are not necessarily sorted by time. The hour and minute values are both two digits, zero-padded, and are on a 24-hour clock (00:00 through 23:59).

After these NA lines, there are NB lines giving the departure and arrival times for the trips from B to A.

Output

For each test case, output one line containing "Case #x: " followed by the number of trains that must start at A and the number of trains that must start at B.

Limits

1 ≤ N ≤ 100

Small dataset

0 ≤ NA, NB ≤ 20

0 ≤ T ≤ 5

Large dataset

0 ≤ NA, NB ≤ 100

0 ≤ T ≤ 60

Sample


Input

Output
2
5
3 2
09:00 12:00
10:00 13:00
11:00 12:30
12:02 15:00
09:00 10:30
2
2 0
09:00 09:01
12:00 12:02

Case #1: 2 2
Case #2: 2 0


SOLUTION

/**
sample file

2
5
3 2
09:00 12:00
10:00 13:00
11:00 12:30
12:02 15:00
09:00 10:30
2
2 0
09:00 09:01
12:00 12:02

*/
package codejam;

import java.io.File;
import java.io.FileInputStream;
import java.util.PriorityQueue;

/**
* @author Ricardo A. Harari - ricardo.harari@gmail.com
*
*/
public class TrainTimable {

PriorityQueue schedules = new PriorityQueue();

int turnaround = -1; // minutes

int n = 0;

private static final String FILE_NAME = "/tmp/sample.txt";

/**
* pass the full location of the sample file as argument
* if you dont pass anything it will search at /tmp/sample.txt
*
* @param args
*/
public static void main(String[] args) {
String fname = args.length == 0 ? FILE_NAME : args[0];
try {
TrainTimable o = new TrainTimable();
o.start(fname);
} catch (Exception e) {
System.out.println("Oppps.");
System.out.println("An exception has been throw:" + e.getMessage());
e.printStackTrace();
}
}

private void start(String fileName) throws Exception {
File f = new File(fileName); // sample file

int na = 0;
int nb = 0;

if (!f.exists() || !f.isFile()) throw new Exception("Sorry, " + fileName + " is not a valid file.");
FileInputStream fi = new FileInputStream(f);
int cint;
String tmp = new String();
int caseNum = 0;
int step = 0;
while ((cint = fi.read()) > -1) {
char c = (char) cint;
if (c == '\r' || c == '\n') {
tmp = tmp.trim();
if (tmp.length() > 0) {
if (step == 0) {
try {
n = Integer.parseInt(tmp);
step++;
} catch (NumberFormatException nfe) {
// do nothing. the 1st line should not be in correct format
}
} else if (step == 1) {
turnaround = Integer.parseInt(tmp);
caseNum++;
na = 0;
nb = 0;
schedules.clear();
step++;
} else if (step == 2) {
int pos = tmp.lastIndexOf(' ');
if (pos > -1) {
na = Integer.parseInt(tmp.substring(0, pos).trim());
nb = Integer.parseInt(tmp.substring(pos).trim());
}
step++;
} else if (na > 0) {
addToSchedule(tmp, true);
na--;
} else if (nb > 0) {
addToSchedule(tmp, false);
nb--;
}
if (step == 3 && na == 0 && nb == 0) {
process(caseNum);
turnaround = 0;
step = 1;
}
tmp = "";
}
} else {
tmp += c;
}
}
if (step == 3) {
process(caseNum);
}
fi.close();
}

/**
* @param caseNum
*/
private void process(int caseNum) {
int trainA = 0;
int trainB = 0;
int availableTrainA = 0;
int availableTrainB = 0;

while (schedules.size() > 0) {
Schedule sched = schedules.poll();
if (!sched.arrive) {
boolean isFromA = sched.source.equals("A");
if (isFromA) {
if (availableTrainA > 0) {
availableTrainA--;
} else {
trainA++;
}
} else {
if (availableTrainB > 0) {
availableTrainB--;
} else {
trainB++;
}
}
} else {
boolean isToB = sched.source.equals("B");
if (isToB) {
availableTrainB++;
} else {
availableTrainA++;
}
}
}
print(caseNum, trainA, trainB);
}

private long hourToLong(String time) {
int hour = Integer.parseInt(time.substring(0, 2));
int minute = Integer.parseInt(time.substring(3));
return hour*60 + minute;
}

/**
* @param tmp
* @param b
*/
private void addToSchedule(String tmp, boolean fromA) {
int pos = tmp.lastIndexOf(' ');
if (pos > -1) {
long start = hourToLong(tmp.substring(0, pos).trim());
long end = hourToLong(tmp.substring(pos).trim()) + turnaround;
schedules.add(new Schedule(start * 100000 + end, fromA ? "A" : "B", false));
schedules.add(new Schedule(end * 100000, fromA ? "B" : "A", true));
}
}

/**
* @param caso
* @param i
*/
private void print(int caso, int na, int nb) {
System.out.println("Case #" + caso + ": " + na + " " + nb);
}

/**
*
* @author Ricardo Alberto Harari - ricardo.harari@gmail.com
*
*/
class Schedule implements Comparable {

public Schedule(long _nextSchedule, String _source, boolean _arrive) {
nextSchedule = _nextSchedule;
source = _source;
arrive = _arrive;
}
long nextSchedule;
String source;
boolean arrive = false;

/* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Schedule o) {
if (nextSchedule - o.nextSchedule < 0) return -1;
if (nextSchedule - o.nextSchedule > 0) return 1;
return 0;
}
}

}

Google Code Jam - Saving the Universe

from Google Code Jam - july/2008....
http://code.google.com/codejam

Problem

The urban legend goes that if you go to the Google homepage and search for "Google", the universe will implode. We have a secret to share... It is true! Please don't try it, or tell anyone. All right, maybe not. We are just kidding.

The same is not true for a universe far far away. In that universe, if you search on any search engine for that search engine's name, the universe does implode!

To combat this, people came up with an interesting solution. All queries are pooled together. They are passed to a central system that decides which query goes to which search engine. The central system sends a series of queries to one search engine, and can switch to another at any time. Queries must be processed in the order they're received. The central system must never send a query to a search engine whose name matches the query. In order to reduce costs, the number of switches should be minimized.

Your task is to tell us how many times the central system will have to switch between search engines, assuming that we program it optimally.

Input

The first line of the input file contains the number of cases, N. N test cases follow.

Each case starts with the number S -- the number of search engines. The next S lines each contain the name of a search engine. Each search engine name is no more than one hundred characters long and contains only uppercase letters, lowercase letters, spaces, and numbers. There will not be two search engines with the same name.

The following line contains a number Q -- the number of incoming queries. The next Q lines will each contain a query. Each query will be the name of a search engine in the case.

Output

For each input case, you should output:

Case #X: Y
where X is the number of the test case and Y is the number of search engine switches. Do not count the initial choice of a search engine as a switch.

Limits

0 < N ≤ 20

Small dataset

2 ≤ S ≤ 10

0 ≤ Q ≤ 100

Large dataset

2 ≤ S ≤ 100

0 ≤ Q ≤ 1000

Sample


Input

Output
2
5
Yeehaw
NSM
Dont Ask
B9
Googol
10
Yeehaw
Yeehaw
Googol
B9
Googol
NSM
B9
NSM
Dont Ask
Googol
5
Yeehaw
NSM
Dont Ask
B9
Googol
7
Googol
Dont Ask
NSM
NSM
Yeehaw
Yeehaw
Googol

Case #1: 1
Case #2: 0

In the first case, one possible solution is to start by using Dont Ask, and switch to NSM after query number 8.
For the second case, you can use B9, and not need to make any switches.


SOLUTION

/**
*
*/
package codejam;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;

/**
* @author Ricardo Alberto Harari - ricardo.harari@gmail.com
*
*/
public class SavingUniverse {

private static final String FILE_NAME = "/tmp/sample.txt";


/**
* pass the full location of the sample file as argument
* if you dont pass anything it will search at /tmp/sample.txt
*
* @param args
*/
public static void main(String[] args) {
String fname = args.length == 0 ? FILE_NAME : args[0];
try {
SavingUniverse o = new SavingUniverse();
o.start(fname);
} catch (Exception e) {
System.out.println("Oppps.");
System.out.println("An exception has been throw:" + e.getMessage());
e.printStackTrace();
}
}

private int n = 0; // # of cases
ArrayList listEngines = null; // search engines nodes
ArrayList listQueries = null;

private void start(String fileName) throws Exception {
File f = new File(fileName); // sample file
int s = 0; // # of search engines
int q = 0; // # of queries

if (!f.exists() || !f.isFile()) throw new Exception("Sorry, " + fileName + " is not a valid file.");
FileInputStream fi = new FileInputStream(f);
int cint;
String tmp = new String();
int caseNum = 0;
while ((cint = fi.read()) > -1) {
char c = (char) cint;
if (c == '\r' || c == '\n') {
if (tmp.length() > 0) {
if (n == 0) {
try {
n = Integer.parseInt(tmp);
} catch (NumberFormatException nfe) {
// do nothing. the 1st line should not be in correct format
}
} else if (q >0) {
addQuery(tmp);
q--;
if (q == 0) {
process(caseNum);
s = 0;
}
} else if (s == 0) {
s = Integer.parseInt(tmp);
if (listEngines == null){
listEngines = new ArrayList();
listQueries = new ArrayList();
} else {
listEngines.clear();
listQueries.clear();
}
caseNum++;
} else if (s > 0) {
listEngines.add(new SEngineNode(tmp));
s--;
if (s == 0) s = -1;
} else if (s == -1) {
q = Integer.parseInt(tmp);
if (q == 0) { // no queries huh?
print(caseNum, 0);
s = 0;
}
}
tmp = "";
}
} else if (c != ' ') {
tmp += c;
}
}
if (n != 0 && q > 0) {
addQuery(tmp.toString());
q--;
if (q == 0) {
process(caseNum);
}
}
}

/**
*
*/
private void process(int caso) {
// check if the 1st node has value of 0
int counter = 0;
for (int i = 0; i <>
QueryNode qrynode = listQueries.get(i);
SEngineNode engine = qrynode.node;
ArrayList farNodeList = cloneEngines(engine);
int j = i + 1;
for (j = i; j <>
QueryNode qrynodenxt = listQueries.get(j);
removeEngine(farNodeList, qrynodenxt);
if (farNodeList.size() == 0) break;
}
if (farNodeList.size() == 0) {
counter++;
i = j - 1;
} else {
break;
}
}
print(caso, counter);
}

/**
* @param farNodeList
* @param qrynodenxt
*/
private void removeEngine(ArrayList farNodeList, QueryNode qrynodenxt) {
String s = qrynodenxt.query;
for (SEngineNode n : farNodeList) {
if (s.indexOf(n.engineName) > -1) {
farNodeList.remove(n);
break;
}
}
}

/**
* @return
*/
private ArrayList cloneEngines(SEngineNode exceptNode) {
ArrayList ret = new ArrayList();
for (SEngineNode node : listEngines) {
if (!node.engineName.equals(exceptNode.engineName)) ret.add(node);
}
return ret;
}

/**
* @param caso
* @param i
*/
private void print(int caso, int i) {
System.out.println("Case #" + caso + ": " + i);
}

/**
* @param string
*/
private void addQuery(String s) {
s = s.toUpperCase();
for (SEngineNode node : listEngines) {
if (s.indexOf(node.engineName) > -1) {
node.qtd++;
listQueries.add(new QueryNode(s, node));
break;
}
}
}

class QueryNode {
public QueryNode(String _query, SEngineNode _engine) {
query = _query;
node = _engine;
}
String query;
SEngineNode node;
}

class SEngineNode {
public SEngineNode(String _engineName) {
engineName = _engineName.toUpperCase().trim();
}
int qtd; // qtd that match this search engine
String engineName;
}

}


quarta-feira, 16 de julho de 2008

Ambiguous Codes

An extensive area of research in computer science is the field of communications. With computer networks being part of everyday life of many people, the development of ways for making networks faster, more reliable and secure is constantly needed. This practical need motivates an extensive research activity in the theory behind communications.

The very first thing needed to establish any kind of communication is a common code. A code is a way of changing the form of a piece of information into some other form, in general to make it possible to convey that piece of information from one place to another. Flag codes used by boats and the Morse code used in telegraphy are examples of codes for translating letters into different forms to enable communication over different media.

More formally, a code is a set of strings composed of symbols from one alphabet. Each string defined in the code is called a code word. A message is then composed concatenating a set of code words to convey the information needed. For example, in Morse code the alphabet is composed of the symbols hyphen and dot; letter ``S" is represented by the code word ``...", letter ``O" is represented by the code word ``--", and therefore the distress message ``SOS" in Morse code is ``...--...".

Codes for communication can have many desirable and undesirable properties such as ambiguity, entropy, redundancy, and many more. In this problem we will focus on ambiguity as a key property.

A code is ambiguous when there exists a message using that code that can be partitioned into different sequences of code words. In other words, in an ambiguous code a message may have more than one meaning. For example, consider the binary alphabet, composed of symbols {0,1}. For the code composed of the words {10, 01, 101} the message 10101 can be understood as 10-101 or 101-01 and therefore the code is ambiguous. On the other hand, for the code composed of the words {01, 10, 011} no ambiguous message exists and therefore the code is unambiguous.

As a part of the computer science community, you are required to develop a tester that checks if codes are ambiguous. In case a code is indeed ambiguous, you are also required to report the length (i.e. the number of symbols) of the shortest ambiguous message for that code.

Input

Each test case will consist on several lines. In all test cases the alphabet will be the set of hexadecimal digits (decimal digits plus the uppercase letters ``A" to ``F"). The first line of a test case will contain an integer N (1$ \le$N$ \le$100) , the number of code words in the code. Each of the next N lines describes a code word and contains a different and non-empty string of at most 50 hexadecimal digits.

Input is terminated by N = 0 .

Output

For each test case, output a single line with the length of the shortest ambiguous message for the provided code or `-1' if the code is unambiguous.

Sample Input

3
10
01
101
3
AB
BA
ABB
0

Sample Output

5

Solution


/**
* Exemplo para resolver o problema de AmbiguousCode
* Este programa usa uma árvore ordenada priorizada pelos elementos de
* menor tamanho. Usa um hash para guardar os valores encontrados e se encontrar
* outro igual gera uma exception onde a mensagem é a String ambigua.
*
* Não implementei para ler de um arquivo texto, como no enunciado. Isto é muito fácil ;)
*
*
* @author Ricardo Alberto Harari - ricardo.harari@gmail.com
*/
package codejam;

import java.util.concurrent.PriorityBlockingQueue;

import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable;

/**
* @author Ricardo A Harari - ricardo.harari@gmail.com
*
*/
public class AmbiguousCode {

private final String[] array = new String[] { "01", "AB", "BA", "ABB", "10", "101" };
//private final String[] array = new String[] { "AB", "BA", "ABB" };

private final Hashtable ht = new Hashtable();

/**
*
* @param pq
* @throws Exception
*/
private void process(PriorityBlockingQueue pq) throws Exception {
if (pq.size() == 0) throw new Exception("-1");
Node father = pq.poll();
for (int j = 0; j <>
Node child = new Node();
child.father = father;
child.step = child.father.step + 1;
child.valueFull = child.father.valueFull;

String s = array[j];
Node check = child.father;
boolean found = false;
while (!found && check != null) {
found = check.value.equals(s);
check = check.father;
}
if (!found) {
child.value = s;
child.valueFull += s;
if (ht.get(child.valueFull) != null) throw new Exception(child.valueFull);
ht.put(child.valueFull, child.valueFull);
pq.add(child);
}
}
}

/**
*
* @param args
*/
public static void main(String[] args) {
AmbiguousCode c = new AmbiguousCode();
c.start();
}

/**
* inicio do processo
*/
private void start() {
try {
PriorityBlockingQueue pq = new PriorityBlockingQueue(array.length * array.length);
pq.clear();
// adiciona os primeiros elementos na priorityqueue
for (int i = 0; i <>
Node n = new Node();
n.value = array[i];
n.valueFull = n.value;
if (ht.get(array[i]) != null) throw new Exception(array[i]);
ht.put(n.value, n.value);
pq.offer(n);
}
// enquanto tiver elementos, processar
while (pq.size() > 0) {
process(pq);
}
System.out.println("-1");
} catch (Exception e) {
System.out.println(e.getMessage().length());
}
}

/**
* nó
*
* @author Ricardo Alberto Harari - ricardo.harari@gmail.com
*
*/
class Node implements Comparable {
Node father = null;
String value = null;
String valueFull = null;
int step = 0;

/*
* o segredo esta em comparar o tamanho da string
*
* desta forma as menores serão processadas primeiro.
*
* (non-Javadoc)
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Node arg0) {
int l1 = valueFull.length();
int l2 = arg0.valueFull.length();
return l1 - l2;
}
}

}

quarta-feira, 9 de julho de 2008

Nova heuristica para o Quebra Cabeca

Veja como uma simples modificação na heuristica do algoritmo do quebra-cabeca pode melhorar significativamente o processamento.
O conceito agora é determinar a qual distancia o estado atual esta da solucao.
Seja o estado:

+---+---+---+
+ 1 + 2 + 3 +
+---+---+---+
+ 0 + 5 + 6 +
+---+---+---+
+ 4 + 7 + 8 +
+---+---+---+

teremos como retorno da heuristica o valor:
retorno = 0 + 0 + 0 + 1 + 0 + 0 + 1 + 1 + 5 = 8

pois os numeros 1,2,3 e 6 estao já na posicao correta, gerando 0 de distancia.
O numero 4, 7 e 8 estao na distancia de 1 posicao e o numero 0 esta na distancia de 5.
O correto seria o numero 0 estar a uma distancia de 3 que é como ele se move no tabuleiro. Movendo-se 2 posicoes para a direita e 1 posicao para baixo. Isto é conhecido como distancia Manhattan e seria uma representação mais real para os passos necessários para deixar o 0 na posição correta. Mesmo tendo implementado considerando a distancia de uma matriz unidimensional o algoritmo resolveu o problema a um custo de 64 passos e expandiu 262 nós para chegar na solução.

Abaixo o código modificado. Como agora o que queremos é minimizar a função heuristica, foi alterada tambem a inner class ComparadorNo retornando agora d1-d2 pois as melhores soluções são aquelas onde a distancia é menor.



import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Set;

/**
* @author Ricardo Alberto Harari - ricardo.harari@gmail.com
*
*/
public class QuebraCabeca {

static int CIMA = 1;
static int BAIXO = 2;
static int DIREITA = 3;
static int ESQUERDA = 4;

static boolean printOn = true;

static int tamanho_fila = 0;
static int tamanho_fila_max = 0;

static double custo_solucao = 0.0;

static int nos_expandidos = 0;

static int dimensao = 3;

static int[] solucao = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 0 };

static int[] inicio = new int[] { 8, 4, 0, 5, 1, 7, 6, 2, 3 };

static PriorityQueue fila = new PriorityQueue(5, new ComparadorNo());
static Set processados = new HashSet();

public static void main(String[] args) {
long l = System.currentTimeMillis();
No no = iniciarBusca(inicio);
if (no != null) {
imprimirResultado(no, true);
} else {
System.out.println("SOLUCAO NAO ENCONTRADA!");
}
System.out.println("\r\n\r\nElapsed time=" + (System.currentTimeMillis() - l) + "ms");
}

/**
* efetua a busca da solucao retorna a solucao ou em caso de nao encontrar null
*
* @param posicoesIniciais
* @return
*/
private static No iniciarBusca(int[] posicoesIniciais) {
No noInicial = new No();
noInicial.estado = posicoesIniciais;
fila.add(noInicial);
tamanho_fila = 1;
while (!(fila.isEmpty())) {
tamanho_fila--;
No no = fila.remove();
if (goal(no.estado)) {
custo_solucao = no.custoCaminho;
return no;
}
adicionarNosAlternativosFila(no);
tamanho_fila = fila.size();
if (tamanho_fila_max < tamanho_fila) tamanho_fila_max = tamanho_fila; // estatistica
}
return null; // falhou
}

/**
* imprimir o resultado da solucao
*
* @param no
* @param imprimeTabuleiros
*/
private static void imprimirResultado(No no, boolean imprimeTabuleiros) {
print("ESTADO FINAL:");
print("===============");
imprimirTabuleiro(no);
print("===============");
print("tamanho_fila_max = " + tamanho_fila_max);
print("nos_expandidos = " + nos_expandidos);
print("===============");
print("OPERACOES REVERSA:");

while (no.pai != null) {
print("===============");
print("Step: " + no.step);
print("Custo:" + no.custoCaminho);
print("Acao:" + getAcaoReversa(no.acao));
no = no.pai;
if (imprimeTabuleiros) imprimirTabuleiro(no);
}
}

/**
* retorna o nome da acao, nao do calhau, mas do bloco a ser movido
*
* @param acao
* @return
*/
private static String getAcaoReversa(int acao) {
switch (acao) {
case 1:
return "BAIXO";
case 2:
return "CIMA";
case 3:
return "ESQUERDA";
case 4:
return "DIREITA";
}
return "NENHUMA";
}

/**
* @param no
*/
private static void imprimirTabuleiro(No no) {
if (!printOn) return;
for (int i = 0; i < dimensao; i++) {
imprimirTabuleiroLinha();
for (int j = 0; j < dimensao; j++) {
int n = i * dimensao + j;
System.out.print("+ " + no.estado[n] + " ");
}
System.out.println("+");
}
imprimirTabuleiroLinha();
}

/**
* imprime uma linha separadora do tabuleiro
*/
private static void imprimirTabuleiroLinha() {
if (!printOn) return;
for (int i = 0; i < dimensao; i++) {
System.out.print("+---");
}
System.out.println("+");
}

/**
* imprime 's'
*
* @param s
*/
private static void print(String s) {
if (printOn) System.out.println(s);

}

/**
* esta eh a chave do problema, determinar uma fucao heuristica que retorne
*
* o quanto esta perto da solucao neste problema a heuristica eh calculada
*
* verificando quantos blocos estao na posicao correta - isto eh, de acordo
*
* com a matriz 'solucao'
*
* @param estado
* @return
*/
private static double heuristica(int[] estado) {
double valor = 0.0;
for (int i = 0; i <>
int vestado = estado[i]; // localizar a posição de vestado na solução
for (int k = 0; k <>
if (solucao[k] == vestado) {
valor += Math.abs(k - i);
break;
}
}
}
return valor;
}

private static boolean goal(int[] estado) {
for (int i = 0; i < dimensao * dimensao; i++) {
if (estado[i] != solucao[i]) return false;
}
return true;
}

private static List recuperarSucessores(int[] estado) {
List filhos = new ArrayList();
if (podeMoverCalhau(estado, CIMA)) {
int[] novoEstado = clonar(estado);
moverCima(novoEstado);
filhos.add(new Sucessor(novoEstado, CIMA));
}
if (podeMoverCalhau(estado, ESQUERDA)) {
int[] novoEstado = clonar(estado);
moverEsquerda(novoEstado);
filhos.add(new Sucessor(novoEstado, ESQUERDA));
}
if (podeMoverCalhau(estado, DIREITA)) {
int[] novoEstado = clonar(estado);
moverDireita(novoEstado);
filhos.add(new Sucessor(novoEstado, DIREITA));
}
if (podeMoverCalhau(estado, BAIXO)) {
int[] novoEstado = clonar(estado);
moverBaixo(novoEstado);
filhos.add(new Sucessor(novoEstado, BAIXO));
}
return filhos;
}

private static void moverCima(int[] estado) {
int pos = 0;
for (int i = dimensao; i < dimensao * dimensao; i++) {
if (estado[i] == 0) {
pos = i;
break;
}
}
if (pos > 0) {
int valor = estado[pos - dimensao];
estado[pos - dimensao] = 0;
estado[pos] = valor;
}
}

private static void moverBaixo(int[] estado) {
int pos = 0;
for (int i = 0; i < dimensao * dimensao; i++) {
if (estado[i] == 0) {
pos = i;
break;
}
}
int valor = estado[pos + dimensao];
estado[pos + dimensao] = 0;
estado[pos] = valor;
}

private static void moverEsquerda(int[] estado) {
int pos = 0;
for (int i = 0; i < dimensao * dimensao; i++) {
if (estado[i] == 0) {
pos = i;
break;
}
}
int valor = estado[pos - 1];
estado[pos - 1] = 0;
estado[pos] = valor;
}

private static void moverDireita(int[] estado) {
int pos = 0;
for (int i = 0; i < dimensao * dimensao; i++) {
if (estado[i] == 0) {
pos = i;
break;
}
}
int valor = estado[pos + 1];
estado[pos + 1] = 0;
estado[pos] = valor;
}

/**
* expandir e adicionar nós subsequentes na fila
*
* @param no
*/
private static void adicionarNosAlternativosFila(No no) {
if (!(processados.contains(no.toString()))) {
processados.add(no.toString());
List expandidos = expandirNos(no);
for (No o : expandidos) {
fila.add(o);
}
}
}

/**
* retorna um estado clonado
*
* @param estado
* @return
*/
private static int[] clonar(int[] estado) {
int[] ret = new int[estado.length];
for (int i = 0; i < estado.length; i++) {
ret[i] = estado[i];
}
return ret;
}

/**
* expandir o no com os seus sucessores possiveis
*
* @param no
* @return
*/
private static List expandirNos(No no) {
List nos = new ArrayList();
List proximos = recuperarSucessores(no.estado);
for (Sucessor prox : proximos) {
No no0 = new No();
no0.pai = no;
no0.estado = prox.estado;
no0.step = no.step + 1;
no0.acao = prox.acao;
// o custo é sempre 1 pois movemos 1 bloco a cada passo
no0.custoStep = 1.0;
no0.custoCaminho += no0.pai.custoCaminho + 1.0;
nos.add(no0);
// if (!processados.contains(no0.toString())) nos.add(no0);
}
nos_expandidos++;
return nos;
}

/**
* verifica se o calhau pode ser movido para uma direcao(acao)
*
* @param estado
* @param acao
* @return
*/
private static boolean podeMoverCalhau(int[] estado, int acao) {
int posicao = 0;
for (int i = 0; i < dimensao * dimensao; i++) {
if (estado[i] == 0) {
posicao = i;
break;
}
}
if (acao == ESQUERDA) {
while (posicao >= 0) {
if (posicao == 0) return false;
posicao -= dimensao;
}
} else if (acao == CIMA) {
if (posicao >= 0 && posicao < dimensao) return false;
} else if (acao == DIREITA) {
posicao++;
while (posicao >= dimensao) {
if (posicao == dimensao) return false;
posicao -= dimensao;
}
} else if (acao == BAIXO) {
if (posicao >= dimensao * (dimensao - 1)) return false;
}
return true;
}

/**
* representa um estado sucessor do estado atual dada uma acao
*
* @author Ricardo A Harari - ricardo.harari@gmail.com
*
*/
static class Sucessor {
public Sucessor(int _estado[], int _acao) {
estado = _estado;
acao = _acao;
}

public int[] estado;
public int acao;
}

/**
* representa um nó de um grafo possui o estado, o nó pai,
*
* a acao, o custo para chegar até este nó, a profundidade do nó
*
* e o custo unitário para ir do pai até o filho
*
* @author Ricardo A Harari - ricardo.harari@gmail.com
*
*/
static class No {
public int[] estado;
public int acao;
public No pai;
public int step = 0;
public double custoCaminho = 0.0;
public double custoStep = 0.0;

public List recuperarArvore() {
No atual = this;
List ret = new ArrayList();
while (!(atual.pai != null)) {
ret.add(0, atual);
atual = atual.pai;
}
ret.add(0, atual);
return ret;
}

@Override
public String toString() {
String ret = "";
for (int i = 0; i < dimensao * dimensao; i++) {
ret += estado[i];
}
return ret;
}

@Override
public boolean equals(Object o) {
if ((o == null) || (this.getClass() != o.getClass())) return false;
if (this == o) return true;
No x = (No) o;
for (int i = 0; i < dimensao; i++) {
if (estado[i] != x.estado[i]) {
return false;
}
}
return true;
}
}

/**
* comparador de Nós chamando a heuristica
*
* @author Ricardo A Harari - ricardo.harari@gmail.com
*
*/
static class ComparadorNo implements Comparator {
/*
* (non-Javadoc)
*
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@Override
public int compare(No o1, No o2) {
double d1 = heuristica(o1.estado);
double d2 = heuristica(o2.estado);
return (int) (d1 - d2);
}
}

}



terça-feira, 8 de julho de 2008

Algoritmo para Quebra-Cabeça

Este exemplo mostra um algorítmo para resolver aqueles quebra-cabeças formados por blocos de numeros, onde é possivel mover uma peça de cada vez e o objetivo é deixar os numeros alinhados (1, 2, 3, 4, .... ):

+---+---+---+
+ 1 + 2 + 3 +
+---+---+---+
+ 4 + 5 + 6 +
+---+---+---+
+ 7 + 8 + +
+---+---+---+

O segredo deste algorítmo esta na definição da função heuristica que irá determinar um valor para cada estado do tabuleiro. O objetivo é maximizar este valor, isto é, a solução será encontrada quando o valor for 8.
A função calcula a quantidade de números que estão alinhados com a solução.
Por exemplo, se tivermos um tabuleiro na seguinte configuração:

+---+---+---+
+ 4 + 2 + 3 +
+---+---+---+
+ 1 + 5 + +
+---+---+---+
+ 7 + 8 + 6 +
+---+---+---+

teremos como resultado 5 pois os numeros 2, 3, 5, 7 e 8 estão posicionados corretamente.
O algoritmo irá efetuar uma pesquisa numa arvore de solução onde a cada iteração estará expandindo a arvore e depois ordenando pelos melhores resultados e estes terão preferencia de processamento. Existe uma coleção de estados processados onde é verificado se o novo estado já foi processado e caso afirmativo é eliminado da fila de processamento.

O problema inicial foi configurado como:

+---+---+---+
+ 8 + 4 + 0 +
+---+---+---+
+ 5 + 1 + 7 +
+---+---+---+
+ 6 + 2 + 3 +
+---+---+---+

O numero '0' é o calhau, isto é, o espaço vazio onde no exemplo acima o 4 pode ser movido para a direita ou o 7 movido para cima.
Cada novo passo tem um novo custo de 1.0 ponto.
Após rodar o algoritmo, a solução foi encontrada e foi necessário expandir 1013 nós e o custo da solução ficou em 130. Expandir 1013 nós significa que o algoritmo produziu 1013 configurações de tabuleiros para encontrar a solução e que 130 seriam suficientes, para este algoritmo, encontrar a solução.
Evidente que ainda não estamos falando de Inteligencia Artificial mas sim de pesquisa operacional. O codigo a seguir pode ser melhorado dando a capacidade do algorítmo aprender com a experiencia mas isso será assunto para discutir no próximo artigo.
A função heuristica pode ser melhorada também, estarei fazendo isso em breve e publicarei uma função que agilizará a solução do problema.

Voce podera alterar a configuração inicial para outra qualquer, basta alterar a matriz "inicio". Poderá aumentar o tamanho do tabuleiro para uma dimensao maior. Para isso altere a matriz inicio, a matriz solucao e a constante dimensao.

Vamos ao código:


import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Set;

/**
* @author Ricardo Alberto Harari - ricardo.harari@gmail.com
*
*/
public class QuebraCabeca {

static int CIMA = 1;
static int BAIXO = 2;
static int DIREITA = 3;
static int ESQUERDA = 4;

static boolean printOn = true;

static int tamanho_fila = 0;
static int tamanho_fila_max = 0;

static double custo_solucao = 0.0;

static int nos_expandidos = 0;

static int dimensao = 3;

static int[] solucao = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 0 };

static int[] inicio = new int[] { 8, 4, 0, 5, 1, 7, 6, 2, 3 };

static PriorityQueue fila = new PriorityQueue(5, new ComparadorNo());
static Set processados = new HashSet();

public static void main(String[] args) {
long l = System.currentTimeMillis();
No no = iniciarBusca(inicio);
if (no != null) {
imprimirResultado(no, true);
} else {
System.out.println("SOLUCAO NAO ENCONTRADA!");
}
System.out.println("\r\n\r\nElapsed time=" + (System.currentTimeMillis() - l) + "ms");
}

/**
* efetua a busca da solucao retorna a solucao ou em caso de nao encontrar null
*
* @param posicoesIniciais
* @return
*/
private static No iniciarBusca(int[] posicoesIniciais) {
No noInicial = new No();
noInicial.estado = posicoesIniciais;
fila.add(noInicial);
tamanho_fila = 1;
while (!(fila.isEmpty())) {
tamanho_fila--;
No no = fila.remove();
if (goal(no.estado)) {
custo_solucao = no.custoCaminho;
return no;
}
adicionarNosAlternativosFila(no);
tamanho_fila = fila.size();
if (tamanho_fila_max < tamanho_fila) tamanho_fila_max = tamanho_fila; // estatistica
}
return null; // falhou
}

/**
* imprimir o resultado da solucao
*
* @param no
* @param imprimeTabuleiros
*/
private static void imprimirResultado(No no, boolean imprimeTabuleiros) {
print("ESTADO FINAL:");
print("===============");
imprimirTabuleiro(no);
print("===============");
print("tamanho_fila_max = " + tamanho_fila_max);
print("nos_expandidos = " + nos_expandidos);
print("===============");
print("OPERACOES REVERSA:");

while (no.pai != null) {
print("===============");
print("Step: " + no.step);
print("Custo:" + no.custoCaminho);
print("Acao:" + getAcaoReversa(no.acao));
no = no.pai;
if (imprimeTabuleiros) imprimirTabuleiro(no);
}
}

/**
* retorna o nome da acao, nao do calhau, mas do bloco a ser movido
*
* @param acao
* @return
*/
private static String getAcaoReversa(int acao) {
switch (acao) {
case 1:
return "BAIXO";
case 2:
return "CIMA";
case 3:
return "ESQUERDA";
case 4:
return "DIREITA";
}
return "NENHUMA";
}

/**
* @param no
*/
private static void imprimirTabuleiro(No no) {
if (!printOn) return;
for (int i = 0; i < dimensao; i++) {
imprimirTabuleiroLinha();
for (int j = 0; j < dimensao; j++) {
int n = i * dimensao + j;
System.out.print("+ " + no.estado[n] + " ");
}
System.out.println("+");
}
imprimirTabuleiroLinha();
}

/**
* imprime uma linha separadora do tabuleiro
*/
private static void imprimirTabuleiroLinha() {
if (!printOn) return;
for (int i = 0; i < dimensao; i++) {
System.out.print("+---");
}
System.out.println("+");
}

/**
* imprime 's'
*
* @param s
*/
private static void print(String s) {
if (printOn) System.out.println(s);

}

/**
* esta eh a chave do problema, determinar uma fucao heuristica que retorne
*
* o quanto esta perto da solucao neste problema a heuristica eh calculada
*
* verificando quantos blocos estao na posicao correta - isto eh, de acordo
*
* com a matriz 'solucao'
*
* @param estado
* @return
*/
private static double heuristica(int[] estado) {
double valor = 0.0;
for (int i = 0; i < dimensao; i++) {
for (int j = 0; j < dimensao; j++) {
int n = i * dimensao + j;
valor += estado[n] == solucao[n] ? 1 : 0;
}
}
return valor;
}

private static boolean goal(int[] estado) {
for (int i = 0; i < dimensao * dimensao; i++) {
if (estado[i] != solucao[i]) return false;
}
return true;
}

private static List recuperarSucessores(int[] estado) {
List filhos = new ArrayList();
if (podeMoverCalhau(estado, CIMA)) {
int[] novoEstado = clonar(estado);
moverCima(novoEstado);
filhos.add(new Sucessor(novoEstado, CIMA));
}
if (podeMoverCalhau(estado, ESQUERDA)) {
int[] novoEstado = clonar(estado);
moverEsquerda(novoEstado);
filhos.add(new Sucessor(novoEstado, ESQUERDA));
}
if (podeMoverCalhau(estado, DIREITA)) {
int[] novoEstado = clonar(estado);
moverDireita(novoEstado);
filhos.add(new Sucessor(novoEstado, DIREITA));
}
if (podeMoverCalhau(estado, BAIXO)) {
int[] novoEstado = clonar(estado);
moverBaixo(novoEstado);
filhos.add(new Sucessor(novoEstado, BAIXO));
}
return filhos;
}

private static void moverCima(int[] estado) {
int pos = 0;
for (int i = dimensao; i < dimensao * dimensao; i++) {
if (estado[i] == 0) {
pos = i;
break;
}
}
if (pos > 0) {
int valor = estado[pos - dimensao];
estado[pos - dimensao] = 0;
estado[pos] = valor;
}
}

private static void moverBaixo(int[] estado) {
int pos = 0;
for (int i = 0; i < dimensao * dimensao; i++) {
if (estado[i] == 0) {
pos = i;
break;
}
}
int valor = estado[pos + dimensao];
estado[pos + dimensao] = 0;
estado[pos] = valor;
}

private static void moverEsquerda(int[] estado) {
int pos = 0;
for (int i = 0; i < dimensao * dimensao; i++) {
if (estado[i] == 0) {
pos = i;
break;
}
}
int valor = estado[pos - 1];
estado[pos - 1] = 0;
estado[pos] = valor;
}

private static void moverDireita(int[] estado) {
int pos = 0;
for (int i = 0; i < dimensao * dimensao; i++) {
if (estado[i] == 0) {
pos = i;
break;
}
}
int valor = estado[pos + 1];
estado[pos + 1] = 0;
estado[pos] = valor;
}

/**
* expandir e adicionar nós subsequentes na fila
*
* @param no
*/
private static void adicionarNosAlternativosFila(No no) {
if (!(processados.contains(no.toString()))) {
processados.add(no.toString());
List expandidos = expandirNos(no);
for (No o : expandidos) {
fila.add(o);
}
}
}

/**
* retorna um estado clonado
*
* @param estado
* @return
*/
private static int[] clonar(int[] estado) {
int[] ret = new int[estado.length];
for (int i = 0; i < estado.length; i++) {
ret[i] = estado[i];
}
return ret;
}

/**
* expandir o no com os seus sucessores possiveis
*
* @param no
* @return
*/
private static List expandirNos(No no) {
List nos = new ArrayList();
List proximos = recuperarSucessores(no.estado);
for (Sucessor prox : proximos) {
No no0 = new No();
no0.pai = no;
no0.estado = prox.estado;
no0.step = no.step + 1;
no0.acao = prox.acao;
// o custo é sempre 1 pois movemos 1 bloco a cada passo
no0.custoStep = 1.0;
no0.custoCaminho += no0.pai.custoCaminho + 1.0;
nos.add(no0);
// if (!processados.contains(no0.toString())) nos.add(no0);
}
nos_expandidos++;
return nos;
}

/**
* verifica se o calhau pode ser movido para uma direcao(acao)
*
* @param estado
* @param acao
* @return
*/
private static boolean podeMoverCalhau(int[] estado, int acao) {
int posicao = 0;
for (int i = 0; i < dimensao * dimensao; i++) {
if (estado[i] == 0) {
posicao = i;
break;
}
}
if (acao == ESQUERDA) {
while (posicao >= 0) {
if (posicao == 0) return false;
posicao -= dimensao;
}
} else if (acao == CIMA) {
if (posicao >= 0 && posicao < dimensao) return false;
} else if (acao == DIREITA) {
posicao++;
while (posicao >= dimensao) {
if (posicao == dimensao) return false;
posicao -= dimensao;
}
} else if (acao == BAIXO) {
if (posicao >= dimensao * (dimensao - 1)) return false;
}
return true;
}

/**
* representa um estado sucessor do estado atual dada uma acao
*
* @author Ricardo A Harari - ricardo.harari@gmail.com
*
*/
static class Sucessor {
public Sucessor(int _estado[], int _acao) {
estado = _estado;
acao = _acao;
}

public int[] estado;
public int acao;
}

/**
* representa um nó de um grafo possui o estado, o nó pai,
*
* a acao, o custo para chegar até este nó, a profundidade do nó
*
* e o custo unitário para ir do pai até o filho
*
* @author Ricardo A Harari - ricardo.harari@gmail.com
*
*/
static class No {
public int[] estado;
public int acao;
public No pai;
public int step = 0;
public double custoCaminho = 0.0;
public double custoStep = 0.0;

public List recuperarArvore() {
No atual = this;
List ret = new ArrayList();
while (!(atual.pai != null)) {
ret.add(0, atual);
atual = atual.pai;
}
ret.add(0, atual);
return ret;
}

@Override
public String toString() {
String ret = "";
for (int i = 0; i < dimensao * dimensao; i++) {
ret += estado[i];
}
return ret;
}

@Override
public boolean equals(Object o) {
if ((o == null) || (this.getClass() != o.getClass())) return false;
if (this == o) return true;
No x = (No) o;
for (int i = 0; i < dimensao; i++) {
if (estado[i] != x.estado[i]) {
return false;
}
}
return true;
}
}

/**
* comparador de Nós chamando a heuristica
*
* @author Ricardo A Harari - ricardo.harari@gmail.com
*
*/
static class ComparadorNo implements Comparator {
/*
* (non-Javadoc)
*
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@Override
public int compare(No o1, No o2) {
double d1 = heuristica(o1.estado);
double d2 = heuristica(o2.estado);
return (int) (d2 - d1);
}
}

}

segunda-feira, 7 de julho de 2008

Algoritmo Genetico - Considerações

Existe um problema conceitual na implementação do problema anterior das 8 rainhas que é o seguinte:

- no algoritmo genético, a probabilidade de sobrevivencia dos individuos é proporcional ao seu fitness. Eu nao impementlei como probabilidade mas sim como certo, sempre pegando os mais adaptados.

O método selecionarAleatorio poderia ser otimizado tambem, para considerar os mais adaptados em serem selecionados com maior probabilidade.

Isto pode gerar a uma situação onde a resolução demore muito, mais que o previsto ou até mesmo que não encontre a solução, após milhares de iterações.

Coloco aqui então uma outra abordagem que me ocorreu. A idéia central é aumentar a probabilidade de mutação caso o fitness do melhor elemento não aumente apos 1mil iteracoes (poderia considerar o fitness global tambem). Depois de 2mil iteracoes a probabilidade de mutação aumenta ainda mais.

Outra idéia é tambem fazer com que somente 2 dos melhores pais sobrevivam e 2 dos piores filhos sejam eliminados. Assim garantimos que outros pais tambem possam disseminar seus genes, mesmo não sendo tão competitivos no momento.

Ainda, ocorrendo mais de 5mil iterações e não atingindo a solução ótima, toda população é eliminada e uma nova é gerada.

Pensando na linha darwinista podemos traduzir que se uma população não se adaptar rapidamente ao ambiente então poderá ser dizimada e uma nova população tomará o seu lugar. Os parametros de quando isso deve ocorrer dependerá do tipo de problema.
Uma metáfora: se não encontrarmos uma maneira de viver em outros planetas poderemos ser solapados por um meteoro ou por mudanças climáticas.
A opção por manter alguns da espécie nesta matança pode ser uma opção tambem mas não foi implementada.

Outra mudança no algoritmo abaixo é que o filho irá sofrer mutação somente se o seu fitness for menor ou igual que o maior fitness.

Após 2mil testes, com estas considerações, a média de iterações foi de 3096,98 o que é considerado satisfatório e não ocorreu nenhuma falha.

Abaixo o algoritmo modificado:



import java.util.HashSet;
import java.util.Random;
import java.util.Set;

/**
* @author Ricardo Alberto Harari - ricardo.harari@gmail.com
*
*
*/
public class OitoRainhas {

private static final int MAX_FITNESS = 8;
private static Random randomico = new Random();

// parametros de stress test
private static boolean ocorreuFalha = false;
private static int totalMaxIteracoes = 0;
private static int totalIteracoes = 0;
private static boolean disablePrint = false;

public static void main(String[] args) {
// disablePrint = true;
// for (int i = 0; i <>
go();
// System.out.println("i=" + i);
// }
// System.out.println("------- MAX ITERACOES=" + totalMaxIteracoes);
// System.out.println("-------MEDIA ITERACOES="
// + (totalIteracoes / 2000.0));
// System.out.println("------- OCORREU FALHA?" + ocorreuFalha);
}

/**
* inicio do processo
*/
private static void go() {
long startTime = System.currentTimeMillis();
println("Populacao Inicial");

// gerar a populacao inicial com 10 individuos
Set populacao = new HashSet();
carregarPopulacao(populacao);
println("------------------------------");

double probabilidadeMutacao = 0.15;
int maxIteracoes = 300000;

String melhorIndividuo = null;
boolean achouSolucao = false;
int bestFitness = 0;

int i = 0;
int counter = 0;

for (i = 0; i <>
melhorIndividuo = geneticAlgorithm(populacao, probabilidadeMutacao,
bestFitness);
int ftness = fitness(melhorIndividuo);
if (ftness > bestFitness) {
probabilidadeMutacao = 0.10;
counter = 0;
println("novo fitness = " + ftness);
bestFitness = ftness;
if (ftness == MAX_FITNESS) {
achouSolucao = true;
break;
}
} else {
counter++;
if (counter > 1000) {
probabilidadeMutacao = 0.30;
} else if (counter > 2000) {
probabilidadeMutacao = 0.50;
} else if (counter > 5000) {
populacao.clear();
carregarPopulacao(populacao);
probabilidadeMutacao = 0.10;
bestFitness = -1;
}
}
}

println("------------------------------");

if (achouSolucao) {
println("Solucao encontrada em " + i + " iteracoes");
println("Solucao =" + melhorIndividuo);
println("Fitness =" + fitness(melhorIndividuo));
} else {
System.out.println("Solucao nao encontrada após " + i
+ " iteracoes");
System.out.println("Melhor Individuo =" + melhorIndividuo);
System.out.println("Fitness =" + fitness(melhorIndividuo));
ocorreuFalha = true;
}

totalIteracoes += i;
if (i > totalMaxIteracoes)
maxIteracoes = i;

println("------------------------------");

mostrarTabuleiro(melhorIndividuo);
println("Elapsed time = " + (System.currentTimeMillis() - startTime)
+ "ms");
}

/**
* @param string
*/
private static void println(String string) {
if (!disablePrint)
System.out.println(string);
}

/**
* @param populacao
*/
private static void carregarPopulacao(Set populacao) {
while (populacao.size() <>
String individuo = gerarIndividuo(8);
println("individuo=" + individuo);
populacao.add(individuo);
}
}

/**
* mostrar o tabuleiro graficamente
*
* @param melhorIndividuo
* @return
*/
private static void mostrarTabuleiro(String melhorIndividuo) {
println("|---+---+---+---+---+---+---+---|");
for (int i = 0; i <>
print("|");
int posicaoRainha = Integer.parseInt(melhorIndividuo.substring(i,
i + 1)) - 1;
for (int j = 0; j <>
if (posicaoRainha == j) {
print(" x |");
} else {
print(" |");
}
}
println("\r\n|---+---+---+---+---+---+---+---|");
}
}

/**
* @param string
*/
private static void print(String string) {
if (!disablePrint)
System.out.print(string);

}

/**
* logica GA mantendo os melhores na populacao retorna o melhor individuo
*
* @param populacao
* @param probabilidadeMutacao
*/
private static String geneticAlgorithm(Set populacao,
double probabilidadeMutacao, int fitnessAtual) {
String melhor = null;
Set filhos = new HashSet();
int tamanhoPopulacao = populacao.size();

while (filhos.size() <>
String p1 = selecionarAleatorio(populacao, "");
String p2 = selecionarAleatorio(populacao, p1);
String filho = crossover(p1, p2);
if (randomico.nextDouble() <= probabilidadeMutacao) {
int ffitness = fitness(filho);
if (ffitness <= fitnessAtual)
filho = mutate(filho);
}
filhos.add(filho);
}

// adicionar dois dos melhores pais
Object[] pais = populacao.toArray();
int[] f = new int[pais.length];
int melhorF = -1;
for (int i = 0; i <>
f[i] = fitness((String) pais[i]);
if (melhorF <>
melhorF = f[i];
}
}
populacao.clear();
while (populacao.size() <>
for (int i = 0; i <>
if (f[i] == melhorF) {
populacao.add((String) pais[i]);
}
if (populacao.size() == 2)
break;
}
melhorF--;
}

filhos.addAll(populacao);
Object[] pop = filhos.toArray();
f = new int[pop.length];
melhorF = -1;
for (int i = 0; i <>
f[i] = fitness((String) pop[i]);
if (melhorF <>
melhorF = f[i];
melhor = (String) pop[i];
}
}
populacao.clear();
while (populacao.size() <>
if (melhorF <>
// should never happen...
System.out.println("???????");
break;
}
for (int i = 0; i <>
if (f[i] == melhorF && populacao.size() <>
populacao.add((String) pop[i]);
}
}
melhorF--;
}
return melhor;
}

/**
* @param filho
* @return
*/
private static String mutate(String filho) {
int mp = randomico.nextInt(filho.length());
int mc = randomico.nextInt(filho.length()) + 1;
filho = filho.substring(0, mp) + mc
+ (mp + 1 == filho.length() ? "" : filho.substring(mp + 1));
return filho;
}

/**
* crossover
*
* @param p1
* @param p2
* @return
*/
private static String crossover(String p1, String p2) {
int i = randomico.nextInt(p1.length());
String ret = p1.substring(0, i) + p2.substring(i);
return ret;
}

/**
* seleciona um individuo da populacao aleatoriamente
*
* @param populacao
* @return
*/
private static String selecionarAleatorio(Set populacao, String px) {
String pn = px;
Object[] tmp = populacao.toArray();
while (pn.equals(px)) {
int i = randomico.nextInt((populacao.size()));
pn = (String) tmp[i];
}
return pn;
}

/**
* gerar um individuo com n posicoes
*
* @param n
* @return
*/
private static String gerarIndividuo(int n) {
String ret = "";
while (ret.length() <>
ret += (randomico.nextInt(n) + 1);
return ret;
}

/**
* função fitness, retorna a quantidade de rainhas a salvo.
*
* @param individuo
* @return
*/
public static int fitness(String individuo) {
int ret = 0;

int[][] tabuleiro = new int[8][8];
// primeiro representamos o tabuleiro com 0 e 1
for (int i = 0; i <>
int posicaoRainha = Integer.parseInt(individuo.substring(i, i + 1)) - 1;
for (int j = 0; j <>
tabuleiro[i][j] = posicaoRainha == j ? 1 : 0;
}
}

// agora verificamos quantas rainhas estao a salvo, este será o nosso
// retorno da função fitness
for (int i = 0; i <>
for (int j = 0; j <>
if (tabuleiro[i][j] == 1) {
if (!temAtacante(tabuleiro, i, j)) {
ret++;
}
}
}
}
return ret;
}

/**
* verifica se existe uma rainha ameaçando a posicao i,j existindo retorna
* true caso contrário retorna false
*
* @param tabuleiro
* @param i
* @param j
* @return
*/
private static boolean temAtacante(int[][] tabuleiro, int i, int j) {
// verificar na horizontal
for (int k = 0; k <>
if (k != i && tabuleiro[k][j] == 1)
return true;
}
// verificar na vertical
for (int k = 0; k <>
if (k != j && tabuleiro[i][k] == 1)
return true;
}
// verificar na diagonal1
int i0 = i - 1;
int j0 = j - 1;
while (i0 >= 0 && j0 >= 0) {
if (tabuleiro[i0][j0] == 1)
return true;
i0--;
j0--;
}
// verificar na diagonal2
i0 = i + 1;
j0 = j + 1;
while (i0 <>
if (tabuleiro[i0][j0] == 1)
return true;
i0++;
j0++;
}
// verificar na diagonal3
i0 = i + 1;
j0 = j - 1;
while (i0 <>= 0) {
if (tabuleiro[i0][j0] == 1)
return true;
i0++;
j0--;
}
// verificar na diagonal4
i0 = i - 1;
j0 = j + 1;
while (i0 >= 0 && j0 <>
if (tabuleiro[i0][j0] == 1)
return true;
i0--;
j0++;
}
return false; // esta a salvo
}

}