TProcura
Biblioteca em C++ para testes paramétricos de algoritmos, e coleção de algoritmos de procura e otimização
Loading...
Searching...
No Matches
TProcuraConstrutiva.cpp
Go to the documentation of this file.
2#include "CListaNo.h"
3#include <stdio.h>
4#include <time.h>
5#include <string.h>
6#include <ctype.h>
7
8constexpr int BUFFER_SIZE = 1024;
9
10// auxiliar para construcao da arvore de procura
12// espacamento entre ramos da arvore de debug
14// valor retornado pela procura (tem de ser libertado)
16// valor retornado pela procura (tem de ser libertado)
18// lowerBound: valor mínimo que a solução pode obter
24
25
26// tamanho em inteiros de 64 bits de um objeto (inferior ou igual a OBJETO_HASHTABLE)
28
30int TProcuraConstrutiva::custoHT[TAMANHO_HASHTABLE]; // hashtable / custo do estado que foi gerado
31uint64_t TProcuraConstrutiva::estadoCodHT[OBJETO_HASHTABLE]; // elemento codificado
32int TProcuraConstrutiva::colocadosHT = 0; // número de elementos colocados na HT
33
36
37
39{
40 static const char* nomesAlgoritmos[] = {
41 "Largura Primeiro",
42 "Custo Uniforme",
43 "Profundidade Primeiro",
44 "Melhor Primeiro",
45 "A*",
46 "IDA*",
47 "Branch and Bound" };
48 static const char* nomesRepetidos[] = {
49 "ignorar",
50 "ascendentes",
51 "gerados" };
53
54 // definir parametros base
55 // algoritmos, alterar
56 parametro[ALGORITMO] = { "ALGORITMO",1,1,7,"Algoritmo base a executar.", nomesAlgoritmos };
57 // parametros adicionados
58 parametro += {
59 { "VER_ACOES", 4, 1, 100, "Mostra estado a cada K ações. Se 1 mostra sempre estados e nunca ações." },
60 { "LIMITE",0,-1,1000000,
61"Valor dependente do algoritmo. \n\
62Largura: 0 sem limite, >0 número máximo de estados gerados não expandidos. \n\
63Profundidade: >0 limite de profundidade, =0 iterativo, <0 sem limite." },
64 { "ESTADOS_REPETIDOS", 1,1,3, "Forma de lidar com os estados repetidos (ignorá-los, ascendentes, gerados).", nomesRepetidos },
65 { "PESO_ASTAR", 100, 0, 10000,
66 "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.",
67 NULL, _TV("0,5:7")},
68 { "RUIDO_HEURISTICA",0,-100,100, "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.",
69 NULL, _TV("0,5:7") },
70 { "BARALHAR_SUCESSORES",0,0,1, "Baralhar os sucessores ao expandir." }
71 };
72
73 // indicadores, alterar
74 indicador[IND_CUSTO].nome = "IND_CUSTO";
75 indicador[IND_CUSTO].descricao = "o resultado é o custo da solução atual";
76 // indicadores adicionados
77 indicador += {
78 { "IND_EXPANSOES", "número de expansões efetuadas", IND_EXPANSOES },
79 { "IND_GERACOES","número de estados gerados", IND_GERACOES },
80 { "IND_LOWER_BOUND", "valor mínimo para a melhor solução, se igual ao custo da solução obtida, então esta é ótima", IND_LOWER_BOUND }
81 };
83}
84
85// Executa uma ação (movimento, passo, jogada, lance, etc.) no estado atual. Caso não seja feito nada, retornar falso.
86bool TProcuraConstrutiva::Acao(const char* acao) {
89 for (int i = 0; i < sucessores.Count(); i++)
90 if (strcmp(acao, Acao(sucessores[i])) == 0) {
93 return true;
94 }
95 return false;
96}
97
98// Coloca em sucessores a lista de objectos sucessores (sao alocados neste metodo e tem de ser apagados)
99// O custo se não existir, deixar a 1 (valor de omissão)
100// chamar o metodo desta classe apos adicionar os sucessores para actualizar geracoes e expansoes,
101// bem como verificar a existência de estados repetidos
103 if (memoriaEsgotada)
104 return;
105 // colocar como pai o estado atual e atualizar custos
106 for (int i = 0; i < sucessores.Count(); i++) {
107 sucessores[i]->pai = this;
108 sucessores[i]->custo += custo;
109 // o custo total tem de estar atualizado antes de se utilizar nas hashtables
110 }
111 // verificação de estados repetidos
113 for (int i = 0; i < sucessores.Count(); i++)
114 if (sucessores[i]->ExisteHT()) {
115 // estado já gerado anteriormente, está na hashtable, pelo que não interessa
116 delete sucessores[i];
117 sucessores[i] = NULL;
118 }
119 }
121 // verificar se o estado já foi gerado na árvore de procura
122 // remover todos os estados que ja tenham sido expandidos neste ramo
123 for (int i = 0; i < sucessores.Count(); i++) {
124 TNo avo = this;
125 while (avo != NULL && avo->Distinto(sucessores[i]))
126 avo = avo->pai;
127 // estado é igual a um avô, não interessa
128 if (avo != NULL) {
129 delete sucessores[i];
130 sucessores[i] = NULL;
131 }
132 }
133 }
134 sucessores -= NULL;
136 sucessores.RandomOrder();
137 expansoes++;
138 geracoes += sucessores.Count();
139}
140
141
142// metodo interno para libertar objectos nao necessarios
143void TProcuraConstrutiva::LibertarVector(TVector<TNo>& vector, int excepto, int maiorQue)
144{
145 for (int i = 0; i < vector.Count(); i++)
146 if (i != excepto && i > maiorQue && vector[i] != NULL)
147 delete vector[i];
148 vector = {};
149}
150
152// Procura em Largura Primeiro: expande primeiro o estado gerado não expandido mais antigo
153// retorna o valor da solução e coloca o caminho no vector (se calcularCaminho=true), ou -1 caso não encontre solucao
154// limite é o número de estados gerados não expandidos, que não pode ultrapassar esse limite
155// os que ultrapassarem são deitados fora (se 0 este limite não importa, podendo haver problemas de memória)
157{
158 TVector<TNo> lista; // lista de nós gerados na árvore de procura (expandidos ou não)
159 lista += this; // nó inical: estado, custo
160 for (int i = 0; i < lista.Count() && !Parar(); i++) {
161 // processar o próximo estado não expandido (estão expandidos todos os anteriores a i)
162 lista[i]->DebugPasso();
163 // expansão deste estado
165 lista[i]->Sucessores(sucessores);
166 // garantir que os limites são respeitados, para evitar problemas de memória
167 VerificaLimites(limite, lista.Count() - i, sucessores);
168 // inserir tudo no final da lista
169 for (int j = 0; j < sucessores.Count(); j++) {
170 lista += sucessores[j];
171 // verificar se é um estado objetivo, a solução é completa nesse caso
172 // teste na geração e não na expansão
173 if (lista.Last()->SolucaoCompleta()) {
175 return ObjetivoAlcancado(lista.Count() - 1, lista); // Sucesso! Terminar a procura e retornar
176 }
177 }
178
180 lista[i]->DebugSucessores(sucessores);
181
182 // Nao se pode libertar estados ja expandidos porque nao se sabe se
183 // os pais sao necessarios ou nao.
184 // todos os estados antes de i, são os estados gerados e expandidos (fechados)
185 // todos os estados após i são estados gerados mas não expandidos (abertos)
186 }
187 // nada feito, libertar tudo excepto o estado inicial que é este objeto, e retornar
189 return -1; // falhou
190}
191
193{
194 estado->CalculaCaminho(completa); // extrair o caminho do estado objetivo até ao estado inicial
195 solucao = estado->Duplicar(); // registar este estado como a solução
196 solucao->custo = estado->custo;
198 return solucao->custo;
199}
200
207
209 TNo atual = this;
210 // limpar o caminho anterior (caso do BnB que obtém mais que uma solução)
211 while (!caminho.Empty())
212 delete caminho.Pop();
213 caminho += atual->Duplicar();
214 caminho.Last()->custo = atual->custo;
215
216 // apenas se a lista estiver completa, há a garantia dos pais não terem sido apagados
217 if (completa) {
218 // obter caminho para a solucao
219 while (atual->pai != NULL) {
220 atual = atual->pai;
221 caminho += atual->Duplicar();
222 caminho.Last()->custo = atual->custo;
223 }
224 }
225 caminho.Invert();
226}
227
229 int limite, int porProcessar, TVector<TNo>& sucessores)
230{
231 // caso o limite seja ultrapassado,
232 // remover elementos a mais da lista de sucessores
233 if (limite != 0 && limite < porProcessar + sucessores.Count()) {
234 int maximo = limite - porProcessar;
235 if (maximo >= 0 && maximo < sucessores.Count()) {
236 for (int i = maximo; i < sucessores.Count(); i++)
237 delete sucessores[i];
238 sucessores.Count(maximo);
239 }
240 else if (maximo < 0)
242 }
243}
244
245
247// Procura Custo Uniforme: expande primeiro o estado gerado não expandido de menor custo acumulado
249{
250 CListaNo lista(limite);
251
252 lista.Inserir(this); // começa apenas com o elemento atual
253
254 for (lista.atual = 0; !Parar() && lista.Estado() != NULL; lista.atual = lista.Proximo()) {
255 lista.Estado()->DebugPasso();
256 if (lista.Estado()->SolucaoCompleta())
257 return ObjetivoAlcancado(lista.Estado(), lista.Completa());
258
260 lista.Estado()->Sucessores(sucessores);
261 // insere por ordem de custo, e não apenas no final
262 lista.Inserir(sucessores);
264 lista.Estado()->DebugSucessores(sucessores);
265 }
266 return -1; // falhou
267}
268
270// Procura em Profundidade Primeiro: expande primeiro o estado gerado não expandido mais novo
271// caso o nivel=-1, e feita uma procura em profunidade normal
272// caso o nivel>0, e feita uma procura em profundidade limitada
273// caso o nivel=0, e feita uma procura em profundidade iterativa, sem limite
274// versão recursiva
276{
277 DebugChamada();
278 if (nivel == 0) { // metodo iterativo
279 int resultado = -1;
280 do {
281 // limpar hashtable: estados gerados no nível anterior não devem impedir nova geração
282 LimparHT();
283 DebugIteracao(nivel + 1);
284 // chamar a profundidade nível 1, e se não resolver, o nível 2, e assim sucessivamente
286 } while (resultado == -1 && !Parar());
287 return resultado;
288 }
289
290 // metodo normal
291 // verificar se o estado atual é objetivo, ou seja, a solução parcial é já completa
292 if (SolucaoCompleta())
293 return SolucaoEncontrada();
294
295 if ((nivel > 1 || nivel < 0) && !Parar()) {
296 // caso o nível seja superior a 1 ou sem limite, expandir o estado atual
299 // tentar todos os sucessores, um de cada vez
300 for (int i = 0; i < sucessores.Count(); i++) {
301 DebugExpansao(i, sucessores.Count());
302 // chamada recursiva, reduzindo o nível
303 if (sucessores[i]->ProfundidadePrimeiro(nivel - 1) >= 0)
304 // este sucessor resolveu o problema, retornar
305 return SolucaoParcial(i, sucessores);
306 }
307 // nenhum dos sucessores resolveu o problema
308 DebugCorte(sucessores.Count());
310 }
311 else
312 DebugCorte();
313
314 // falha na procura neste nó, ou não há estado objetivo a partir daqui
315 // ou atingiu-se o limite, pelo que temos de retornar -1
316 return -1;
317}
318
320 // sucesso!
322 if (solucao == NULL)
323 solucao = Duplicar();
324 else // caso existam várias soluções, substitui a anterior
325 solucao->Copiar(this);
327 return solucao->custo = custo;
328}
329
331{
332 // solução parcial já registada, adicionar este nó e retornar o custo até ao momento
334 return solucao->custo;
335}
336
338 for (int i = 0; i < caminho.Count(); i++) {
339 if (Parametro(VER_ACOES) > 1) {
340 // mostrar o estado a cada K ações, no início e no fim
341 if (i % Parametro(VER_ACOES) == 0 || i == caminho.Count() - 1) {
342 caminho[i]->Debug();
343 // mostrar custo
344 printf(" (g:%d) ", caminho[i]->custo);
345 }
346 // mostrar a ação
347 if (i < caminho.Count() - 1)
348 printf(" %s", caminho[i]->Acao(caminho[i + 1]));
349 }
350 else {
351 caminho[i]->Debug();
352 printf(" (g:%d) ", caminho[i]->custo);
353 }
354 }
355 if (caminho.Empty())
356 printf("Caminho vazio.");
357}
358
359
361// Algoritmo Melhor Primeiro: expande primeiro o estado gerado não expandido com melhor heurística
362// versão recursiva, idêntico a ProcuraPrimeiro()
364{
365 DebugChamada();
366 if (SolucaoCompleta())
367 return SolucaoEncontrada();
368
369 if ((nivel <= 0 || nivel > 1) && !Parar()) {
371 TVector<int> id; // índice para ordenar os sucessores por heurística
373 CalcularHeuristicas(sucessores, &id); // id fica ordenado por heurística
374 // processar todos os sucessores da mesma forma que a procura em profundidade,
375 // mas utilizando a ordem id, por heurística
376 for (int i = 0; i < id.Count(); i++) {
377 DebugExpansao(i, id.Count());
378 if (sucessores[id[i]]->MelhorPrimeiro(nivel - 1) >= 0)
379 return SolucaoParcial(id[i], sucessores);
380 }
381 DebugCorte(sucessores.Count());
383 }
384 else
385 DebugCorte();
386 return -1;
387}
388
390// Algoritmo IDAStar: procura em profundidade limitada a um upperBound
391// idêntico a MelhorPrimeiro() mas cortando quando upperBound já não consegue ser obtido
393{
394 DebugChamada();
395 if (upperBound == 0) { // parte iterativa
396 int resultado = -1;
397 // primeiro valor para o lower bound, a heurística no nó raiz
398 lowerBound = Heuristica(); // o custo é zero atualmente
399 if (lowerBound == 0)
400 lowerBound++;
401 do {
402 // limpar hashtable: estados gerados no nível anterior não devem impedir nova geração
403 LimparHT();
405 // ver se há uma solução com este valor
407 // o valor de lowerBound é atualizado, para utilizar na próxima iteração se necessário
408 } while (resultado == -1 && !Parar());
409 return resultado;
410 }
411
412 if (SolucaoCompleta())
413 return SolucaoEncontrada();
414
415 if (!Parar()) {
417 TVector<int> id; // índice para ordenar os sucessores por heurística
419 CalcularHeuristicas(sucessores, &id, true); // id fica ordenado por LB
420 // processar todos os sucessores da mesma forma que a procura em profundidade,
421 // mas utilizando a ordem id, por heurística
422 for (int i = 0; i < id.Count(); i++) {
423 int atual = sucessores[id[i]]->LowerBound();
424 DebugExpansao(i, id.Count());
425 if (atual > upperBound) {
426 // acima do permitido nesta iteração
427 if (lowerBound == upperBound || lowerBound > atual)
428 lowerBound = atual;
429 DebugCorte(); // estado cortado, não expandido
430 }
431 else {
432 if (sucessores[id[i]]->IDAStar(upperBound) >= 0)
433 return SolucaoParcial(id[i], sucessores);
434 }
435 }
436 DebugCorte(sucessores.Count());
438 }
439 return -1;
440}
441
443// Algoritmo BranchAndBound:
444// idêntico a MelhorPrimeiro(), mas continua após a primeira solução
445// explora apenas os estados em que f(n) < atual solução (LB<UB)
447{
448 DebugChamada();
449 if (SolucaoCompleta())
450 return SolucaoEncontrada(true);
451
452 if (!Parar()) {
454 TVector<int> id; // índice para ordenar os sucessores por heurística
456 CalcularHeuristicas(sucessores, &id, true); // id fica ordenado por LB
457 // processar todos os sucessores, mas apenas se LB<UB
458 for (int i = 0; i < id.Count(); i++) {
459 DebugExpansao(i, id.Count());
460 if (upperBound && sucessores[id[i]]->LowerBound() >= upperBound) {
461 DebugCorte(); // estado cortado, e os seguintes não serão expandidos
462 break;
463 }
464 int resultado = sucessores[id[i]]->BranchAndBound(upperBound);
465 // mesmo que exista um resultado, continuar à procura de melhor
466 if (resultado > 0 && (!upperBound || resultado < upperBound))
468 }
469 DebugCorte(sucessores.Count());
471 }
472 return (solucao != NULL ? solucao->custo : -1);
473}
474
476 TVector<TNo>& sucessores, TVector<int>* id, bool sortLB)
477{
479 // obter os valores heurísticos, e ordentar o índice
480 for (int i = 0; i < sucessores.Count(); i++) {
481 sucessores[i]->heuristica = sucessores[i]->Heuristica();
482 if (id != NULL)
483 heuristicas += (sucessores[i]->heuristica + (sortLB ? sucessores[i]->custo : 0));
484 }
485 if (id != NULL)
486 heuristicas.Sort(id); // ordenar id
487}
488
489
491// Algoritmo AStar: expande primeiro o estado gerado não expandido com melhor custo+heurística
492// Idêntico a CustoUniforme()
494{
495 CListaNo lista(limite);
496 lista.Inserir(this); // estado único
497
498 for (lista.atual = 0; !Parar() && lista.Estado() != NULL; lista.atual = lista.Proximo()) {
499 lista.Estado()->DebugPasso();
500 if (lista.Estado()->SolucaoCompleta())
501 return ObjetivoAlcancado(lista.Estado(), lista.Completa());
502
504 lista.Estado()->Sucessores(sucessores);
505 // atualiza as heurísticas, sendo esta a única diferença para CustoUniforme
507 // insere por ordem de custo, e não apenas no final
508 lista.Inserir(sucessores);
510 lista.Estado()->DebugSucessores(sucessores);
511 }
512
513 return -1; // falhou
514}
515
516// Redefinir para poder utilizar os algoritmos informados
517// Esta função deve devolver o custo estimado por baixo,
518// desde este estado até ao estado final mais proximo (é um valor minimo),
519// colocando esse valor na variável heuristica
520// chamar este método para actualiacao de avaliacoes
529
530
531// Metodo para ser chamado antes de analisar cada sucessor
532void TProcuraConstrutiva::DebugExpansao(int sucessor, int sucessores, bool duplo)
533{
534 if (Parametro(NIVEL_DEBUG) >= PASSOS) {
535 if (sucessor > 0)
536 NovaLinha(false);
537
538 if (sucessor == 0 && sucessores == 1) { // só um ramo
539 DebugRamo(Parametro(NIVEL_DEBUG) < COMPLETO ? '-' : ' ', duplo ? '#' : '+');
540 ramo += ' '; // a ser impresso nesta posição nas linhas seguintes
541 }
542 else if (sucessor == 0) { // início e continua
543 DebugRamo(Parametro(NIVEL_DEBUG) < COMPLETO ? '-' : ' ', duplo ? '#' : '+');
544 ramo += (duplo ? '/' : '|'); // a ser impresso nesta posição nas linhas seguintes
545 }
546 else if (sucessor > 0 && sucessor < sucessores - 1) { // no meio e continua
547 DebugRamo(' ', duplo ? '#' : '+');
548 ramo.Last() = (duplo ? '/' : '|');
549 }
550 else {
551 DebugRamo(' ', duplo ? '#' : '+'); // no fim, vai acabar
552 ramo.Last() = ' '; // a ser impresso nesta posição nas linhas seguintes
553 }
554 }
555}
556
557void TProcuraConstrutiva::DebugRamo(char ramo, char folha) {
558 for (int i = 0; i < espacosRamo; i++)
559 printf("%c", ramo);
560 printf("%c", folha);
561}
562
563
564// Metodo para ser chamado quando nao ha sucessores ou ha um corte de profundidade
565void TProcuraConstrutiva::DebugCorte(int sucessores, bool duplo)
566{
567 if (Parametro(NIVEL_DEBUG) >= PASSOS) {
568 if (sucessores < 0) {
570 printf("%c ", '='); // corte de profundidade
571 DebugEstado();
573 Debug();
574 }
575 }
576 else if (sucessores > 0)
577 ramo.Pop();
578 else if (Parametro(NIVEL_DEBUG) < COMPLETO) { // ramo em que nao e possivel continuar
579 printf("%c ", '&');
580 DebugEstado();
582 Debug();
583 }
584 }
585}
586
587// Encontrou uma solucao
589{
591 printf(" Solução encontrada!");
592 if (!continuar)
593 ramo = {};
594 Debug();
595 printf("(g:%d)", custo);
596 }
597 else {
599 Debug();
601 ramo.Pop();
602 }
603}
604
605// Informacao de debug na chamada ao metodo recursivo
607{
608 if (Parametro(NIVEL_DEBUG) == ATIVIDADE && expansoes % 1000 == 0)
609 printf("#");
611 // neste nível, cada estado expandido é visualizado, não apenas os estados folha
612 DebugEstado();
613 if (pai != NULL)
614 printf(" %s", pai->Acao(this)); // mostra sempre a ação
615 if (Parametro(VER_ACOES) == 1 || pai == NULL)
616 Debug();
617 NovaLinha();
618 }
619}
620
621// Chamar sempre que se quer uma nova linha com a arvore em baixo
623{
624 printf("\n");
625 for (int i = 0; i < ramo.Count() - (tudo ? 0 : 1); i++)
626 printf("%*s%c", espacosRamo, " ", ramo[i]);
627}
628
629// Passo no algoritmo em largura
631{
632 if (Parametro(NIVEL_DEBUG) == ATIVIDADE && expansoes % 1000 == 0)
633 printf("#");
634 if (Parametro(NIVEL_DEBUG) >= PASSOS) {
635 printf("\n");
636 DebugEstado();
637 }
639 Debug();
640}
641// Mostrar sucessores
643 if (Parametro(VER_ACOES) > 2) {
644 // mostrar apenas ações
645 printf("\nAções: ");
646 for (int i = 0; i < sucessores.Count() && i < 30; i++) {
647 printf("%s ", Acao(sucessores[i]));
648 if (i % 10 == 9)
649 printf("\n ");
650 }
651 }
652 else {
653 ramo = {};
654 ramo += ' ';
655 for (int i = 0; i < sucessores.Count() && i < 30; i++) {
656 ramo.First() = '+';
657 NovaLinha();
658 sucessores[i]->DebugEstado(i + 1);
659 if (i < sucessores.Count() - 1)
660 ramo.First() = '|';
661 else
662 ramo.First() = ' ';
663 printf(" %s", Acao(sucessores[i])); // mostra sempre a ação
664 if (Parametro(VER_ACOES) == 1)
665 sucessores[i]->Debug();
666 }
667 ramo = {};
668
669 }
670}
671
672
673// uma nova iteração de um algoritmo iterativo
675 Debug(ATIVIDADE,true, "\n") ||
676 Debug(PASSOS,true, "\nIteração %d:\n", iteracao) ||
677 Debug(DETALHE, false, "\nIteração %d: (expansões %d, gerações %d, avaliações %d)\n",
679}
680
681// informação geral sobre o estado
682void TProcuraConstrutiva::DebugEstado(int id, int pai) const {
683 if (id >= 0) {
684 printf("#%d ", id);
685 if (pai >= 0)
686 printf("(#%d) ", pai);
687 }
688 printf("g:%d ", custo);
689 if (heuristica)
690 printf("h:%d ", heuristica);
691
692 if (expansoes)
693 printf("%d", expansoes);
694 if (geracoes)
695 printf("|%d", geracoes);
696 if (iteracoes)
697 printf("|%d", iteracoes);
698}
699
700
701// Chamar antes de iniciar uma procura
703{
705 geracoes = expansoes = 0;
706 ramo = {};
707 while (!caminho.Empty())
708 delete caminho.Pop();
709 if (solucao != NULL)
710 delete solucao;
711 solucao = NULL;
712 LimparHT();
713}
714
715
717 int resultado = -1;
718 switch (Parametro(ALGORITMO)) {
719 case 1: resultado = LarguraPrimeiro(Dominio(Parametro(LIMITE), 0)); break;
720 case 2: resultado = CustoUniforme(Dominio(Parametro(LIMITE), 0)); break;
722 case 4: resultado = MelhorPrimeiro(Parametro(LIMITE)); break;
723 case 5: resultado = AStar(Dominio(Parametro(LIMITE), 0)); break;
724 case 6: resultado = IDAStar(Dominio(Parametro(LIMITE), 0)); break;
725 case 7: resultado = BranchAndBound(Dominio(Parametro(LIMITE), 0)); break;
726 }
727 return custo = resultado;
728}
729
730
731
733{
735 if (solucao != NULL) {
737 delete solucao;
738 solucao = NULL;
739 }
740}
741
742
745 int opcao = 0;
747 do {
748 caminho += Duplicar();
749 if (caminho.Count() > 1)
750 caminho.Last()->custo += caminho[caminho.Count() - 2]->custo;
751 else
752 caminho.Last()->custo = 0;
756 // linha com informação
757 DebugEstado();
758 Debug();
760 if (sucessores.Empty()) {
761 printf("\nSem sucessores.");
762 opcao = 0;
763 }
764 else {
765 char str[BUFFER_SIZE];
766 printf("\nSucessor [1-%d, ação(ões), exe]:", sucessores.Count());
768 opcao = atoi(str);
769 if (opcao == 0 && strlen(str) > 1) {
770 char* token;
771 opcao = sucessores.Count() + 1;
772 token = strtok(str, " \t\n\r");
773 // executar algoritmo
774 if (strcmp(token, "exe") == 0) {
776 backup = caminho;
777 caminho = {};
779 int resultado = 0;
780 switch (resultado = ExecutaAlgoritmo()) {
781 case -1: printf("Impossível\n"); break;
782 case -2: printf("Não resolvido\n"); break;
783 default: printf("Resolvido (%d)\n", resultado); break;
784 }
786 if (solucao != NULL) {
788 delete backup.Pop();
789 backup += caminho;
790 caminho = backup;
791 caminho.Pop(); // este último estado será adicionado
792 }
793 else
794 caminho = backup;
795 }
796 else {
797 int nAcoes = 0;
798 do {
799 // executar a ação
800 if (Acao(token))
801 nAcoes++;
802 else {
803 printf("Ação %s inválida.\n", token);
804 break;
805 }
806 token = strtok(NULL, " \t\n\r");
807
808 if (token != NULL) {
809 // há outra ação, é preciso atualizar o caminho
810 caminho += Duplicar();
811 if (caminho.Count() > 1)
812 caminho.Last()->custo += caminho[caminho.Count() - 2]->custo;
813 else
814 caminho.Last()->custo = 0;
815 }
816
817 } while (token != NULL);
818 if (nAcoes > 0)
819 printf("Executadas %d ações com sucesso.\n", nAcoes);
820 }
821 }
822 }
823 if (opcao > 0 && opcao <= sucessores.Count())
824 Copiar(sucessores[opcao - 1]);
826 } while (opcao != 0);
827}
828
830 // utilizando FNV-1 hash (http://www.isthe.com/chongo/tech/comp/fnv/)
831 uint32_t h = 2166136261;
833 for (int i = 0; i < OBJETO_HASHTABLE && estadoCodHT[i]; i++) {
834 uint64_t valor = estadoCodHT[i];
835 // processar cada octeto
836 // quando o valor fica 0, já não interessa
837 // já que existindo dados, são distintos de 0
838 for (int i = 0; i < 8 && valor; i++) {
839 unsigned char octeto = valor & 255;
840 valor >>= 8;
841 h *= 16777619;
842 h ^= octeto;
843 }
844 }
845 return h % TAMANHO_HASHTABLE;
846}
847
851 int usado = 0; // contar para calcular taxa de ocupação
852 for (int i = 0; i < TAMANHO_HASHTABLE; i++) {
853 bool limpo = true;
854 for (int j = 0; j < tamanhoCodificado; j++)
855 if (elementosHT[i][j] != 0) {
856 limpo = false;
857 elementosHT[i][j] = 0;
858 }
859 if (!limpo)
860 usado++;
861 }
862 // reportar estatísticas se existir muito reuso
863 if (usado > 0 && usado * 2 <= colocadosHT) {
864 printf("\nHT: utilização %d%%, reuso: %.2f vezes",
865 usado * 100 / TAMANHO_HASHTABLE,
866 1.0 * colocadosHT / usado);
867 }
868 }
869 else
870 for (int i = 0; i < TAMANHO_HASHTABLE; i++)
871 for (int j = 0; j < tamanhoCodificado; j++)
872 elementosHT[i][j] = 0;
873 colocadosHT = 0;
874 // coloca o estado atual na hasttable, para não ser gerado
875 ExisteHT();
876 }
877}
878
880 // caso não exista, retorna falso e insere
881 // se existe retorna verdade, e estiver lá outro elemento, substitui
882 unsigned int original = Hash();
883 unsigned int indice = original % TAMANHO_HASHTABLE;
884 for (int i = 0; i < tamanhoCodificado; i++)
885 if (elementosHT[indice][i] != estadoCodHT[i]) {
886 SubstituirHT(indice);
887 colocadosHT++;
888 return false; // não existia
889 }
890 // elemento é igual, mas se o custo for mais alto, não conta
891 // já que este elemento com custo mais baixo, pode conduzir à solução ou melhores soluções
892 // neste caso substitui e retorna que o estado não existe
893 if (custoHT[indice] > custo) {
894 SubstituirHT(indice);
895 return false; // existe mas com um custo mais alto
896 }
897 return true; // igual, estado já analisado
898}
899
901{
902 // substituir elemento
903 for (int i = 0; i < tamanhoCodificado; i++)
904 elementosHT[indice][i] = estadoCodHT[i];
905 custoHT[indice] = custo;
906}
907
908// estados repetidos: verificar todos os gerados
909// implementar para utilizar hashtables com perdas
910// converte o estado atual para a variável estado, utilizando o menor espaço possível
911// caso existam simetrias, normalizar o estado antes de codificar, para considerar exploradas todas as versões
913 for (int i = 0; i < tamanhoCodificado; i++)
914 estado[i] = 0;
915}
916
918{
919 if (id == IND_CUSTO)
920 return custo;
921 else if (id == IND_EXPANSOES)
922 return expansoes;
923 else if (id == IND_GERACOES)
924 return geracoes;
925 else if (id == IND_LOWER_BOUND)
926 return lowerBound;
927 return TProcura::Indicador(id);
928}
929
930
931
constexpr int BUFFER_SIZE
constexpr int BUFFER_SIZE
@ VER_ACOES
Mostra estado a cada K ações. Se 1, mostra sempre estados e nunca ações.
@ LIMITE
Valor dependente do algoritmo. Exemplo: Profundidade limitada.
@ BARALHAR_SUCESSORES
Baralhar os sucessores ao expandir.
@ RUIDO_HEURISTICA
Ruído a adicionar à heurística para testes de robustez.
@ ESTADOS_REPETIDOS
Forma de lidar com estados repetidos (ignorá-los, ascendentes, gerados).
constexpr int OBJETO_HASHTABLE
@ IND_GERACOES
número de estados gerados durante a procura
@ IND_CUSTO
o resultado é o custo da solução atual, sendo um upper bound
@ IND_EXPANSOES
número de expansões efetuadas durante a procura
@ IND_LOWER_BOUND
valor mínimo para a melhor solução, se igual ao custo da solução obtida, então esta é ótima
@ 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
constexpr int TAMANHO_HASHTABLE
@ PASSOS
Exibe passos intermediários.
Definition TProcura.h:65
@ ATIVIDADE
Apenas eventos principais.
Definition TProcura.h:64
@ COMPLETO
Mostra toda a execução detalhadamente.
Definition TProcura.h:67
@ NADA
Sem informações de debug.
Definition TProcura.h:63
@ DETALHE
Debug detalhada sobre estados e decisões.
Definition TProcura.h:66
@ CONT_ALGORITMO
Tempo da execução do algoritmo por instância.
Definition TProcura.h:84
@ ALGORITMO
Algoritmo base a executar.
Definition TProcura.h:42
@ NIVEL_DEBUG
Nível de debug, de reduzido a completo.
Definition TProcura.h:43
TVector< int > _TV(const char *str)
Definition TVector.h:1154
Lista ordenada de nós para algoritmos de procura informada.
Definition CListaNo.h:21
Representa um estado no espaço de estados.
void DebugCorte(int sucessores=-1, bool duplo=false)
void DebugSucessores(TVector< TNo > &sucessores)
void DebugRamo(char ramo, char folha)
int SolucaoEncontrada(bool continuar=false)
void DebugSolucao(bool continuar=false)
void ExecucaoTerminada() override
Chamar após a execução do algoritmo. Grava o tempo consumido.
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)
void NovaLinha(bool tudo=true)
int ObjetivoAlcancado(int item, TVector< TNo > &lista)
static void LibertarVector(TVector< TNo > &vector, int excepto=-1, int maiorQue=-1)
virtual void SubstituirHT(int indice)
void Explorar()
definir para explorar manualmente os dados (não definido em TProcura, apenas em TProcuraConstrutiva)
void CalculaCaminho(bool completa=true)
void DebugExpansao(int sucessor, int sucessores, bool duplo=false)
void LimparEstatisticas() override
Chapar antes da execução do algoritmo. Limpa valores estatísticos, e fixa o instante limite de tempo para...
void DebugEstado(int id=-1, int pai=-1) const
void DebugIteracao(int iteracao)
void VerificaLimites(int limite, int porProcessar, TVector< TNo > &sucessores)
static int custoHT[TAMANHO_HASHTABLE]
static uint64_t estadoCodHT[OBJETO_HASHTABLE]
static bool memoriaEsgotada
Flag indicando problemas de memória esgotada.
Definition TProcura.h:501
virtual void Debug(bool completo=true)
Mostra o estado no ecrã, para debug.
Definition TProcura.cpp:93
int Parametro(int id) const
Definition TProcura.h:522
static int Dominio(int &variavel, int min=INT_MIN, int max=INT_MAX)
Limita o domínio de um parâmetro inteiro.
static int resultado
Resultado retornado pelo algoritmo na última execução.
Definition TProcura.h:493
virtual int64_t Indicador(int id)
Retorna um indicador, após a execução do algoritmo.
Definition TProcura.cpp:80
static int iteracoes
Número total de iterações realizadas na última execução.
Definition TProcura.h:497
virtual void ResetParametros()
Inicializa os parametros, indicadores e instâncias.
Definition TProcura.cpp:47
virtual bool Parar(void)
Verifica se a procura deve ser interrompida.
Definition TProcura.h:363
virtual void ExecucaoTerminada()
Chamar após a execução do algoritmo. Grava o tempo consumido.
static TVector< TIndicador > indicador
Indicadores que podem ser calculados após a execução, quer com informação da instãncia, quer com resultado da ...
Definition TProcura.h:488
static TVector< int > indAtivo
Definition TProcura.h:489
static TVector< TParametro > parametro
Parâmetros a serem utilizados na configuração atual.
Definition TProcura.h:485
static double Cronometro(enum ECronometro id=CONT_ALGORITMO, bool inicialiar=false)
retorna o tempo em segundos desde que o cronómetro foi inicializado
Definition TProcura.h:750
virtual void LimparEstatisticas()
Chapar antes da execução do algoritmo. Limpa valores estatísticos, e fixa o instante limite de tempo para...
Definition TProcura.cpp:99
static double tempo
tempo consumido na última execução.
Definition TProcura.h:495
Item & First()
Definition TVector.h:221
Item & Pop()
Definition TVector.h:190
TVector< Item > & Sort(TVector< int > *idxvect=nullptr)
Ordena todo o vetor, opcionalmente devolvendo índices ordenados.
Definition TVector.h:597
Item & Last()
Definition TVector.h:224
int Count() const
Definition TVector.h:227
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)
void ResetParametros() override
Redefinição. Ver TProcura::ResetParametros().
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.
int64_t Indicador(int id) override
Redefinição. Ver TProcura::Indicador().
int ExecutaAlgoritmo()
Executa o algoritmo com os parametros atuais.
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 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 de expansões efetuadas.
static int lowerBound
Valor mínimo que a solução pode apresentar, obtido pela procura.
static int tamanhoCodificado
Número de inteiros de 64 bits utilizados para codificar um objeto (≤ OBJETO_HASHTABLE).
static int geracoes
Número de estados gerados.
static TVector< TNo > caminho
Solução retornada pela procura (os estados devem ser libertados).
static TVector< unsigned char > ramo
static TNo solucao
Estado objetivo encontrado, retornado pela procura (deve ser libertado).
unsigned int rand(int seq)
Retorna o próximo valor pseudo-aleatório.
Definition TRand.cpp:46