TProcuraAdversa
Algoritmos de procura adversa
Loading...
Searching...
No Matches
TProcuraConstrutiva.cpp
Go to the documentation of this file.
2#include <stdio.h>
3#include <time.h>
4#include <string.h>
5#include <ctype.h>
6
7#define BUFFER_SIZE 1024
8
9
10// numero de geracaes de estados
12// numero de expansoes de estados
14// numero de chamadas a funcao heuristica
16// auxiliar para construcao da arvore de procura
18// espacamento entre ramos da arvore de debug
20// valor retornado pela procura (tem de ser libertado)
22// valor retornado pela procura (tem de ser libertado)
24// lowerBound: valor mínimo que a solução pode obter
26
27// deadline da corrida atual
29// flag de problemas de memória esgotada
31// ID da instância atual (problemas com várias instâncias, a utilizar em SolucaoVazia())
32TParametro TProcuraConstrutiva::instancia = { NULL,1,1,1, NULL, NULL };
33// adicionar parâmetros específicos, se necessário
35
36// conjuntos de valores de parâmetros, para teste
38
39// tamanho em inteiros de 64 bits de um objeto (inferior ou igual a OBJETO_HASHTABLE)
41
43int TProcuraConstrutiva::custoHT[TAMANHO_HASHTABLE]; // hashtable / custo do estado que foi gerado
44uint64_t TProcuraConstrutiva::estadoCodHT[OBJETO_HASHTABLE]; // elemento codificado
45int TProcuraConstrutiva::colocadosHT = 0; // número de elementos colocados na HT
46
47TProcuraConstrutiva::TProcuraConstrutiva(void) : pai(NULL), custo(1), heuristica(0) {
48}
49
51{
52 static const char* nomesAlgoritmos[] = {
53 "Largura Primeiro",
54 "Custo Uniforme",
55 "Profundidade Primeiro",
56 "Melhor Primeiro",
57 "A*",
58 "IDA*",
59 "Branch and Bound" };
60 static const char* nomesDebug[] = {
61 "nada",
62 "atividade",
63 "passos",
64 "detalhe",
65 "completo" };
66 static const char* nomesRepetidos[] = {
67 "ignorar",
68 "ascendentes",
69 "gerados" };
70 // definir parametros base
71 parametro.Count(0);
72 // algoritmos
73 parametro.Add({ "Algoritmo",1,1,7,"Algoritmo base a executar.", nomesAlgoritmos });
74 // nivel de debug
75 parametro.Add({ "Debug",0,0,4,"Nível de debug, de reduzido a completo.",nomesDebug });
76 // ver Acoes (ou apenas estados completos)
77 parametro.Add({ "Ver",4,1,100,"Mostra estado a cada K ações. Se 1 mostra sempre estados e nunca ações.",NULL });
78 // seed
79 parametro.Add({ "Seed",1,1,1000000, "Semente aleatória para inicializar a sequência de números pseudo-aleatórios.",NULL });
80 // limite tempo
81 parametro.Add({ "Tempo",10,1,3600, "Tempo limite em segundos. ",NULL });
82 // máximo de gerações
83 parametro.Add({ "Gerações",0,0,1000000000, "Número de gerações máximo (0 não há limite). ", NULL });
84 // máximo de expansões
85 parametro.Add({ "Expansões",0,0,1000000000, "Número de expansões máximo (0 não há limite). ",NULL });
86 // máximo de avaliacoes
87 parametro.Add({ "Avaliações",0,0,1000000000, "Número de avaliações máximo (0 não há limite). ",NULL });
88 // limite (algoritmo)
89 parametro.Add({ "Limite",0,-1,1000000,
90 "Valor dependente do algoritmo. \n\
91Largura: 0 sem limite, >0 número máximo de estados gerados não expandidos. \n\
92Profundidade: >0 limite de profundidade, =0 iterativo, <0 sem limite.",NULL });
93 // estados repetidos
94 parametro.Add({ "Repetidos", 1,1,3, "Forma de lidar com os estados repetidos (ignorá-los, ascendentes, gerados).",
95 nomesRepetidos });
96 // pesoAStar
97 parametro.Add({ "pesoAStar",100,0,10000,
98 "Peso aplicado à heuristica, na soma com o custo para calculo do lower bound. No A*, se peso 0, fica custo uniforme, h(n) não conta, se peso 100 fica A* normal, se superior a 100 aproxima-se do melhor primeiro.",NULL });
99 // ruido heuristica
100 parametro.Add({ "ruido",0,-100,100,
101 "Ruído a adicionar à heurística, para testes de robustez. Se K positivo, adicionar entre 0 e K-1, se negativo, o valor a adicionar pode ser positivo ou negativo.",NULL });
102 // baralhar sucessores
103 parametro.Add({ "baralhar",0,0,1,
104 "Baralhar os sucessores ao expandir.",NULL });
105
106 // colocar as configurações vazias (podem ser inicializadas se existirem configurações de omissão)
108}
109
110// Executa uma ação (movimento, passo, jogada, lance, etc.) no estado atual. Caso não seja feito nada, retornar falso.
111bool TProcuraConstrutiva::Acao(const char* acao) {
112 TVector<TNo> sucessores;
113 Sucessores(sucessores);
114 for (int i = 0; i < sucessores.Count(); i++)
115 if (strcmp(acao, Acao(sucessores[i])) == 0) {
116 Copiar(sucessores[i]);
118 return true;
119 }
120 return false;
121}
122
123// Coloca em sucessores a lista de objectos sucessores (sao alocados neste metodo e tem de ser apagados)
124// O custo se não existir, deixar a 1 (valor de omissão)
125// chamar o metodo desta classe apos adicionar os sucessores para actualizar geracoes e expansoes,
126// bem como verificar a existência de estados repetidos
128 if (memoriaEsgotada)
129 return;
130 // colocar como pai o estado atual e atualizar custos
131 for (int i = 0; i < sucessores.Count(); i++) {
132 sucessores[i]->pai = this;
133 sucessores[i]->custo += custo;
134 // o custo total tem de estar atualizado antes de se utilizar nas hashtables
135 }
136 // verificação de estados repetidos
137 if (parametro[estadosRepetidos].valor == gerados) {
138 for (int i = 0; i < sucessores.Count(); i++)
139 if (sucessores[i]->ExisteHT()) {
140 // estado já gerado anteriormente, está na hashtable, pelo que não interessa
141 delete sucessores[i];
142 sucessores[i] = NULL;
143 }
144 }
145 else if (parametro[estadosRepetidos].valor == ascendentes) {
146 // verificar se o estado já foi gerado na árvore de procura
147 // remover todos os estados que ja tenham sido expandidos neste ramo
148 for (int i = 0; i < sucessores.Count(); i++) {
149 TNo avo = this;
150 while (avo != NULL && avo->Distinto(sucessores[i]))
151 avo = avo->pai;
152 // estado é igual a um avô, não interessa
153 if (avo != NULL) {
154 delete sucessores[i];
155 sucessores[i] = NULL;
156 }
157 }
158 }
159 sucessores.Remove(NULL);
160 if (parametro[baralharSuc].valor == 1)
161 sucessores.RandomOrder();
162 expansoes++;
163 geracoes += sucessores.Count();
164}
165
166
167// metodo interno para libertar objectos nao necessarios
168void TProcuraConstrutiva::LibertarVector(TVector<TNo>&vector, int excepto, int maiorQue)
169{
170 for (int i = 0; i < vector.Count(); i++)
171 if (i != excepto && i > maiorQue && vector[i] != NULL)
172 delete vector[i];
173 vector.Count(0);
174}
175
177// Procura em Largura Primeiro: expande primeiro o estado gerado não expandido mais antigo
178// retorna o valor da solução e coloca o caminho no vector (se calcularCaminho=true), ou -1 caso não encontre solucao
179// limite é o número de estados gerados não expandidos, que não pode ultrapassar esse limite
180// os que ultrapassarem são deitados fora (se 0 este limite não importa, podendo haver problemas de memória)
182{
183 TVector<TNo> lista; // lista de nós gerados na árvore de procura (expandidos ou não)
184 lista.Add(this); // nó inical: estado, custo
185 for (int i = 0; i < lista.Count() && !Parar(); i++) {
186 // processar o próximo estado não expandido (estão expandidos todos os anteriores a i)
187 lista[i]->DebugPasso();
188 // expansão deste estado
189 TVector<TNo> sucessores;
190 lista[i]->Sucessores(sucessores);
191 // garantir que os limites são respeitados, para evitar problemas de memória
192 VerificaLimites(limite, lista.Count() - i, sucessores);
193 // inserir tudo no final da lista
194 for (int j = 0; j < sucessores.Count(); j++) {
195 lista.Add(sucessores[j]);
196 // verificar se é um estado objetivo, a solução é completa nesse caso
197 // teste na geração e não na expansão
198 if (lista.Last()->SolucaoCompleta()) {
199 LibertarVector(sucessores, -1, j);
200 return ObjetivoAlcancado(lista.Count() - 1, lista); // Sucesso! Terminar a procura e retornar
201 }
202 }
203
204 if(parametro[nivelDebug].valor > detalhe)
205 lista[i]->DebugSucessores(sucessores);
206
207 // Nao se pode libertar estados ja expandidos porque nao se sabe se
208 // os pais sao necessarios ou nao.
209 // todos os estados antes de i, são os estados gerados e expandidos (fechados)
210 // todos os estados após i são estados gerados mas não expandidos (abertos)
211 }
212 // nada feito, libertar tudo excepto o estado inicial que é este objeto, e retornar
213 LibertarVector(lista, 0);
214 return -1; // falhou
215}
216
218{
219 estado->CalculaCaminho(completa); // extrair o caminho do estado objetivo até ao estado inicial
220 solucao = estado->Duplicar(); // registar este estado como a solução
221 solucao->custo = estado->custo;
223 return solucao->custo;
224}
225
227{
228 ObjetivoAlcancado(lista[item]);
229 LibertarVector(lista, 0);
230 return solucao->custo;
231}
232
234 TNo atual = this;
235 // limpar o caminho anterior (caso do BnB que obtém mais que uma solução)
236 while (caminho.Count() > 0)
237 delete caminho.Pop();
238 caminho.Add(atual->Duplicar());
239 caminho.Last()->custo = atual->custo;
240
241 // apenas se a lista estiver completa, há a garantia dos pais não terem sido apagados
242 if (completa) {
243 // obter caminho para a solucao
244 while (atual->pai != NULL) {
245 atual = atual->pai;
246 caminho.Add(atual->Duplicar());
247 caminho.Last()->custo = atual->custo;
248 }
249 }
250 caminho.Invert();
251}
252
254 int limite, int porProcessar, TVector<TNo>& sucessores)
255{
256 // caso o limite seja ultrapassado,
257 // remover elementos a mais da lista de sucessores
258 if (limite != 0 && limite < porProcessar + sucessores.Count()) {
259 int maximo = limite - porProcessar;
260 if (maximo >= 0 && maximo < sucessores.Count()) {
261 for (int i = maximo; i < sucessores.Count(); i++)
262 delete sucessores[i];
263 sucessores.Count(maximo);
264 }
265 else if (maximo < 0)
266 LibertarVector(sucessores);
267 }
268}
269
270
272// Procura Custo Uniforme: expande primeiro o estado gerado não expandido de menor custo acumulado
274{
275 CListaNo lista(limite);
276
277 lista.Inserir(this); // começa apenas com o elemento atual
278
279 for (lista.atual = 0; !Parar() && lista.Estado() != NULL; lista.atual = lista.Proximo()) {
280 lista.Estado()->DebugPasso();
281 if(lista.Estado()->SolucaoCompleta())
282 return ObjetivoAlcancado(lista.Estado(), lista.Completa());
283
284 TVector<TNo> sucessores;
285 lista.Estado()->Sucessores(sucessores);
286 // insere por ordem de custo, e não apenas no final
287 lista.Inserir(sucessores);
288 if (parametro[nivelDebug].valor > detalhe)
289 lista.Estado()->DebugSucessores(sucessores);
290 }
291 return -1; // falhou
292}
293
295// Procura em Profundidade Primeiro: expande primeiro o estado gerado não expandido mais novo
296// caso o nivel=-1, e feita uma procura em profunidade normal
297// caso o nivel>0, e feita uma procura em profundidade limitada
298// caso o nivel=0, e feita uma procura em profundidade iterativa, sem limite
299// versão recursiva
301{
302 DebugChamada();
303 if (nivel == 0) { // metodo iterativo
304 int resultado = -1;
305 do {
306 // limpar hashtable: estados gerados no nível anterior não devem impedir nova geração
307 LimparHT();
308 DebugIteracao(nivel + 1);
309 // chamar a profundidade nível 1, e se não resolver, o nível 2, e assim sucessivamente
310 resultado = ProfundidadePrimeiro(++nivel);
311 } while (resultado == -1 && !Parar());
312 return resultado;
313 }
314
315 // metodo normal
316 // verificar se o estado atual é objetivo, ou seja, a solução parcial é já completa
317 if (SolucaoCompleta())
318 return SolucaoEncontrada();
319
320 if((nivel>1 || nivel<0) && !Parar()) {
321 // caso o nível seja superior a 1 ou sem limite, expandir o estado atual
322 TVector<TNo> sucessores;
323 Sucessores(sucessores);
324 // tentar todos os sucessores, um de cada vez
325 for (int i = 0; i < sucessores.Count(); i++) {
326 DebugExpansao(i, sucessores.Count());
327 // chamada recursiva, reduzindo o nível
328 if (sucessores[i]->ProfundidadePrimeiro(nivel - 1) >= 0)
329 // este sucessor resolveu o problema, retornar
330 return SolucaoParcial(i, sucessores);
331 }
332 // nenhum dos sucessores resolveu o problema
333 DebugCorte(sucessores.Count());
334 LibertarVector(sucessores);
335 }
336 else
337 DebugCorte();
338
339 // falha na procura neste nó, ou não há estado objetivo a partir daqui
340 // ou atingiu-se o limite, pelo que temos de retornar -1
341 return -1;
342}
343
345 // sucesso!
346 DebugSolucao(continuar);
347 if (solucao == NULL)
348 solucao = Duplicar();
349 else // caso existam várias soluções, substitui a anterior
350 solucao->Copiar(this);
352 return solucao->custo = custo;
353}
354
356{
357 // solução parcial já registada, adicionar este nó e retornar o custo até ao momento
358 LibertarVector(sucessores, i);
359 return solucao->custo;
360}
361
363 for (int i = 0; i < caminho.Count(); i++) {
364 if (parametro[verAcoes].valor > 1) {
365 // mostrar o estado a cada K ações, no início e no fim
366 if (i % parametro[verAcoes].valor == 0 || i == caminho.Count() - 1) {
367 caminho[i]->Debug();
368 // mostrar custo
369 printf(" (g:%d) ", caminho[i]->custo);
370 }
371 // mostrar a ação
372 if(i < caminho.Count() - 1)
373 printf(" %s", caminho[i]->Acao(caminho[i + 1]));
374 }
375 else {
376 caminho[i]->Debug();
377 printf(" (g:%d) ", caminho[i]->custo);
378 }
379 }
380 if (caminho.Count() == 0)
381 printf("Caminho vazio.");
382}
383
384
386// Algoritmo Melhor Primeiro: expande primeiro o estado gerado não expandido com melhor heurística
387// versão recursiva, idêntico a ProcuraPrimeiro()
389{
390 DebugChamada();
391 if (SolucaoCompleta())
392 return SolucaoEncontrada();
393
394 if ((nivel <= 0 || nivel > 1) && !Parar()) {
395 TVector<TNo> sucessores;
396 TVector<int> id; // índice para ordenar os sucessores por heurística
397 Sucessores(sucessores);
398 CalcularHeuristicas(sucessores, &id); // id fica ordenado por heurística
399 // processar todos os sucessores da mesma forma que a procura em profundidade,
400 // mas utilizando a ordem id, por heurística
401 for (int i = 0; i < id.Count(); i++) {
402 DebugExpansao(i, id.Count());
403 if (sucessores[id[i]]->MelhorPrimeiro(nivel - 1) >= 0)
404 return SolucaoParcial(id[i], sucessores);
405 }
406 DebugCorte(sucessores.Count());
407 LibertarVector(sucessores);
408 }
409 else
410 DebugCorte();
411 return -1;
412}
413
415// Algoritmo IDAStar: procura em profundidade limitada a um upperBound
416// idêntico a MelhorPrimeiro() mas cortando quando upperBound já não consegue ser obtido
418{
419 DebugChamada();
420 if (upperBound == 0) { // parte iterativa
421 int resultado = -1;
422 // primeiro valor para o lower bound, a heurística no nó raiz
423 lowerBound = Heuristica(); // o custo é zero atualmente
424 if (lowerBound == 0)
425 lowerBound++;
426 do {
427 // limpar hashtable: estados gerados no nível anterior não devem impedir nova geração
428 LimparHT();
430 // ver se há uma solução com este valor
431 resultado = IDAStar(lowerBound);
432 // o valor de lowerBound é atualizado, para utilizar na próxima iteração se necessário
433 } while (resultado == -1 && !Parar());
434 return resultado;
435 }
436
437 if (SolucaoCompleta())
438 return SolucaoEncontrada();
439
440 if (!Parar()) {
441 TVector<TNo> sucessores;
442 TVector<int> id; // índice para ordenar os sucessores por heurística
443 Sucessores(sucessores);
444 CalcularHeuristicas(sucessores, &id, true); // id fica ordenado por LB
445 // processar todos os sucessores da mesma forma que a procura em profundidade,
446 // mas utilizando a ordem id, por heurística
447 for (int i = 0; i < id.Count(); i++) {
448 int atual = sucessores[id[i]]->LowerBound();
449 DebugExpansao(i, id.Count());
450 if (atual > upperBound) {
451 // acima do permitido nesta iteração
452 if (lowerBound == upperBound || lowerBound > atual)
453 lowerBound = atual;
454 DebugCorte(); // estado cortado, não expandido
455 }
456 else {
457 if (sucessores[id[i]]->IDAStar(upperBound) >= 0)
458 return SolucaoParcial(id[i], sucessores);
459 }
460 }
461 DebugCorte(sucessores.Count());
462 LibertarVector(sucessores);
463 }
464 return -1;
465}
466
468// Algoritmo BranchAndBound:
469// idêntico a MelhorPrimeiro(), mas continua após a primeira solução
470// explora apenas os estados em que f(n) < atual solução (LB<UB)
472{
473 DebugChamada();
474 if (SolucaoCompleta())
475 return SolucaoEncontrada(true);
476
477 if (!Parar()) {
478 TVector<TNo> sucessores;
479 TVector<int> id; // índice para ordenar os sucessores por heurística
480 Sucessores(sucessores);
481 CalcularHeuristicas(sucessores, &id, true); // id fica ordenado por LB
482 // processar todos os sucessores, mas apenas se LB<UB
483 for (int i = 0; i < id.Count(); i++) {
484 DebugExpansao(i, id.Count());
485 if (upperBound && sucessores[id[i]]->LowerBound() >= upperBound) {
486 DebugCorte(); // estado cortado, e os seguintes não serão expandidos
487 break;
488 }
489 int resultado = sucessores[id[i]]->BranchAndBound(upperBound);
490 // mesmo que exista um resultado, continuar à procura de melhor
491 if (resultado > 0 && (!upperBound || resultado < upperBound))
492 upperBound = resultado;
493 }
494 DebugCorte(sucessores.Count());
495 LibertarVector(sucessores);
496 }
497 return (solucao != NULL ? solucao->custo : -1);
498}
499
501 TVector<TNo>& sucessores, TVector<int> *id, bool sortLB)
502{
503 TVector<int> heuristicas;
504 // obter os valores heurísticos, e ordentar o índice
505 for (int i = 0; i < sucessores.Count(); i++) {
506 sucessores[i]->heuristica = sucessores[i]->Heuristica();
507 if (id != NULL)
508 heuristicas.Add(sucessores[i]->heuristica + (sortLB ? sucessores[i]->custo : 0));
509 }
510 if (id != NULL)
511 heuristicas.Sort(id); // ordenar id
512}
513
514
516// Algoritmo AStar: expande primeiro o estado gerado não expandido com melhor custo+heurística
517// Idêntico a CustoUniforme()
519{
520 CListaNo lista(limite);
521 lista.Inserir(this); // estado único
522
523 for (lista.atual = 0; !Parar() && lista.Estado()!=NULL; lista.atual = lista.Proximo()) {
524 lista.Estado()->DebugPasso();
525 if (lista.Estado()->SolucaoCompleta())
526 return ObjetivoAlcancado(lista.Estado(), lista.Completa());
527
528 TVector<TNo> sucessores;
529 lista.Estado()->Sucessores(sucessores);
530 // atualiza as heurísticas, sendo esta a única diferença para CustoUniforme
531 CalcularHeuristicas(sucessores);
532 // insere por ordem de custo, e não apenas no final
533 lista.Inserir(sucessores);
534 if (parametro[nivelDebug].valor > detalhe)
535 lista.Estado()->DebugSucessores(sucessores);
536 }
537
538 return -1; // falhou
539}
540
541// Redefinir para poder utilizar os algoritmos informados
542// Esta função deve devolver o custo estimado por baixo,
543// desde este estado até ao estado final mais proximo (é um valor minimo),
544// colocando esse valor na variável heuristica
545// chamar este método para actualiacao de avaliacoes
547 avaliacoes++;
548 if (parametro[ruidoHeur].valor > 0)
550 if (parametro[ruidoHeur].valor < 0)
551 heuristica += TRand::rand() % (-2 * parametro[ruidoHeur].valor) - parametro[ruidoHeur].valor;
552 return heuristica;
553}
554
555
556// Escrever informacao de debug sobre o objecto atual
557// (utilizar variavel TProcuraConstrutiva::debug para seleccionar o detalhe pretendido)
559{
560 if(parametro[nivelDebug].valor > nada)
561 printf("\nTProcuraConstrutiva::Debug() não definido.");
562}
563
564// Metodo para ser chamado antes de analisar cada sucessor
565void TProcuraConstrutiva::DebugExpansao(int sucessor, int sucessores, bool duplo)
566{
567 if(parametro[nivelDebug].valor >= passos) {
568 if(sucessor > 0)
569 NovaLinha(false);
570
571 if (sucessor == 0 && sucessores == 1) { // só um ramo
572 DebugRamo(parametro[nivelDebug].valor < completo ? '-' : ' ', duplo ? '#' : '+');
573 ramo.Add(' '); // a ser impresso nesta posição nas linhas seguintes
574 }
575 else if (sucessor == 0) { // início e continua
576 DebugRamo(parametro[nivelDebug].valor < completo ? '-' : ' ', duplo ? '#' : '+');
577 ramo.Add(duplo ? '/' : '|'); // a ser impresso nesta posição nas linhas seguintes
578 }
579 else if (sucessor > 0 && sucessor < sucessores - 1) { // no meio e continua
580 DebugRamo(' ', duplo ? '#' : '+');
581 ramo.Last() = (duplo ? '/' : '|');
582 }
583 else {
584 DebugRamo(' ', duplo ? '#' : '+'); // no fim, vai acabar
585 ramo.Last() = ' '; // a ser impresso nesta posição nas linhas seguintes
586 }
587 }
588}
589
590void TProcuraConstrutiva::DebugRamo(char ramo, char folha) {
591 for (int i = 0; i < espacosRamo; i++)
592 printf("%c", ramo);
593 printf("%c", folha);
594}
595
596
597// Metodo para ser chamado quando nao ha sucessores ou ha um corte de profundidade
598void TProcuraConstrutiva::DebugCorte(int sucessores,bool duplo)
599{
600 if(parametro[nivelDebug].valor >= passos) {
601 if(sucessores<0) {
602 if (parametro[nivelDebug].valor < completo) {
603 printf("%c ", '='); // corte de profundidade
604 DebugEstado();
605 if (parametro[nivelDebug].valor >= detalhe)
606 Debug();
607 }
608 } else if(sucessores>0)
609 ramo.Pop();
610 else if(parametro[nivelDebug].valor < completo) { // ramo em que nao e possivel continuar
611 printf("%c ", '&');
612 DebugEstado();
613 if(parametro[nivelDebug].valor >= detalhe)
614 Debug();
615 }
616 }
617}
618
619// Encontrou uma solucao
621{
622 if(parametro[nivelDebug].valor > nada && SolucaoCompleta()) {
623 printf(" Solução encontrada!");
624 if(!continuar)
625 ramo.Count(0);
626 Debug();
627 printf("(g:%d)", custo);
628 } else {
629 if(parametro[nivelDebug].valor > atividade)
630 Debug();
631 if(parametro[nivelDebug].valor >= passos && !continuar)
632 ramo.Pop();
633 }
634}
635
636// Informacao de debug na chamada ao metodo recursivo
638{
639 if (parametro[nivelDebug].valor == atividade && expansoes % 1000 == 0)
640 printf("#");
641 if (parametro[nivelDebug].valor > detalhe) {
642 // neste nível, cada estado expandido é visualizado, não apenas os estados folha
643 DebugEstado();
644 if(pai!=NULL)
645 printf(" %s", pai->Acao(this)); // mostra sempre a ação
646 if (parametro[verAcoes].valor == 1 || pai==NULL)
647 Debug();
648 NovaLinha();
649 }
650}
651
652// Chamar sempre que se quer uma nova linha com a arvore em baixo
654{
655 printf("\n");
656 for (int i = 0; i < ramo.Count() - (tudo ? 0 : 1); i++)
657 printf("%*s%c", espacosRamo, " ", ramo[i]);
658}
659
660// Passo no algoritmo em largura
662{
663 if (parametro[nivelDebug].valor == atividade && expansoes % 1000 == 0)
664 printf("#");
665 if (parametro[nivelDebug].valor >= passos) {
666 printf("\n");
667 DebugEstado();
668 }
669 if (parametro[nivelDebug].valor > passos)
670 Debug();
671}
672// Mostrar sucessores
674 if (parametro[verAcoes].valor > 2) {
675 // mostrar apenas ações
676 printf("\nAções: ");
677 for (int i = 0; i < sucessores.Count() && i < 30; i++) {
678 printf("%s ", Acao(sucessores[i]));
679 if (i % 10 == 9)
680 printf("\n ");
681 }
682 }
683 else {
684 ramo.Count(0);
685 ramo.Add(' ');
686 for (int i = 0; i < sucessores.Count() && i < 30; i++) {
687 ramo.First() = '+';
688 NovaLinha();
689 sucessores[i]->DebugEstado(i + 1);
690 if (i < sucessores.Count() - 1)
691 ramo.First() = '|';
692 else
693 ramo.First() = ' ';
694 printf(" %s", Acao(sucessores[i])); // mostra sempre a ação
695 if (parametro[verAcoes].valor == 1)
696 sucessores[i]->Debug();
697 }
698 ramo.Count(0);
699
700 }
701}
702
703
704// uma nova iteração de um algoritmo iterativo
706 if (parametro[nivelDebug].valor == atividade)
707 printf("\n");
708 if (parametro[nivelDebug].valor > atividade) {
709 printf("\nIteração %d:", iteracao);
710 if (parametro[nivelDebug].valor > passos)
711 printf(" (expansões %d, gerações %d, avaliações %d)\n", expansoes, geracoes, avaliacoes);
712 else
713 printf("\n");
714 }
715}
716
717// informação geral sobre o estado
718void TProcuraConstrutiva::DebugEstado(int id, int pai) {
719 if (id >= 0) {
720 printf("#%d ", id);
721 if(pai>=0)
722 printf("(#%d) ", pai);
723 }
724 printf("g:%d ", custo);
725 if (heuristica)
726 printf("h:%d ", heuristica);
727
728 if (expansoes)
729 printf("%d", expansoes);
730 if(geracoes)
731 printf("|%d", geracoes);
732 if(avaliacoes)
733 printf("|%d", avaliacoes);
734}
735
736
737
738// Chamar antes de iniciar uma procura
740{
742 ramo.Count(0);
743 while (caminho.Count() > 0)
744 delete caminho.Pop();
745 if (solucao != NULL)
746 delete solucao;
747 solucao = NULL;
748 LimparHT();
749 inicio = clock();
750 instanteFinal = inicio + parametro[limiteTempo].valor * CLOCKS_PER_SEC;
751 memoriaEsgotada = false;
752}
753
754// Metodo para teste manual do objecto (chamadas aos algoritmos, construcao de uma solucao manual)
755// Este metodo destina-se a testes preliminares, e deve ser redefinido apenas se forem definidos novos algoritmos
757{
758 int selecao, resultado;
759 clock_t inicio;
762 SolucaoVazia();
763 LimparEstatisticas(inicio);
764 while(true) {
765 printf("\n%s", nome);
767 printf("\n[Estatísticas] expansões %d | gerações %d | avaliações %d ",
769 Debug();
770 printf("\n\
771_______________________________________________________________________________\n\
772| 1 - Inicializar | 2 - Explorar | 3 - Solução/Caminho |\n\
773| 4 - Parâmetros | 5 - Executar | 6 - Configurações | 7 - Teste");
774 if ((selecao = NovoValor("\nOpção: ")) == NAO_LIDO)
775 return;
776 switch (Dominio(selecao, 0, 8)) {
777 case 0: return;
778 case 1: TRand::srand(parametro[seed].valor); SolicitaInstancia(); SolucaoVazia(); break;
779 case 2: ExplorarSucessores(); break;
780 case 3: MostrarSolucao(); break;
781 case 4: EditarParametros(); break;
782 case 5:
783 // executar um algoritmo
784 LimparEstatisticas(inicio);
785 resultado = ExecutaAlgoritmo();
787 printf("\nResultado: %d", resultado);
788 FinalizarCorrida(inicio);
789 break;
790 case 6: EditarConfiguracoes(); break;
791 case 7:
793 NovoValor("Instância inicial: "),
794 NovoValor("Instancia final: "),
795 NovoValor("Mostrar soluções? "));
796 break;
797 case 8: return;
798 default: printf("\nOpção não definida."); break;
799 }
800 }
801}
802
804 int nElementos = (idParametros == NULL ? parametro.Count() : idParametros->Count());
805 printf("\n");
806 for (int i = 0; i < nElementos; i++) {
807 int parID = (idParametros == NULL ? i : (*idParametros)[i]);
808 // identificação do parâmetro
809 if (detalhe == 0 || parametro[parID].nome == NULL)
810 printf("P%d:", parID + 1);
811 else
812 printf("P%d(%s): ", parID + 1, parametro[parID].nome);
813 // valor do parâmetro
814 if(detalhe==0|| parametro[parID].nomeValores == NULL)
815 printf("%d", parametro[parID].valor);
816 else
817 printf("%s", parametro[parID].nomeValores[parametro[parID].valor - parametro[parID].min]);
818 // mostrar intervalo permitido
819 if(detalhe>1)
820 printf(" (%d a %d)", parametro[parID].min, parametro[parID].max);
821 // separador/mudança de linha
822 if (i < nElementos - 1) {
823 if (detalhe > 1 || (i + 1) % (detalhe == 0 ? 10 : 4) == 0)
824 printf("\n");
825 else if (detalhe > 0)
826 printf(" | ");
827 else
828 printf(" ");
829 }
830 }
831}
832
834 int opcao = 0, valor;
835 while(1) {
837 if ((opcao = NovoValor("\nParametro:")) == NAO_LIDO || opcao == 0)
838 return;
839 opcao = Dominio(opcao, 1, parametro.Count());
840 // mostrar descrição se existir
841 if (parametro[opcao - 1].descricao != NULL)
842 printf("\n%s", parametro[opcao - 1].descricao);
843 // mostrar textos dos valores possíveis, caso existam
844 if (parametro[opcao - 1].nomeValores != NULL)
845 for (int i = parametro[opcao - 1].min; i <= parametro[opcao - 1].max; i++)
846 printf("\n%d: %s", i,
847 parametro[opcao - 1].nomeValores[i - parametro[opcao - 1].min]);
848
849 // valor atual
850 if (parametro[opcao - 1].nome != NULL)
851 printf("\n%s (atual %d): ", parametro[opcao - 1].nome, parametro[opcao - 1].valor);
852 else
853 printf("\nP%d (atual %d): ", opcao, parametro[opcao - 1].valor);
854 // solicitar valor
855 valor = NovoValor("");
856 if (valor != NAO_LIDO || valor == 0)
857 parametro[opcao - 1].valor = Dominio(
858 valor,
859 parametro[opcao - 1].min,
860 parametro[opcao - 1].max);
861 }
862}
863
864// gravar (ou ler) a configuração atual
866 if (operacao == gravar) {
867 for (int i = 0; i < parametro.Count() && i < parametros.Count(); i++)
868 parametro[i].valor = parametros[i];
869 }
870 else if (operacao == ler) {
871 parametros.Count(0);
872 for (int i = 0; i < parametro.Count(); i++)
873 parametros.Add(parametro[i].valor);
874 }
875}
876
877
879 TVector<int> atual; // parâmetros atuais
880 int id = -1;
881
882 ConfiguracaoAtual(atual, ler);
883
884 // procurar a configuração atual
885 for (int i = 0; i < configuracoes.Count() && id < 0; i++)
886 if (configuracoes[i].Equal(atual))
887 id = i;
888 // caso a configuração atual não exista, adicionar
889 if (id < 0) {
891 configuracoes.Last() = atual;
892 id = configuracoes.Count() - 1;
893 }
894
896
897 if ((id = NovoValor("\nSelecionar configuração (negativo apaga): ")) != NAO_LIDO) {
898 id = Dominio(id, -configuracoes.Count() - 1, configuracoes.Count() + 1);
899 if (id < 0) {
900 configuracoes.Delete(-id - 1);
901 }
902 else if (id > 0) {
903 atual = configuracoes[id - 1];
904 }
905 }
907}
908
910 TVector<int> comum, distinto;
911 // identificar parametros comuns e distintos entre as parametrizações
912 for (int i = 0; i < configuracoes.First().Count(); i++) {
913 bool igual = true;
914 for (int j = 1; j < configuracoes.Count() && igual; j++)
915 igual = (configuracoes.First()[i] == configuracoes[j][i]);
916 if (igual)
917 comum.Add(i);
918 else
919 distinto.Add(i);
920 }
921 // mostra parametros comuns, evitando repetição em cada configuração
922 printf("\nParametros comuns a %d configurações:", configuracoes.Count());
923 MostraParametros(detalhe, &comum);
924
925 // visualizar configurações atuais, assinalando a atualmente escolhida
926 for (int i = 0; i < configuracoes.Count(); i++) {
927 printf("\n--- Configuração %d", i + 1);
928 if (i == atual)
929 printf(" --- atual");
931 MostraParametros(detalhe, &distinto);
932 }
933}
934
935
936
938 int resultado = -1;
939 switch (parametro[algoritmo].valor) {
940 case 1: resultado = LarguraPrimeiro(Dominio(parametro[limite].valor, 0)); break;
941 case 2: resultado = CustoUniforme(Dominio(parametro[limite].valor, 0)); break;
942 case 3: resultado = ProfundidadePrimeiro(parametro[limite].valor); break;
943 case 4: resultado = MelhorPrimeiro(parametro[limite].valor); break;
944 case 5: resultado = AStar(Dominio(parametro[limite].valor, 0)); break;
945 case 6: resultado = IDAStar(Dominio(parametro[limite].valor, 0)); break;
946 case 7: resultado = BranchAndBound(Dominio(parametro[limite].valor, 0)); break;
947 }
948 return resultado;
949}
950
951
952// utilizar para executar testes empíricos, utilizando todas as instãncias,
953// com o último algoritmo executado e configurações existentes
954void TProcuraConstrutiva::TesteEmpirico(int inicio, int fim, bool mostrarSolucoes) {
955 TVector<TResultado> resultados; // guarda as soluções obtidas
956 TVector<int> atual;
957 int backupID = instancia.valor;
958 if (inicio == NAO_LIDO || inicio == 0)
959 inicio = instancia.min;
960 if (fim == NAO_LIDO || fim == 0)
961 fim = instancia.max;
964 ConfiguracaoAtual(atual, ler);
965 if (configuracoes.Count() == 0) {
966 // não foram feitas configurações, utilizar a atual
968 configuracoes.Last() = atual;
969 }
970 // percorrer todas as instâncias
971 for (int configuracao = 0; configuracao < configuracoes.Count(); configuracao++) {
972 ConfiguracaoAtual(configuracoes[configuracao], gravar);
974 for (instancia.valor = inicio; instancia.valor <= fim; instancia.valor++) {
975 int resultado = -1;
976 clock_t inicioCorrida;
978 // carregar instância
979 SolucaoVazia();
980 // executar um algoritmo
981 printf("\nInstância %d: ", instancia.valor);
982 inicioCorrida = clock();
983 LimparEstatisticas(inicioCorrida);
984 resultado = ExecutaAlgoritmo();
985 resultados.Add({
987 (solucao != NULL ? solucao->custo : resultado),
988 expansoes,geracoes,avaliacoes,configuracao,
989 clock() - inicioCorrida });
990 if (solucao != NULL && solucao->SolucaoCompleta()) {
991 if (mostrarSolucoes)
993 }
994 else {
995 if (Parar())
996 printf("Não resolvido. ");
997 if (TempoExcedido())
998 printf("Tempo excedido. ");
999 if (memoriaEsgotada)
1000 printf("Memória esgotada. ");
1001 if (resultado < 0 && !Parar())
1002 printf("Instância Impossível! (se algoritmo completo) ");
1003 else // não resolvido, cancelar resultados
1004 resultados.Last().custo = -2;
1005 }
1006 if (solucao != NULL)
1007 delete solucao;
1008 solucao = NULL;
1009 printf("DONE.");
1010 }
1011 }
1012 MostraRelatorio(resultados);
1013 ConfiguracaoAtual(atual, gravar);
1014 instancia.valor = backupID;
1015 TRand::srand(parametro[seed].valor);
1016 SolucaoVazia();
1017}
1018
1020{
1021 TVector<TResultado> total; // totais por cada configuração
1022 total.Count(configuracoes.Count());
1023 for (int i = 0; i < total.Count(); i++)
1024 total[i] = { 0,0,0,0,0,0,0 };
1025
1026 // mostrar os resultados
1027 printf("\n ID |conf| custo(g) | expansões | gerações | avaliações | tempo(s) |");
1028 printf("\n----|----|----------|------------|-----------|------------|----------|");
1029 for (int i = 0; i < resultados.Count(); i++) {
1030 if (resultados[i].custo >= -1)
1031 total[resultados[i].configuracao].instancia++;
1032 printf("\n%3d |%3d |", resultados[i].instancia, resultados[i].configuracao + 1);
1033 if (resultados[i].custo < 0)
1034 printf(" %8s |", resultados[i].custo < -1 ? "não res." : "sem sol.");
1035 else
1036 printf(" %8d |", resultados[i].custo);
1037 printf(" %10d | %9d | %10d | %7.3fs |",
1038 resultados[i].expansoes,
1039 resultados[i].geracoes,
1040 resultados[i].avaliacoes,
1041 1. * resultados[i].tempo / CLOCKS_PER_SEC);
1042 if (resultados[i].custo > 0)
1043 total[resultados[i].configuracao].custo += resultados[i].custo;
1044 total[resultados[i].configuracao].expansoes += resultados[i].expansoes;
1045 total[resultados[i].configuracao].geracoes += resultados[i].geracoes;
1046 total[resultados[i].configuracao].avaliacoes += resultados[i].avaliacoes;
1047 total[resultados[i].configuracao].tempo += resultados[i].tempo;
1048 }
1049 printf("\n----|----|----------|------------|-----------|------------|----------| resolvidas");
1050 // tabela com os totais por configuração
1051 for (int i = 0; i < total.Count(); i++) {
1052 printf("\nTotal%3d |", i+1);
1053 printf(" %8d | %10d | %9d | %10d | %7.3fs | %3d",
1054 total[i].custo,
1055 total[i].expansoes,
1056 total[i].geracoes,
1057 total[i].avaliacoes,
1058 1. * total[i].tempo / CLOCKS_PER_SEC,
1059 total[i].instancia);
1060 }
1061 // mostrar torneio entre configurações
1062 CalculaTorneio(resultados);
1064 printf("\n");
1065}
1066
1068 TVector<TVector<int>> torneio; // pares de configurações: 1 melhor, 0 igual -1 pior
1069 torneio.Count(configuracoes.Count());
1070 for (int i = 0; i < configuracoes.Count(); i++) {
1071 torneio[i].Count(configuracoes.Count());
1072 torneio[i].Reset(0);
1073 }
1074 // registar resultados mediante o melhor resultado
1075 for (int i = 0; i < configuracoes.Count(); i++)
1076 for (int j = 0; j < configuracoes.Count(); j++)
1077 if (i != j) {
1078 TVector<TResultado> configuracaoI, configuracaoJ;
1079 ExtrairConfiguracao(resultados, configuracaoI, i);
1080 ExtrairConfiguracao(resultados, configuracaoJ, j);
1081 for (int k = 0; k < configuracaoI.Count() && k < configuracaoJ.Count(); k++)
1082 if (configuracaoI[k].instancia == configuracaoJ[k].instancia)
1083 torneio[i][j] += MelhorResultado(configuracaoI[k], configuracaoJ[k]);
1084 }
1085 MostrarTorneio(torneio);
1086}
1087
1088
1090{
1091 TVector<int> pontos;
1092 pontos.Count(torneio.Count());
1093 pontos.Reset(0);
1094 // registar resultados mediante o melhor resultado
1095 for (int i = 0; i < torneio.Count(); i++)
1096 for (int j = 0; j < torneio.Count(); j++)
1097 if (i != j) {
1098 pontos[i] += torneio[i][j];
1099 if (jogo) // contar pontos perdidos de pretas
1100 pontos[j] -= torneio[i][j];
1101 }
1102
1103 // mostrar tabela do torneio
1104 printf("\nTorneio (#instâncias melhores):");
1105 BarraTorneio(true);
1106 for (int i = 0; i < pontos.Count(); i++) {
1107 printf("\n%2d", i + 1);
1108 for (int j = 0; j < pontos.Count(); j++)
1109 if (i == j)
1110 printf(" |");
1111 else
1112 printf("%3d |", torneio[i][j]);
1113 // no final colocar os pontos totais
1114 printf("%3d", pontos[i]);
1115 BarraTorneio(false);
1116 }
1117}
1118
1120 extracao.Count(0);
1121 for (int i = instancia.min; i <= instancia.max; i++)
1122 for (int j = 0; j < resultados.Count(); j++)
1123 if (resultados[j].instancia == i && resultados[j].configuracao == configuracao)
1124 extracao.Add(resultados[j]);
1125}
1126
1127
1129 // barra inical/final: |----|----|----|
1130 printf("\n |");
1131 for (int i = 0; i < configuracoes.Count(); i++)
1132 if(nomes)
1133 printf("-%02d-|", i + 1);
1134 else
1135 printf("----|");
1136}
1137
1138
1140 // se não resolvido por ambos, retornar igualdade
1141 if (base.custo == -2 && alternativa.custo == -2)
1142 return 0;
1143 // se igual no custo e o tempo menor que 0.1, retornar igualdade
1144 if (base.custo == alternativa.custo && 10 * abs(base.tempo - alternativa.tempo) / CLOCKS_PER_SEC == 0)
1145 return 0;
1146 // primeiro custo (ou não resolvido, -2)
1147 if ((base.custo == -2 && alternativa.custo > -2) || (alternativa.custo > 0 && base.custo > alternativa.custo))
1148 return -1;
1149 if ((base.custo > -2 && alternativa.custo == -2) || (base.custo > 0 && alternativa.custo > base.custo))
1150 return 1;
1151 // agora o tempo
1152 return base.tempo < alternativa.tempo ? 1 : -1;
1153}
1154
1156{
1157 if (solucao != NULL) {
1158 Copiar(solucao);
1159 delete solucao;
1160 solucao = NULL;
1161 }
1162 printf(" (%.3fs)", 1.0 * (clock() - inicio) / CLOCKS_PER_SEC);
1163 if (TempoExcedido())
1164 printf(" Tempo excedido.");
1165 else if (memoriaEsgotada)
1166 printf(" Memória esgotada.");
1167}
1168
1169int TProcuraConstrutiva::NovoValor(const char* prompt) {
1170 char str[BUFFER_SIZE];
1171 printf("%s", prompt);
1172 fgets(str, BUFFER_SIZE, stdin);
1173 if (strlen(str) > 1)
1174 return atoi(str);
1175 return NAO_LIDO;
1176}
1177
1179 if (instancia.max != instancia.min) {
1180 int resultado;
1181 printf("\nNova instância (atual %d) [%d-%d]: ",
1183 instancia.min,
1184 instancia.max);
1185 if ((resultado = NovoValor("")) != NAO_LIDO && resultado != 0) {
1186 instancia.valor = resultado;
1188 }
1189 }
1190 else
1192}
1193
1195 TVector<TNo> sucessores;
1196 clock_t inicio;
1197 int opcao = 0;
1198 LimparEstatisticas(inicio);
1199 do {
1200 caminho.Add(Duplicar());
1201 if (caminho.Count() > 1)
1202 caminho.Last()->custo += caminho[caminho.Count() - 2]->custo;
1203 else
1204 caminho.Last()->custo = 0;
1206 Sucessores(sucessores);
1207 CalcularHeuristicas(sucessores);
1208 // linha com informação
1209 DebugEstado();
1210 Debug();
1211 DebugSucessores(sucessores);
1212 if (sucessores.Count() == 0) {
1213 printf("\nSem sucessores.");
1214 opcao = 0;
1215 }
1216 else {
1217 char str[BUFFER_SIZE];
1218 printf("\nSucessor [1-%d, ação(ões), exe]:", sucessores.Count());
1219 fgets(str, BUFFER_SIZE, stdin);
1220 opcao = atoi(str);
1221 if (opcao == 0 && strlen(str)>1) {
1222 char* token;
1223 opcao = sucessores.Count() + 1;
1224 token = strtok(str, " \t\n\r");
1225 // executar algoritmo
1226 if (strcmp(token, "exe") == 0) {
1227 TVector<TNo> backup;
1228 backup = caminho;
1229 caminho.Count(0);
1230 LimparEstatisticas(inicio);
1231 if (jogo) {
1232 int valor = ExecutaAlgoritmo();
1233 printf("Valor: %d\n", valor);
1234 if (solucao != NULL)
1235 Copiar(solucao);
1236 caminho = backup;
1237 }
1238 else {
1239 int resultado;
1240 switch (resultado=ExecutaAlgoritmo()) {
1241 case -1: printf("Impossível\n"); break;
1242 case -2: printf("Não resolvido\n"); break;
1243 default: printf("Resolvido (%d)\n", resultado); break;
1244 }
1245 if (solucao != NULL) {
1246 Copiar(solucao);
1247 delete backup.Pop();
1248 backup += caminho;
1249 caminho = backup;
1250 caminho.Pop(); // este último estado será adicionado
1251 }
1252 else
1253 caminho = backup;
1254 }
1255 }
1256 else {
1257 int nAcoes = 0;
1258 do {
1259 // executar a ação
1260 if (Acao(token))
1261 nAcoes++;
1262 else {
1263 printf("Ação %s inválida.\n", token);
1264 break;
1265 }
1266 token = strtok(NULL, " \t\n\r");
1267
1268 if (token != NULL) {
1269 // há outra ação, é preciso atualizar o caminho
1270 caminho.Add(Duplicar());
1271 if (caminho.Count() > 1)
1272 caminho.Last()->custo += caminho[caminho.Count() - 2]->custo;
1273 else
1274 caminho.Last()->custo = 0;
1275 }
1276
1277 } while (token != NULL);
1278 if (nAcoes > 0)
1279 printf("Executadas %d ações com sucesso.\n", nAcoes);
1280 }
1281 }
1282 }
1283 if (opcao > 0 && opcao <= sucessores.Count())
1284 Copiar(sucessores[opcao - 1]);
1285 LibertarVector(sucessores);
1286 } while (opcao != 0);
1287}
1288
1290 // utilizando FNV-1 hash (http://www.isthe.com/chongo/tech/comp/fnv/)
1291 uint32_t h = 2166136261;
1293 for (int i = 0; i < OBJETO_HASHTABLE && estadoCodHT[i]; i++) {
1294 uint64_t valor = estadoCodHT[i];
1295 // processar cada octeto
1296 // quando o valor fica 0, já não interessa
1297 // já que existindo dados, são distintos de 0
1298 for (int i = 0; i < 8 && valor; i++) {
1299 unsigned char octeto = valor & 255;
1300 valor >>= 8;
1301 h *= 16777619;
1302 h ^= octeto;
1303 }
1304 }
1305 return h % TAMANHO_HASHTABLE;
1306}
1307
1309 if (parametro[estadosRepetidos].valor == gerados) {
1310 if (parametro[nivelDebug].valor >= 1) {
1311 int usado = 0; // contar para calcular taxa de ocupação
1312 for (int i = 0; i < TAMANHO_HASHTABLE; i++) {
1313 bool limpo = true;
1314 for (int j = 0; j < tamanhoCodificado; j++)
1315 if (elementosHT[i][j] != 0) {
1316 limpo = false;
1317 elementosHT[i][j] = 0;
1318 }
1319 if (!limpo)
1320 usado++;
1321 }
1322 // reportar estatísticas se existir muito reuso
1323 if (usado > 0 && usado * 2 <= colocadosHT) {
1324 printf("\nHT: utilização %d%%, reuso: %.2f vezes",
1325 usado * 100 / TAMANHO_HASHTABLE,
1326 1.0 * colocadosHT / usado);
1327 }
1328 } else
1329 for (int i = 0; i < TAMANHO_HASHTABLE; i++)
1330 for (int j = 0; j < tamanhoCodificado; j++)
1331 elementosHT[i][j] = 0;
1332 colocadosHT = 0;
1333 // coloca o estado atual na hasttable, para não ser gerado
1334 ExisteHT();
1335 }
1336}
1337
1339 // caso não exista, retorna falso e insere
1340 // se existe retorna verdade, e estiver lá outro elemento, substitui
1341 unsigned int original = Hash();
1342 unsigned int indice = original % TAMANHO_HASHTABLE;
1343 for (int i = 0; i < tamanhoCodificado; i++)
1344 if (elementosHT[indice][i] != estadoCodHT[i]) {
1345 SubstituirHT(indice);
1346 colocadosHT++;
1347 return false; // não existia
1348 }
1349 // elemento é igual, mas se o custo for mais alto, não conta
1350 // já que este elemento com custo mais baixo, pode conduzir à solução ou melhores soluções
1351 // neste caso substitui e retorna que o estado não existe
1352 if (custoHT[indice] > custo) {
1353 SubstituirHT(indice);
1354 return false; // existe mas com um custo mais alto
1355 }
1356 return true; // igual, estado já analisado
1357}
1358
1360{
1361 // substituir elemento
1362 for (int i = 0; i < tamanhoCodificado; i++)
1363 elementosHT[indice][i] = estadoCodHT[i];
1364 custoHT[indice] = custo;
1365}
1366
1367// estados repetidos: verificar todos os gerados
1368// implementar para utilizar hashtables com perdas
1369// converte o estado atual para a variável estado, utilizando o menor espaço possível
1370// caso existam simetrias, normalizar o estado antes de codificar, para considerar exploradas todas as versões
1372 for (int i = 0; i < tamanhoCodificado; i++)
1373 estado[i] = 0;
1374}
1375
1376int TProcuraConstrutiva::Dominio(int& variavel, int min, int max) {
1377 if (variavel < min)
1378 variavel = min;
1379 if (variavel > max)
1380 variavel = max;
1381 return variavel;
1382}
1383
1385 // não libertar o primeiro elemento
1386 for (int i = 1; i < indice.Count(); i++)
1387 if (indice[i].estado != NULL)
1388 delete indice[i].estado;
1389}
1390
1391// retorna o índice onde foi inserido
1392int CListaNo::NovoElemento(TNo elemento) {
1393 int id = -1;
1394 if (limite == 0 || indice.Count() < 2 * limite) {
1395 // adicionar, há espaço
1396 indice.Add({ elemento, 1, 1 });
1397 id = indice.Count() - 1;
1398 }
1399 else {
1400 // limite de estados atingido, gerar o espaço
1401 if (livre.Count() == 0)
1402 // não há livres, libertar metade
1403 LibertarLista();
1404
1405 // utilizar o primeiro livre
1406 indice[id = livre.Pop()] = { elemento, 1, 1 };
1407 }
1408 return id;
1409}
1410
1411void CListaNo::LibertarLista() {
1412 bool jaProcessados = false; // libertar primeiro estados já processados (anteriores a atual)
1413 // parar assim que existam "limite" estados livres
1414 completa = false;
1415 while (livre.Count() < limite) {
1416 if (!jaProcessados) {
1417 jaProcessados = true;
1418 // apagar elementos após o estado inicial
1419 while (livre.Count() < limite && Proximo(0) != atual)
1420 LibertarSeguinte(0);
1421 }
1422 else {
1423 int anteriorID = -1, piorID = -1;
1424 for (int i = atual; ProximoDistinto(i) != 1; i = ProximoDistinto(i)) {
1425 piorID = ProximoDistinto(i);
1426 anteriorID = i;
1427 }
1428 if (piorID < 0) {
1429 // valores todos iguais após atual, apagar o seguinte ao atual
1430 while (livre.Count() < limite)
1431 LibertarSeguinte(atual);
1432 }
1433 else {
1434 // começar por libertar todos após o primeiro elemento com o pior ID
1435 while (livre.Count() < limite && Proximo(piorID) != 1)
1436 LibertarSeguinte(piorID);
1437 if (livre.Count() < limite) {
1438 // tem de se eliminar o primeiro elemento com o pior valor
1439 // o que implica atualizar o proxDistinto de todos os anteriores
1440 while (Proximo(anteriorID) != piorID) {
1441 indice[anteriorID].proxDistinto = 1; // último elemento
1442 anteriorID = Proximo(anteriorID);
1443 }
1444 LibertarSeguinte(anteriorID);
1445 }
1446 // tudo liberto com este valor, se ainda não é suficiente, seguir para o próximo
1447 }
1448 }
1449 }
1450}
1451
1452void CListaNo::LibertarSeguinte(int id) {
1453 int i = Proximo(id);
1454 livre.Push(i);
1455 if (indice[i].estado != NULL) {
1456 delete indice[i].estado;
1457 indice[i].estado = NULL;
1458 }
1459 indice[id].prox = indice[i].prox;
1460 indice[id].proxDistinto = indice[i].proxDistinto;
1461}
1462
1463// insere por ordem de LB
1464int CListaNo::Inserir(TNo elemento, int id) {
1465 int valor, valorI;
1466 int idNovo = NovoElemento(elemento);
1467 if (idNovo == 0) {
1468 // primeiro elemento, inserir um elemento final
1469 indice.Add({ NULL,-1,-1 });
1470 return 0;
1471 }
1472 valor = Valor(idNovo);
1473 // atualizar índice
1474 for (int i = id, j = -1; i >= 0; i = ProximoDistinto(i)) {
1475 valorI = Valor(i);
1476 if (valorI == valor) {
1477 // inserir logo a seguir, já que assim nada mais altera
1478 indice[idNovo].prox = Proximo(i);
1479 indice[idNovo].proxDistinto = ProximoDistinto(i);
1480 indice[i].prox = idNovo;
1481 return i;
1482 }
1483 else if (valorI < valor)
1484 j = i; // mantem o primeiro elemento anterior
1485 else { // valor atual já é superior, valor novo
1486 if (j < 0) {
1487 // heurística não consistente,
1488 // e agora está a gerar um estado com menor valor que o pai
1489 // i, novo, i.prox, ...
1490 // o i fica com o próximo distinto em novo, o que não é verdade mas mais vale assim
1491 indice[idNovo].prox = Proximo(i);
1492 indice[idNovo].proxDistinto = Proximo(i);
1493 indice[i].prox = idNovo;
1494 indice[i].proxDistinto = idNovo;
1495 return idNovo;
1496 }
1497 else {
1498 // utilizar o índice de j, anterior, colocando antes de i
1499 // j, ... , novo, i
1500 // atualizar o proxDistinto de j, passa a novo
1501 indice[idNovo].prox = i;
1502 indice[idNovo].proxDistinto = i;
1503 while (j != idNovo) {
1504 indice[j].proxDistinto = idNovo;
1505 if (Proximo(j) == i || Proximo(j) == j)
1506 indice[j].prox = idNovo;
1507 j = Proximo(j);
1508 }
1509 return idNovo;
1510 }
1511 }
1512 }
1513 return 0;
1514}
1515
1516// inserir todos os elementos por ordem
1518 TVector<int> lbElementos, idElementos;
1519 int id = atual;
1520
1521 // 1. Ordenar elementos, complexidaded O(N log(N))
1522 for (int j = 0; j < elementos.Count(); j++)
1523 lbElementos.Add(elementos[j]->LowerBound());
1524 lbElementos.Sort(&idElementos);
1525
1526 // 2. inserir por ordem, tirando partido do local onde já está o último elemento
1527 for (int j = 0; j < idElementos.Count(); j++)
1528 id = Inserir(elementos[idElementos[j]], id);
1529
1530}
1531
1532
#define BUFFER_SIZE
@ limite
Valor dependente do algoritmo. Exemplo: Profundidade limitada.
@ ruidoHeur
Ruído a adicionar à heurística para testes de robustez.
@ estadosRepetidos
Forma de lidar com estados repetidos (ignorá-los, ascendentes, gerados).
@ verAcoes
Mostra estado a cada K ações. Se 1, mostra sempre estados e nunca ações.
@ seed
Semente aleatória para inicializar a sequência de números pseudo-aleatórios.
@ nivelDebug
Nível de debug, de reduzido a completo.
@ limiteTempo
Tempo limite em segundos.
@ baralharSuc
Baralhar os sucessores ao expandir.
@ algoritmo
Algoritmo base a executar.
#define NAO_LIDO
@ ascendentes
estados são comparados com ascendentes, e se forem repetidos são removidos
@ gerados
estados são comparados com todos os gerados, e se forem repetidos são removidos
#define TAMANHO_HASHTABLE
@ detalhe
Debug detalhada sobre estados e decisões.
@ atividade
Apenas eventos principais.
@ completo
Mostra toda a execução detalhadamente.
@ passos
Exibe passos intermediários.
@ nada
Sem informações de debug.
#define OBJETO_HASHTABLE
TNo Estado(int i=-1)
int Inserir(TNo elemento, int id=0)
int ProximoDistinto(int i=-1)
int Proximo(int i=-1)
Representa um estado no espaço de estados.
void DebugCorte(int sucessores=-1, bool duplo=false)
void DebugSucessores(TVector< TNo > &sucessores)
void MostrarTorneio(TVector< TVector< int > > &torneio, bool jogo=false)
void DebugRamo(char ramo, char folha)
int SolucaoEncontrada(bool continuar=false)
void DebugEstado(int id=-1, int pai=-1)
void DebugSolucao(bool continuar=false)
static int NovoValor(const char *prompt)
void ConfiguracaoAtual(TVector< int > &parametros, int operacao)
void CalculaTorneio(TVector< TResultado > &resultados)
static uint64_t elementosHT[TAMANHO_HASHTABLE][OBJETO_HASHTABLE]
void CalcularHeuristicas(TVector< TNo > &sucessores, TVector< int > *id=NULL, bool sortLB=false)
int SolucaoParcial(int i, TVector< TNo > &sucessores)
int Dominio(int &variavel, int min=INT_MIN, int max=INT_MAX)
void NovaLinha(bool tudo=true)
int ObjetivoAlcancado(int item, TVector< TNo > &lista)
int MelhorResultado(TResultado base, TResultado alternativa)
static void LibertarVector(TVector< TNo > &vector, int excepto=-1, int maiorQue=-1)
virtual void SubstituirHT(int indice)
void MostrarConfiguracoes(int detalhe, int atual=-1)
void CalculaCaminho(bool completa=true)
void ExplorarSucessores(bool jogo=false)
void MostraRelatorio(TVector< TResultado > &resultados)
void DebugExpansao(int sucessor, int sucessores, bool duplo=false)
void ExtrairConfiguracao(TVector< TResultado > &resultados, TVector< TResultado > &extracao, int configuracao)
void LimparEstatisticas(clock_t &inicio)
void DebugIteracao(int iteracao)
void VerificaLimites(int limite, int porProcessar, TVector< TNo > &sucessores)
void FinalizarCorrida(clock_t inicio)
static int custoHT[TAMANHO_HASHTABLE]
void MostraParametros(int detalhe=1, TVector< int > *idParametros=NULL)
static uint64_t estadoCodHT[OBJETO_HASHTABLE]
static unsigned int rand(int seq=0)
Definition TRand.cpp:61
static void srand(unsigned int seed, int seq=0)
Definition TRand.cpp:44
Item & First()
Definition TVector.h:68
void Delete(int i)
Definition TVector.h:326
void Add(Item a)
Definition TVector.h:54
Item & Pop()
Definition TVector.h:61
int Count()
Definition TVector.h:70
void RandomOrder()
Definition TVector.h:289
void Reset(Item const &i)
Definition TVector.h:309
void Remove(Item const &i)
Definition TVector.h:297
Item & Last()
Definition TVector.h:69
void Sort(TVector< int > *idxvect=NULL)
Definition TVector.h:172
void Push(Item a)
Definition TVector.h:59
int CustoUniforme(int limite=0)
Executa a procura por custo uniforme, algoritmo cego.
int ProfundidadePrimeiro(int nivel=0)
Executa a procura em profundidade primeiro, algoritmo cego.
int LarguraPrimeiro(int limite=0)
Executa a procura em largura primeiro, algoritmo cego.
int AStar(int limite=0)
Executa a procura A*, algoritmo informado.
int IDAStar(int upperBound=0)
Executa a procura IDA*, algoritmo informado.
int MelhorPrimeiro(int nivel=0)
Executa a procura melhor primeiro, algoritmo informado.
int BranchAndBound(int upperBound=0)
Executa o algoritmo Branch-and-Bound, um algoritmo informado.
virtual void Sucessores(TVector< TNo > &sucessores)
Coloca em sucessores a lista de estados sucessores.
virtual bool SolucaoCompleta(void)
Verifica se o estado actual é objectivo (é uma solução completa)
virtual void SolucaoVazia(void)
Coloca o objecto no estado inicial da procura.
virtual void TesteManual(const char *nome)
Inicializa a interação com o utilizador.
virtual void Copiar(TNo objecto)
Fica com uma cópia do objecto.
virtual TNo Duplicar(void)=0
Cria um objecto que é uma cópia deste.
virtual bool Distinto(TNo estado)
Verifica se o estado actual distinto do fornecido.
virtual void MostrarSolucao(void)
Mostrar solução, seja um caminho ou o próprio estado.
virtual void TesteEmpirico(int inicio=-1, int fim=-1, bool mostrarSolucoes=true)
Executa testes empíricos, em todas as configurações guardadas, nas instâncias selecionadas.
virtual int ExecutaAlgoritmo()
Executa o algoritmo com os parametros atuais.
virtual bool Parar(void)
Verifica se a procura deve ser interrompida.
virtual int Heuristica(void)
Função para calcular quanto falta para o final, o valor da heurística.
virtual void Codifica(uint64_t estado[OBJETO_HASHTABLE])
Codifica o estado para um vetor de inteiros de 64 bits.
virtual void Debug(void)
Mostra o estado no ecrã, para debug.
virtual void ResetParametros()
Inicializa os parametros.
virtual const char * Acao(TNo sucessor)
Retorna a ação (movimento, passo, jogada, lance, etc.) que gerou o sucessor.
int heuristica
Estimativa para o custo até um estado objetivo, se disponível.
int custo
Custo total acumulado desde o estado inicial.
TNo pai
Ponteiro para o estado pai, na árvore de procura.
static int expansoes
Número total de expansões realizadas na procura.
static int lowerBound
Valor mínimo que a solução pode apresentar, obtido pela procura.
static int avaliacoes
Número total de avaliações realizadas na procura.
static bool memoriaEsgotada
Flag indicando problemas de memória esgotada.
static int tamanhoCodificado
Número de inteiros de 64 bits utilizados para codificar um objeto (≤ OBJETO_HASHTABLE).
static int geracoes
Número total de gerações realizadas na procura.
static TVector< TNo > caminho
Solução retornada pela procura (os estados devem ser libertados).
static TVector< TVector< int > > configuracoes
Conjuntos de configurações para teste empírico.
static TVector< TParametro > parametro
Parâmetros a serem utilizados na configuração atual.
static TVector< unsigned char > ramo
static TNo solucao
Estado objetivo encontrado, retornado pela procura (deve ser libertado).
static TParametro instancia
ID da instância atual, a ser utilizado em SolucaoVazia().
Estrutura para registo de um parâmetro.
int valor
valor do parametro
int max
valor máximo que o parametro pode tomar
int min
valor mínimo que o parametro pode tomar