Prévia do material em texto
Ariel Da Silva Dias COMPLEXIDADE DE PROBLEMAS Sumário INTRODUÇÃO ������������������������������������������������� 3 CLASSES DE PROBLEMAS: P E NP ���������������� 8 Algoritmos NP ��������������������������������������������������������������������� 11 Algoritmos NP-Completos ������������������������������������������������� 13 Algoritmos NP-Hard ������������������������������������������������������������ 14 Podemos dizer que P = NP? ����������������������������������������������� 15 FORÇA BRUTA E HEURÍSTICAS �������������������17 Algoritmos de força bruta �������������������������������������������������� 17 Pesquisa heurística ������������������������������������������������������������ 25 Diferença entre Força Bruta e Pesquisa heurística ���������� 29 PROBLEMA DO CAIXEIRO VIAJANTE ��������� 32 CONSIDERAÇÕES FINAIS ����������������������������36 REFERÊNCIAS BIBLIOGRÁFICAS & CONSULTADAS ��������������������������������������������38 2 INTRODUÇÃO A maioria dos algoritmos transforma objetos de entrada em objetos de saída� O tempo de execução de um algoritmo ou método de estrutura de dados geralmente cresce com o tamanho da entrada, embora também possa variar para diferentes en- tradas do mesmo tamanho� Além disso, o tempo de execução é afetado por muitos fatores, como o ambiente de hardware e o ambiente de software� Apesar das possíveis variações decorrentes de diferentes fatores ambientais, os estudos apre- sentados aqui se concentram na relação entre o tempo de execução de um algoritmo e o tamanho de sua entrada� Deste modo, em cada análise, tere- mos o tempo de execução de um algoritmo como uma função , em que n é o tamanho da entrada, o que pode ser representado em uma expressão polinomial� Revendo então as principais funções da análise de um algoritmo, temos: y Função constante f(n)=C: Para qualquer ar- gumento n, a função constante atribui o valor C� Não importa qual seja o tamanho da entrada n, f(n) sempre será igual ao valor constante C� A função constante mais fundamental é f(n) = 1 , o que caracteriza o número de etapas necessárias para 3 realizar uma operação básica em um computador, como adicionar dois números, atribuir um valor a alguma variável ou comparar dois números� Deste modo, um algoritmo constante não depende do tamanho da entrada� y Função logarítmica f(n)=log n: trata-se de um dos aspectos interessantes e surpreendentes da análise de estruturas de dados e algoritmos� A forma geral de uma função logarítmica é , para alguma constante , onde b é a base do logaritmo� A aproximação de base dois surge na análise de algoritmos, uma vez que uma operação comum em muitos algoritmos é dividir repetidamente uma entrada pela metade� Normalmente deixamos de lado a escrita do valor da base quando ela é 2, e apresentamos apenas log n� A função logarítmica fica um pouco mais lenta à medida que n cresce. Sempre que n dobra, o tempo de execução aumenta em uma constante� O exemplo mais conhecido é o algoritmo de busca binária� y Função linear f(n)=n: função simples, mas importante� Dado um valor de entrada n, a função linear f atribui o próprio valor n� Essa função surge em uma análise de algoritmo sempre que fazemos uma única operação básica para cada um dos n elementos� Por exemplo, comparar um número x com cada elemento de uma matriz de tamanho n exigirá n comparações� A função linear também representa o melhor tempo de execução que es- 4 peramos alcançar para qualquer algoritmo que processe uma coleção de n entradas� y Função N-Log-N f(n)=n log n : essa função cresce um pouco mais rápido que a função linear e muito mais devagar que a função quadrática� Se pudermos melhorar o tempo de execução da resolução de algum problema de quadrático para N-Log-N, teremos um algoritmo que roda muito mais rápido em geral� Isso se transforma em um grande problema, pois sempre que n dobra, o tempo de execução mais que dobra� y Função quadrática f(n)=n²: essa função apa- rece muito na análise de algoritmos, pois existem muitos algoritmos que possuem loops aninhados, onde o loop interno realiza um número linear de operações e o loop externo é realizado um número linear de vezes� Nesses casos, o algoritmo executa n * n = n² operações� y Função exponencial f(n)=bn: nessa função, b é uma constante positiva, chamada de base e o argumento n é o expoente� Na análise do algoritmo, a base mais comum para a função exponencial é b = 2� Por exemplo, se tivermos um loop que começa executando uma operação e depois dobra o número de operações executadas a cada iteração, então o número de operações executadas na enésima iteração é 2n� O algoritmo exponencial geralmente não é apropriado para uso prático, um exemplo são as Torres de Hanói� 5 Em um cenário ideal, gostaríamos que as opera- ções da estrutura de dados fossem executadas em tempos proporcionais à função constante ou logarítmica e gostaríamos que nossos algoritmos fossem executados em tempo linear ou n-log-n� Al- goritmos com tempos de execução quadráticos ou cúbicos são menos práticos, mas algoritmos com tempos de execução exponenciais são inviáveis para todas as entradas, exceto as de menor tamanho� Observe que esse conjunto de algoritmos que verificamos até agora podem ser resolvidos em tempo polinomial, por esse motivo, são chamados de algoritmos polinomiais� Por outro lado, existem alguns algoritmos que não podem ser resolvidos ou não podem ser verificados em tempo polinomial. Diz-se que um algoritmo é solucionável em tempo polinomial se o número de passos necessários para completar o algoritmo para uma dada entra- da for O(nk) para algum inteiro não negativo k, em que n é a complexidade da entrada� Diz-se que algoritmos de tempo polinomial são «rápidos»� As operações matemáticas mais conhecidas, como adição, subtração, multiplicação e divisão, bem como calcular raízes quadradas, potências e logaritmos, podem ser executadas em tempo polinomial� O cálculo dos dígitos das constantes matemáticas mais interessantes, incluindo o pi (π) pode ser feito em tempo polinomial� 6 Neste e-book, estudaremos as diversas classes de problemas em análise de algoritmos, conside- rando os algoritmos que podem ser solucionados em tempo polinomial e aqueles que não podem� Também conhecerá dois tipos de algoritmos que são muito utilizados em diferentes áreas, mas principalmente em inteligência artificial: força bruta e busca heurística� 7 CLASSES DE PROBLEMAS: P E NP Considere, por exemplo, que você tenha uma lista não ordenada de números e queira escrever um al- goritmo para encontrar o maior� O algoritmo, então, tem que olhar para todos os números da lista: não há como contornar isso� Mas se ele simplesmente mantiver um registro do maior número visto até agora, ele terá que olhar para cada entrada apenas uma vez� O tempo de execução do algoritmo é, portanto, diretamente proporcional ao número de elementos que ele está manipulando, ou seja, o tempo será n� É claro que a maioria dos algoritmos é mais complicada e, portanto, menos eficiente do que aquela para encontrar o maior número em uma lista; mas muitos algoritmos comuns têm tempos de execução proporcionais a n2, ou n vezes o loga- ritmo de n, ou similares� Uma expressão matemática que envolve n’s e n²s e n’s elevados a outras potências é chamado de polinômio, ou seja, P é o conjunto de problemas cujos tempos de solução são proporcionais aos polinômios envolvendo n’s� Todas as principais funções de P foram mencionadas na introdução deste e-book� 8 Desse modo, um algoritmo cujo tempo de execução é proporcional a é mais lento do que um algorit- mo cujo tempo de execução é proporcional a n� Mas tais diferenças se tornam insignificantes em comparação a outra distinção, entre expressões polinomiais e expressões em que um número é elevado à enésima potência, como, digamos, 2n� Enquanto, por exemplo, um algoritmo cujo tempo de execução é proporcional a nleva um segundo para realizar uma computação envolvendo 100 elementos, um algoritmo cujo tempo de execução é proporcional a n3 pode levar quase três horas� Já um algoritmo cujo tempo de execução é propor- cional a 2n leva 300 quintilhões de anos – e essa discrepância fica muito, muito pior quanto mais o N cresce� Considere, por exemplo, os problemas da vida real (do dia a dia). Embora possamos usar uma ampla variedade de termos para classificar os problemas, na maioria dos casos usamos uma escala Fácil a Difícil� Na ciência da computação teórica, a classificação e a complexidade das definições de problemas comuns têm dois conjuntos principais: P, que é o tempo polinomial que foi apresentado, e NP, que é o tempo polinomial não determinístico� Existem também conjuntos NP-Difícil e NP-Completo, que 9 usamos para expressar problemas mais sofisti- cados. No caso de classificação de fácil a difícil, podemos rotulá-los como fácil (P), médio (NP), difícil (NP-Completo) e, finalmente, mais difícil (NP-Hard). O relacionamento deles é apresentado na figura 1. Figura 1: relacionamento P, NP, NP-Completo e NP-Hard NP - Hard NP - Completo NP P Mais difícil Difícil Médio Fácil Fonte: elaborado pelo autor� No diagrama da figura 1, assumimos que P e NP são o mesmo conjunto, ou, em outras palavras, assumimos que P é diferente de NP ou P≠NP� Essa é uma afirmação aparentemente verdadeira, mas ainda não comprovada� Outro aspecto interessante deste diagrama é que existe alguma sobreposição entre NP e NP-Difícil� Chamamos NP-Completo quando o problema pertence a ambos os conjuntos� 10 Logo, após mapear P, NP, NP-Completo e NP-Difícil para “fácil”, “médio”, “difícil” e “muito difícil”, uma pergunta pode ser colocada: como é possível colocar um determinado algoritmo em cada uma destas categorias? Para isso, serão apresentadas a seguir algumas formalidades� ALGORITMOS NP NP (que significa tempo polinomial não determinís- tico) é o conjunto de problemas cujas soluções podem ser verificadas em tempo polinomial, mas não podem ser resolvidos em tempo polinomial� Mas, até onde qualquer um pode verificar, muitos desses problemas levam um tempo exponencial para serem resolvidos� Talvez o problema de tempo exponencial mais famoso em NP, por exemplo, seja encontrar fatores primos de um grande número� Verificar uma solução requer apenas multiplicação, mas resolver o problema parece exigir sistemati- camente experimentar muitos candidatos� Espera-se que esses algoritmos tenham uma complexidade exponencial, que definiremos como: 𝑇𝑇 𝑛𝑛 = 𝑂𝑂(𝐶𝐶1 𝑥𝑥 𝑘𝑘𝐶𝐶2𝑥𝑥𝑛𝑛) , em que C1 > 0, C2 > 0 e k > 0 e , sendo que C1, C2 e k são constantes e n é o tamanho da entrada� T(n) é uma função do tempo exponencial quando pelo menos C1=1 e C2=1� Como resultado, obtemos 11 O(kn)� Por exemplo, observaremos complexidades como O(nn), O(2n), O(20.0000001xn) neste conjunto de problemas� Formalmente, é possível afirmar que esses pro- blemas NP devem ser problemas de decisão – ter uma resposta sim ou não – embora observe que, na prática, todos os problemas de função podem ser transformados em problemas de decisão� Essa distinção nos ajuda a definir o que queremos dizer com verificado. Para falar com precisão, então, um algoritmo é NP se não pode ser resolvido em tempo polinomial e o conjunto de soluções para qualquer problema de decisão pode ser verificado em tempo polinomial por uma “Máquina de Turing Determinística”� Um dos exemplos mais comuns e eficazes é o Sudoku� Dada uma grade de Sudoku não resol- vida (9 x 9, por exemplo), um algoritmo levaria um bom tempo para resolvê-lo� No entanto, se a grade 9 x 9 aumentar para uma grade de 100 x 100 ou 10�000 x 10�000, o tempo que levaria para resolvê-la aumentaria exponencialmente porque o problema em si se torna significativamente mais difícil� No entanto, dada uma grade de Sudoku resolvida (de 9 x 9), é bastante simples verificar se a solução específica está realmente correta, mesmo que o tamanho seja dimensionado para 12 10�000 x 10�000� Seria mais lento, mas o tempo para verificar uma solução aumenta a uma taxa mais lenta (polinomialmente). ALGORITMOS NP-COMPLETOS O próximo conjunto é muito semelhante ao ante- rior� Dando uma olhada no diagrama, todos eles pertencem a NP, mas estão entre os mais difíceis do conjunto� O que os torna diferentes de outros problemas NP é uma distinção útil chamada com- pletude� Para qualquer problema NP-Completo, existe um algoritmo de tempo polinomial que pode transformar o problema em qualquer outro proble- ma NP-completo� Esse requisito de transformação também é chamado de redução� Formalmente, podemos dizer que se trata de um conjunto de problemas a cada um dos quais qual- quer outro problema NP pode ser reduzido em tempo polinomial e cuja solução ainda pode ser verificada em tempo polinomial. Isso significa que qualquer problema NP pode ser transformado em um problema NP-Completo� Informalmente, eles são os “mais difíceis” dos problemas NP� Assim, se qualquer problema NP- -Completo pode ser resolvido em tempo polinomial, então se todo problema NP-Completo pode ser resolvido em tempo polinomial, todo problema em NP também pode ser resolvido em tempo polinomial 13 (ou seja, P = NP). O exemplo mais famoso seria o problema dos Caixeiros Viajantes� ALGORITMOS NP-HARD O último conjunto de problemas contém os pro- blemas mais difíceis e complexos da ciência da computação� Eles não são apenas difíceis de resolver, mas também difíceis de verificar� Entre os problemas mais difíceis da ciência da compu- tação estão: y Agrupamento K-means: trata-se de um termo genérico para uma classe de algoritmos não su- pervisionados para descobrir grupos de coisas, pessoas ou ideias que estão intimamente relacio- nadas entre si; y Problema do Caixeiro Viajante: este é o problema de otimização da ciência da computação mais conhecido no mundo moderno� Em outras pala- vras, trata-se de um problema de encontrar a rota ótima entre os nós no grafo� A distância total de viagem pode ser um dos critérios de otimização� Mais adiante foi reservado um tópico para falarmos um pouco mais sobre este algoritmo que tem sido empregado não somente na computação, mas em diversas áreas como controle de rota de entregas e rota de automóveis� Esses algoritmos têm uma propriedade semelhante aos de NP-Completo pois todos eles podem ser 14 reduzidos a qualquer problema em NP� Por causa disso, eles estão NP-Hard e são pelo menos tão difíceis quanto qualquer outro problema no NP� Um problema pode estar tanto em NP quanto em NP-Hard, que é outro aspecto de ser NP-Completo� Essa característica levou a um debate sobre se o algoritmo do Caixeiro Viajante é ou não NP-Comple- to� Como os problemas NP e NP-Completo podem ser verificados em tempo polinomial, provar que um algoritmo não pode ser verificado em tempo polinomial também é suficiente para colocar o algoritmo em NP-Hard� PODEMOS DIZER QUE P = NP? Uma questão que chama a atenção de muitos cientistas da computação é se todos os algoritmos NP pertencem ou não a P� Trata-se de um problema interessante porque significaria, por exemplo, que qualquer problema NP-Completo pode ser resolvido em tempo polinomial� Por causa desse problema, temos um dos Problemas do Prêmio do Milênio para o qual há um prêmio de US$ 1�000�000� Desse modo, torna-se possível afirmar que P! = NP, no entanto, P = NP ainda pode ser possível� Se fosse assim, além de problemas NP-Completos serem resolvidos em tempo polinomial, certos algoritmos NP-Difícil também simplificariam drasticamente. Por exemplo, se o verificador deles for NP ou NP- 15 -Completo, segue-se que eles também devem ser solucionáveis em tempo polinomial, movendo-os também para P = NP = NP - Completo� Agora que os conceitos foram todos definidos, uma pergunta mais importante a ser feita é: “Por que tudo isso importa?”� Ter uma solução paraesse problema pode ter consequências profundas não apenas no meio acadêmico, mas também em situações práticas� Isso inclui: y Criptografia: das muitas senhas que mantemos, seja do e-mail, do celular ou aquelas que usamos no caixa eletrônico, contamos com esses códigos para governar nossas vidas cotidianas porque são fáceis de verificar, mas difíceis de decifrar; y Roteamento de veículos: O transporte e o mo- vimento da logística seriam otimizados em todo o mundo, impactando muitos setores, desde o trans- porte até o comércio eletrônico e a manufatura; y Dobramento de proteínas: Compreender ou prever como uma determinada sequência de amino- ácidos (uma proteína) se dobrará para formar sua forma 3D ajudará em muitas áreas, como design de medicamentos e talvez até curar câncer� Observe então que ocorreria uma mudança radical na ciência da computação e até mesmo nos cená- rios do mundo real� Se encontrarmos um algoritmo de tempo polinomial, esses algoritmos descritos (e outros) se tornam vulneráveis a ataques. 16 FORÇA BRUTA E HEURÍSTICAS A inteligência artificial (IA) é um dos setores em maior expansão nos últimos anos, com diversos tipos de aplicações, que abrange desde a montagem de automóveis em uma linha de fábrica, passando pelo reconhecimento biométrico, até chegar em descoberta de doenças e suas curas na saúde� Trata-se então do estudo da construção de objetos que agem objetivamente� Essas Ias, para realizarem suas ações e alcança- rem seus objetivos, executam tipos específicos de algoritmos� Dois desses algoritmos são força bruta e busca heurística� ALGORITMOS DE FORÇA BRUTA O algoritmo de força bruta também é conhecido como busca cega ou busca uniforme� A força bruta pode localizar um estado não objetivo a partir de um estado objetivo, além disso, ela normalmente não tem controle sobre a busca escolhida� A busca cega ou força bruta é uma das principais estratégias de busca quando não se tem um caminho direto para a busca� O algoritmo de força bruta é usado quando não há outro algoritmo disponível para acelerar o pro- 17 cesso e é preciso verificar cada solução possível para poder resolvê-lo� Por exemplo, vamos pegar um cenário em que você precisa pesquisar no di- cionário o significado da palavra “psicossomático” usando o algoritmo de força bruta� Você precisa percorrer cada palavra para chegar à sua palavra- -alvo� Considerando um dicionário com n palavras, a complexidade de tempo seria igual a O(n), uma vez que leva n passos – sendo também o número de palavras do dicionário� Você pode usar o método de busca binária para chegar ao seu objetivo rapidamente, ou seja, dividir o dicionário em duas listas iguais e obter o ponto médio� Se a palavra vier antes do ponto médio, você pegará a primeira parte e descartará a outra metade� Isso se repetirá até que você descubra sua palavra� Nesse caso, a complexidade será O (log (n))� Você pode usar essas técnicas para acelerar o desempenho, mas se você tiver um dicionário de entrada não ordenado e precisar percorrer cada palavra para descobrir sua palavra-alvo, não terá o mesmo resultado� Você pode melhorar os algoritmos de força bruta reduzindo os critérios de pesquisa, pois são direta- mente proporcionais à entrada fornecida. Também pode ser melhorado usando diferentes abordagens 18 heurísticas para cada problema, o que resulta na solução ótima de um problema� O algoritmo de força bruta baseia-se na abordagem de tentativa e erro, que é uma metodologia fundamental na resolução de problemas� Nessa metodologia, o programa continua fazendo tentativas até obter sucesso no problema� Por exemplo, você tem o desafio de encontrar todos os inteiros entre 1 e 100�000�000 que são divisíveis por 332� Se você adotar uma abordagem simples, o algoritmo de força bruta geraria todos os inteiros que estão no intervalo� Você pode reduzir os critérios de pesquisa e torná-los mais eficientes começando com 332 e somando (adicionando) repetidamente o mesmo número até que o número exceda o limite determinado� Assim, você pode salvar os possíveis ensaios realizando análises em alguns casos� Existem vários tipos de algoritmos de força bru- ta, dentre eles a busca em largura e a busca em profundidade� Busca em largura e busca em profundidade Problemas de busca são aqueles em que nossa tarefa é encontrar o caminho ótimo entre um nó inicial e um nó objetivo em um grafo� Existem diversas variações para problemas de busca: o 19 grafo pode ser direcionado ou não direcionado, ponderado ou não ponderado, e pode haver mais de um nó objetivo� A busca em largura realiza a busca pela formação dos níveis de uma árvore� Ele liga vários tópicos visitados, obtendo o objetivo mais superficial do usuário que está mais próximo da raiz� A busca em profundidade: vagueia consecutiva- mente ao longo do caminho enquanto desce na árvore até surgir uma solução para o problema ou até chegar em um caminho sem saída, momento o qual ele volta atrás em seu caminho e cava outros caminhos� Ambos os algoritmos pesquisam sobrepondo uma árvore ao grafo, que chamamos de árvore de busca� Busca em profundidade e busca em largura definem sua raiz para o nó inicial e aumentam adicionando os sucessores das folhas atuais da árvore� Dessa forma, essas técnicas cobrem todo o grafo até encontrarem o nó objetivo ou esgotarem o grafo� O que eles diferem é a ordem em que visitam os nós, ou seja, adiciona-os à árvore de busca. A busca em profundidade, por exemplo, em cada etapa adiciona à árvore um filho do nó incluído mais recentemente com pelo menos um filho não incluído� Portanto, adiciona o nó inicial, depois 20 seu filho, depois seu neto e assim por diante. Por esse motivo, essa técnica aumenta ao máximo a profundidade da árvore de pesquisa em cada etapa, ou seja, se não houver mais filhos de um nó para adicionar, ele retrocede para o pai do nó� A figura 2 ilustra o caminho feito pela busca em profundidade em uma árvore� Figura 2: árvore de busca em profundidade 1 52 3 4 6 8 9 7 Fonte: Elaborado pelo autor� Observe que o número 1 representa o primeiro nó visitado pelo algoritmo de busca, o número 2 representa o segundo nó representado pelo algo- ritmo de busca e assim por diante� De outra maneira, a busca em largura percorre a árvore camada por camada� Primeiro adiciona todos os filhos do nó inicial, completando assim o nível 1. Em seguida, adiciona todos os filhos de todas as folhas do primeiro nível um por um� Depois, ele 21 adiciona todos os filhos dos netos do nó inicial e assim por diante� Portanto, se o fator de ramificação for constante em todos os níveis, essa técnica tornará a árvore mais ampla a cada etapa� Para fazer isso, a busca em largura adiciona à árvore um filho do nó adicio- nado menos recentemente que possui pelo menos um filho não incluído. A figura 3 ilustra o caminho feito pela busca em largura em uma árvore� Figura 3: árvore de busca em largura 1 2 5 6 3 7 4 8 9 Fonte: Elaborado pelo autor� Ao comparar o caminho percorrido pela figura 2 e pela figura 3, é possível destacar as principais características para cada algoritmo� Na árvore de busca em largura, todos os números de inclusão no nível i são menores do que os nú- meros no nível i + 1� Esse não é o caso busca em profundidade que, se o nó i tiver um número de 22 inclusão menor que o i + 1, todos os descenden- tes de i + 1 terão números menores que i e seus descendentes� Assim, a busca em largura aumenta a árvore nível por nível, enquanto a busca em profundidade au- menta subárvore por subárvore� No pior cenário, a busca em profundidade cria uma árvore de pesquisa cuja profundidade é m, portanto, sua complexidade de tempo é , em que b é o número de nós na árvore e m a profundidade� Como o BFS (Breadth-First Search – Busca em largura) é ótimo, seu resultado de pior caso é encontrar o nó objetivo após adicionar b1 nós no nível 1, b2 no nível 2 e assimpor diante até o nível d, em que adiciona bd nós� No total, a árvore de pior caso da busca em largura contém 1 + b1 + b2 + … + bd nós, o que significa que o tempo para crescer é da ordem de O(bd)� Onde aplicar a força bruta? A força bruta pode ser aplicada a uma ampla varie- dade de problemas, desde problemas de tentativa e erro, pesquisa a um número, realização de algum tipo de classificação nas listas não classificadas de entrada, procura de números inteiros entre alguns intervalos em qualquer condição, até descobrir o maior número em um intervalo, dentre outras opções 23 possíveis� Trata-se de uma técnica extremamente útil na resolução de problemas de pequeno porte� Destaca-se também a popularidade dos algoritmos de força bruta entre os invasores para roubar in- formações confidenciais. Quando um invasor usa um conjunto de possíveis candidatos para atacar algumas informações direcionadas ou dados va- zados, esse fenômeno é chamado de ataque de força bruta� O atacante continua as tentativas até que haja algum sucesso� Se o tamanho da entra- da for maior, mais tempo levará, mas há uma boa probabilidade de atingir a meta� O ataque mais comum é roubar as informações de senha dos usuários� No ataque de dicionário, um invasor usa um dicionário de senhas que contém bilhões de palavras que podem ser usadas como senha por usuários normais, a partir disso, o pro- cesso de ataque executa uma a uma as senhas de um dicionário para autenticar as credenciais dos usuários e se o usuário definir uma senha que esteja neste dicionário, há um risco alto do atacante ter sucesso em sua ação� Na técnica de ordenação de força bruta, a lista de dados é verificada várias vezes para localizar o menor elemento na lista� Após cada iteração sobre a lista, ele substitui o menor elemento no 24 topo da pilha e inicia a próxima iteração a partir do segundo menor dado da lista� Neste exemplo de ordenação, o problema é de ta- manho ‘n’, e a operação básica é o teste ‘if’, cujos itens de dados estão sendo comparados em cada iteração� Não haverá diferença entre o pior e o melhor caso, pois o número de troca é sempre n-1� A principal desvantagem do método de força bruta é que, para muitos problemas do mundo real, o número de candidatos naturais é proibitivamente grande� Por exemplo, se procurarmos os divisores de um número como descrito acima, o número de candidatos testados será o número n dado� PESQUISA HEURÍSTICA Uma heurística no sentido mais comum é um mé- todo que envolve a adaptação da abordagem de um problema com base em soluções anteriores para problemas semelhantes� Essas abordagens visam ser fáceis e rapidamente aplicáveis a uma série de problemas, de modo a encontrar soluções aproximadas rapidamente sem usar o tempo e os recursos para desenvolver e executar uma abordagem precisa� O princípio de uma heurística pode ser aplicado a vários problemas em matemá- tica, ciências e otimização, aplicando heurística computacionalmente� 25 A pesquisa heurística também é conhecida como pesquisa informada e é orientada a objetivos� O objetivo principal da busca heurística é ser rápido e fácil, abrangendo uma variedade de problemas, pois encontra uma resposta aproximada sem exigir ou gastar muito tempo e recursos� A busca heurística pode não fornecer sempre a solução mais precisa ou melhor, mas encontra uma solução adequada em um período racional de tempo, sendo um método de busca muito útil para resolver problemas difíceis� Portanto, é o melhor caminho para problemas que levam um tempo infinito para serem resolvidos. A procura de espaços de solução tem sido fun- damental para o desenvolvimento da inteligência artificial para a resolução de problemas. Desde então, o uso de algoritmos de busca heurística se expandiu consideravelmente para incluir uma variedade de aplicações, como planejamento de rotas, biologia computacional e robótica� Existem vários tipos diferentes de algoritmos de busca heurística que podem ser utilizados depen- dendo da tarefa, a saber: y Hill Climbing: algoritmo aplicado em IA cujo objetivo é o de encontrar a melhor solução dispo- nível, continuando a gerar soluções até encontrar o estado requisitado� A cada passo do processo, 26 o algoritmo irá gerar soluções (nós) e comparar o nó com o objetivo� Se for o estado de meta es- perado, ele conclui o processo� Se não for, ele irá gerar novos nós e repetir até que o estado objetivo seja encontrado ou até atingir o máximo local, ou o estado em que todas as outras opções são piores que o atual� As estratégias do Hill Climbing são frequentemente utilizadas para otimizações matemáticas, como no problema do caixeiro viajante, que tenta encontrar as rotas mais curtas possíveis entre as paradas de vendas� y Simulated Annealing: trata-se de um algoritmo iterativo com uma solução inicial e um problema de otimização� O Simulated Annealing usa funções heurísticas para comparar os estados vizinhos dos dados e decidir entre permanecer no mesmo estado ou mudar para um novo� O algoritmo se moverá para estados mais próximos da solução ótima até atingir um estado que tenha sido observado como bom o suficiente para a aplicação ou até que seu tempo de execução seja atingido� y A*: É um algoritmo de busca que é usado para encontrar o caminho mais curto entre um ponto inicial e um ponto final. Trata-se de um algoritmo útil que é frequentemente usado para percorrer um mapa para encontrar o caminho mais curto a ser seguido� A* foi inicialmente projetado como um problema de travessia de grafos, para ajudar 27 a construir um robô que possa encontrar seu pró- prio curso� O algoritmo A* ainda continua sendo um algorit- mo amplamente popular para travessia de grafos pois procura por caminhos mais curtos primeiro, tornando-o um algoritmo ideal e completo� Outro aspecto que torna o A* tão poderoso é o uso de grafos ponderados em sua implementação, e isso quer dizer que ele usa números para representar o custo de tomar cada caminho ou curso de ação, ou seja, que os algoritmos podem seguir o caminho com o menor custo e encontrar a melhor rota em termos de distância e tempo� Um algoritmo heurístico sacrifica a otimização – precisão e exatidão – por velocidade para resolver problemas com mais rapidez e eficiência. Todos os grafos possuem diferentes nós ou pon- tos que o algoritmo deve tomar para chegar ao nó final. Todos os caminhos entre esses nós têm um valor numérico, que é considerado como o peso do caminho� O total de todos os caminhos transversais lhe dá o custo dessa rota� Inicialmente, o algoritmo calcula o custo para todos os nós vizinhos imediatos (n) e escolhe aquele de menor custo, e esse processo se repete até que nenhum novo nó possa ser escolhido e todos os 28 caminhos tenham sido percorridos� Então, você deve considerar o melhor caminho entre eles� DIFERENÇA ENTRE FORÇA BRUTA E PESQUISA HEURÍSTICA A principal diferença entre Força Bruta e Busca Heurística é que a força bruta é uma forma de busca desinformada, ou seja, pesquisa sem as informações adequadas, além de não ter conhe- cimento sobre o problema� A busca desinformada (ou Pesquisa Cega), como o nome sugere, está pesquisando sem “informa- ções” sobre o nó objetivo� Por exemplo, em busca em largura os nós no mesmo nível são percorri- dos primeiro e então ele se move para o próximo nível e não para até encontrar o nó com valores que estava procurando, logo, não há necessidade de informações para selecionar os nós a serem percorridos� Você também pode pensar em uma estratégia de pesquisa desinformada como uma estratégia de pesquisa de força bruta� Por outro lado, a busca heurística é uma busca in- formada, em outras palavras, pesquisa com dados e informações adequados para obter resultados possíveis, isso quer dizer que gera um caminho em torno da solução e, uma vez que é principalmente orientada a objetivos, não leva muito tempo para 29 executar tarefas– ao contrário da força bruta, que é relativamente demorada� A busca Informada está pesquisando com “in- formações” sobre o nó objetivo� Por exemplo, no algoritmo de busca A*, primeiro, coletamos informações sobre o nó objetivo, como sua locali- zação na forma de coordenadas cartesianas e, em seguida, escrevemos uma função que pode guiar nossa travessia do nó, por exemplo, encontrando sua distância euclidiana� Essa função é chamada de função heurística� A função heurística guiará nossa travessia de nós informando a distância que resta de cada nó até o nó objetivo� Você também pode pensar na Estraté- gia de Pesquisa Informada como uma Estratégia de Pesquisa Guiada� As estratégias de pesquisa informada e desin- formada são o coração e a alma da Inteligência Artificial. Na vida real, as buscas informadas e desinformadas podem ter essa aparência: a busca desinformada ocorre quando você está olhando de um site para outro ou folheando os livros da revista sem realmente precisar encontrar a infor- mação, enquanto a busca informada é de natureza oposta, por exemplo, você está no trabalho ou está estudando alguma coisa e chega um termo que você não entende naquele momento, então você 30 não navegará na internet por alguma informação aleatória, mas especificamente para encontrar o termo específico. Resumidamente, a força bruta é considerada uma forma cega de busca, pois não inclui técnica heurística e, por definição, trata-se de uma técnica geralmente muito ineficiente. A pesquisa heurística por sua vez é baseada em problemas, logo, está técnica geralmente encontra uma solução aproxi- mada para um problema� 31 PROBLEMA DO CAIXEIRO VIAJANTE O Problema do Caixeiro Viajante é o desafio de encontrar a rota mais curta e mais eficiente para uma pessoa tomar, dada uma lista de destinos específicos. Trata-se de um problema algorítmico bem conhecido nas áreas de ciência da computa- ção e pesquisa operacional� Obviamente, há muitas rotas diferentes para esco- lher, mas encontrar a melhor – aquela que exigirá a menor distância ou custo – é o que matemáticos e cientistas da computação passaram décadas tentando resolver� O problema do caixeiro viajante atraiu tanta aten- ção porque é muito fácil de descrever, mas muito difícil de resolver, o que faz com que o problema do caixeiro viajante pertença à classe de proble- mas de otimização combinatória NP-completos� Isso significa que o problema do caixeiro viajante é classificado como NP-difícil porque não possui solução “rápida” e a complexidade de calcular a melhor rota aumentará quando você adicionar mais destinos ao problema� O problema pode ser resolvido analisando cada rota de ida e volta para determinar a mais curta� No entanto, à medida que o número de destinos 32 aumenta, o número correspondente de viagens de ida e volta supera as capacidades até mesmo dos computadores mais rápidos� Com 10 destinos, pode haver mais de 300�000 permutações e combinações de ida e volta� Com 15 destinos, o número de rotas possíveis pode ultrapassar 87 bilhões� Dentre as soluções mais populares, destacam-se: y Abordagem força bruta: que calcula e com- para todas as permutações possíveis de rotas ou caminhos para determinar a solução única mais curta� Para resolver o PCV (Problema do Caixeiro Viajante) usando a abordagem força bruta, você deve calcular o número total de rotas e, em seguida, desenhar e listar todas as rotas possíveis� Calcule a distância de cada rota e, em seguida, escolha a mais curta – esta é a solução ideal; y Método Branch and Bound: esse método divide um problema a ser resolvido em vários subproblemas� É um sistema para resolver uma série de subproblemas, cada um dos quais pode ter várias soluções possíveis e a solução selecio- nada para um problema pode afetar as possíveis soluções de subproblemas subsequentes� Para resolver o problema do caixeiro viajante usando o método Branch and Bound, você deve escolher um nó inicial e, em seguida, definir o limite para um valor muito grande (digamos, infinito). Selecione o arco mais barato entre o nó não visitado e o atual 33 e, em seguida, adicione à distância atual. Repita o processo enquanto a distância atual for menor que o limite� Se a distância atual for menor que o limite, você terminou� Agora você pode somar a distância para que o limite seja igual à distância atual� Repita esse processo até que todos os arcos tenham sido cobertos� y Método do vizinho mais próximo: essa é talvez a heurística do problema do caixeiro viajante mais simples� A chave para esse método é sempre visi- tar o destino mais próximo e depois voltar para a primeira cidade quando todas as outras cidades forem visitadas� Para resolver o problema dessa maneira, escolha uma cidade aleatória e então procure a cidade não visitada mais próxima e vá até lá� Depois de visitar todas as cidades, você deve retornar à primeira cidade. y Inserção mais distante: ao contrário das outras inserções, a Inserção Mais Distante começa com uma cidade e a conecta com a cidade mais distan- te dela� Em seguida, ele encontra repetidamente a cidade que ainda não está no passeio que está mais distante de qualquer cidade no passeio e a coloca entre as duas cidades que fariam com que o passeio resultante fosse o mais curto possível� y Algoritmo Christofides: o Algoritmo de Chris- tofides é uma heurística com garantia de aproxi- mação de � Na pior das hipóteses, o passeio não é maior que da duração do passeio ideal� Devido 34 à sua velocidade e garantia de aproximação de , o Algoritmo Christofides é frequentemente usado para construir um limite superior, como um tour inicial que será otimizado usando heurísticas de melhoria de tour, ou como um limite superior para ajudar a limitar o espaço de busca para ramificações e técnicas de corte utilizadas na busca da rota ideal� Apesar da complexidade de resolver o Problema do Caixeiro Viajante, ele ainda encontra aplicações em diversas áreas. Por exemplo, soluções eficien- tes encontradas por meio desse problema estão sendo usadas em rotas de entregas de mercadoria de um centro de transporte, como um depósito ou armazém, até a escolha de entrega do cliente final. 35 CONSIDERAÇÕES FINAIS Nesse e-book você pôde compreender a teoria da complexidade, que realiza a classificação de problemas com base em quão difíceis eles são de resolver� Você pôde compreender também que um proble- ma é atribuído à classe P (tempo polinomial) se o número de etapas necessárias para resolvê-lo for limitado por alguma potência do tamanho do problema� Por outro lado, um problema é atribuído à classe NP (tempo polinomial não determinístico) se ele permitir uma solução não determinística e o nú- mero de etapas para verificar a solução é limitado por alguma potência do tamanho do problema� A classe de problemas P é um subconjunto da classe de problemas NP, mas também existem problemas que não são NP� Durante a leitura, você observou que se uma solução é conhecida para um problema NP, ela pode ser reduzida a uma única verificação em tempo polino- mial e que um problema é NP-completo se for NP e um algoritmo, para resolvê-lo, pode ser traduzido em um para resolver qualquer outro problema NP� Como exemplos de problemas NP-completos ve- rificamos o problema do caixeiro viajante. 36 Além disso, pôde compreender que um problema é dito NP-hard se um algoritmo, para resolvê-lo, pode ser traduzido de maneira que consiga resolver qualquer outro problema NP� Observe então que é muito mais fácil mostrar que um problema é NP do que mostrar que é NP-hard� Um problema que é NP e NP-hard é chamado de problema NP-completo� Você também conheceu duas técnicas de de- senvolvimento de algoritmos, a heurística (busca informada) e a força bruta (busca não informada), também conhecida como busca cega ou busca uniforme, que é um procedimento mais demorado, entretanto, cabe ressaltar que a estratégia de busca informadaé mais eficiente do que a estratégia de busca não informada quando temos informações sobre o nó objetivo, caso essa informação não esteja presente, a estratégia de busca não infor- mada deve ser aplicada� 37 Referências Bibliográficas & Consultadas BORIN, V� P� Estrutura de dados� Curitiba: Contentus, 2020� [Biblioteca Virtual]� CAMPOS FILHO, F� F� Algoritmos numéricos: uma abordagem moderna de cálculo numérico� 3� ed� Rio de Janeiro: LTC, 2018� [Minha Biblioteca]� CORMEN, T� H� Desmistificando algoritmos� 1� ed� Rio de Janeiro: LTC, 2014� [Minha Biblioteca]� DROZDEK, A� Estrutura de dados e algoritmos em C++� 4� ed� Cengage Learning, 2017� [Minha Biblioteca]� GOLDBARG, M� C�; GOLDBARG, E� G�, LUNA, H� P� L� Otimização combinatória e meta-heurísticas: Algoritmos e Aplicações� 1� ed� Rio de Janeiro: GEN / Elsevier, 2016� [Minha Biblioteca]� PINTO, R� A�; PRESTES, L� P�; SERPA, M� S�; COUTO, J� M� C�; BIANCO, C� M�; NUNES, P� C� M� Estrutura de dados� Porto Alegre: Sagah, 2019� [Minha Biblioteca]� TOSCANI, L� V�, VELOSO, P� A� S� Complexidade de algoritmos� 3� ed� Porto Alegre: Bookman, 2012� [Minha Biblioteca]� WAZLAWICK, R� S� Introdução a algoritmos e programação com Python: uma abordagem dirigida por testes; 1� ed� Rio de Janeiro: Elsevier, 2017� [Minha Biblioteca]� Introdução Classes de Problemas: P e NP Algoritmos NP Algoritmos NP-Completos Algoritmos NP-Hard Podemos dizer que P = NP? Força bruta e heurísticas Algoritmos de força bruta Pesquisa heurística Diferença entre Força Bruta e Pesquisa heurística Problema do caixeiro viajante Considerações finais Referências Bibliográficas & Consultadas