Considere um gráfico de grade quadrada n por n que se parece com isso.
É importante notar que este gráfico é 11 por 11 .
A qualquer momento, um homem fica em um cruzamento e ele só se move vertical ou horizontalmente um passo de cada vez para o próximo cruzamento. Infelizmente, ele bebeu demais, por isso escolhe a direção em que se move aleatoriamente entre as 4 direções possíveis (para cima, para baixo, para a esquerda, para a direita). Isso é até 4, como se ele estivesse em uma parede, ele tem apenas 3 opções, é claro, e em um canto ele tem apenas 2.
Ele começa no canto inferior esquerdo e seu objetivo é chegar em casa, que é o canto superior direito. O tempo é simplesmente o número de etapas que ele leva.
No entanto, você é um adversário malicioso que deseja que ele chegue em casa o mais lentamente possível. Você pode excluir qualquer número de arestas do gráfico a qualquer momento durante sua caminhada. A única restrição é que você deve sempre deixar um caminho para ele chegar em casa e não pode excluir uma vantagem que ele já usou.
O desafio é criar um adversário o mais malicioso possível e testá-lo em um gráfico de
100 por 10020 por 20 com um andador bêbado aleatório. Sua pontuação é simplesmente o tempo médio que o caminhante leva para chegar em casa com mais de10mil corridas.
Você pode usar qualquer idioma e bibliotecas que desejar, desde que estejam disponíveis gratuitamente e sejam facilmente instaláveis no Linux.
O que eu preciso implementar?
Você deve implementar o código para o caminhante aleatório e também para o adversário, e o código deve ser combinado para que a saída durante a execução seja simplesmente a média de 1.000 execuções usando o código do adversário. O código do caminhante aleatório deve ser muito simples de escrever, pois ele escolhe entre (x-1, y), (x + 1, y), (x, y-1) e (x, y + 1), certificando-se de que nenhum deles foi excluído ou está fora do alcance.
O código do adversário é obviamente mais difícil e também precisa lembrar quais bordas o bêbado já atravessou, para que ele não tente excluir nenhum deles e para garantir que ainda haja uma rota de volta para o bêbado, o que é um pouco mais complicado. fazer rapidamente.
O adendo 10 corridas não é suficiente, mas eu não queria punir as pessoas que conseguiam fazer longas caminhadas. Agora aumentei para 1000 devido a pedidos populares. No entanto, se a sua caminhada for longa, você não poderá fazer 1000 corridas em um período realista, apenas informe o número máximo de corridas que puder.
Tabela de pontuações máximas de 100 por 100.
- 976124.754 da Optimizer.
- 103000363.218 de Peter Taylor.
Editar 1. Alterou o tamanho do gráfico para 20 por 20 para ajudar no tempo de execução dos testes das pessoas. Farei uma nova pontuação alta para esse tamanho à medida que as pessoas enviarem a pontuação.
Tabela de pontuações máximas de 20 por 20.
230,794.38 (100k runs) by justhalf
227,934 by Sparr
213,000 (approx) by Peter Taylor
199,094.3 by stokastic
188,000 (approx) by James_pic
64,281 by Geobits
Respostas:
230.794,38 em 20x20, 100k execuções
Última atualização: Eu finalmente criei uma solução dinâmica de 2 caminhos perfeita. Eu disse perfeito, já que a versão anterior não é simétrica, era mais fácil obter um caminho mais longo se o bêbado seguisse um caminho sobre o outro. O atual é simétrico, para que possa obter o número esperado de etapas mais alto. Depois de alguns testes, parece estar em torno de 230k, uma melhoria em relação ao anterior, que é de cerca de 228k. Mas estatisticamente falando, esses números ainda estão dentro de seu enorme desvio, então não afirmo que isso seja significativamente melhor, mas acredito que isso deveria ser melhor que a versão anterior.
O código está na parte inferior desta postagem. Ele é atualizado para ser muito mais rápido que a versão anterior, completando 1000 execuções em 23s.
Abaixo está o exemplo de execução e o labirinto de amostras:
Submissões anteriores
Finalmente eu posso igualar o resultado de Sparr! = D
Com base em meus experimentos anteriores (consulte a parte inferior deste post), a melhor estratégia é ter um caminho duplo e fechar um quando o bêbado atingir qualquer um deles, e a variável deriva de quão bom podemos prever dinamicamente para onde o bêbado irá. aumentar a chance de ele entrar no caminho mais longo.
Então, com base na minha
DOUBLE_PATH
estratégia, construí outra, que muda o labirinto (meuDOUBLE_PATH
labirinto era facilmente modificável) dependendo do movimento dos bêbados. Como ele segue um caminho com mais de uma opção disponível, fecharei os caminhos para deixar apenas duas opções possíveis (uma de onde ele veio, outra a não-viajada).Isso soa semelhante ao que Sparr alcançou, como mostra o resultado. A diferença com a dele é pequena demais para ser considerada melhor, mas eu diria que minha abordagem é mais dinâmica que ele, já que meu labirinto é mais modificável que o de Sparr =)
O resultado com um exemplo de labirinto final:
Seção Experimentos
O melhor acaba sendo a mesma estratégia que o stokastic, tenho orgulho de experimentar usando várias estratégias e imprimir bons resultados :)
Cada labirinto impresso abaixo é o último labirinto após o bêbado chegar em casa, então eles podem ser um pouco diferentes de corrida para corrida devido à aleatoriedade no movimento dos bêbados e à dinamicidade do adversário.
Vou descrever cada estratégia:
Caminho único
Essa é a abordagem mais simples, que criará um único caminho da entrada para a saída.
Ilha (nível 0)
Essa é uma abordagem que tenta prender o bêbado em uma ilha quase isolada. Não funciona tão bem quanto eu esperava, mas essa é uma das minhas primeiras idéias, então eu a incluo.
Existem dois caminhos que levam à saída e, quando o bêbado se aproxima de um deles, o adversário a fecha, forçando-o a encontrar a outra saída (e possivelmente fica preso novamente na ilha)
Caminho Duplo
Essa é a estratégia mais discutida, que é ter dois caminhos iguais para a saída e fechar um deles quando o bêbado se aproximar de um deles.
Ilha (nível 1)
Inspirados nos vários caminhos da ilha e na alta contagem de passeios em um único caminho, conectamos a ilha à saída e fazemos um labirinto de caminho único na ilha, criando no total três caminhos para sair e, semelhante ao caso anterior, feche qualquer um dos saia quando o bêbado se aproxima.
Isso funciona um pouco melhor que o caminho único puro, mas ainda não derrota o caminho duplo.
Ilha (nível 2)
Tentando expandir a idéia anterior, criei uma ilha aninhada, criando no total cinco caminhos, mas isso não parece funcionar tão bem.
Ilha (nível 3)
Percebendo que o caminho duplo realmente funciona melhor que o caminho único, vamos fazer a ilha em caminho duplo!
O resultado é uma melhoria em relação à ilha (nível 1), mas ainda não supera o caminho duplo puro.
Para comparação, o resultado para o caminho duplo do tamanho da ilha é de 131.134,42 movimentos em média. Portanto, isso adiciona um número bastante significativo de movimentos (cerca de 40k), mas não o suficiente para superar o caminho duplo.
Ilha (nível 4)
Novamente, experimentando com ilha aninhada, e novamente não funciona tão bem.
Conclusão
Em suma, isso prova que ter um único caminho longo da posição atual do bêbado para a saída funciona melhor, o que é alcançado pela estratégia de caminho duplo, uma vez que após fechar uma saída, o bêbado terá que percorrer a distância máxima possível para chegar a a saída.
Isso sugere ainda que a estratégia básica ainda deve ser um caminho duplo, e só podemos modificar a dinâmica da criação dos caminhos, o que foi feito pelo Sparr. Então, acredito que a estratégia dele é o caminho a seguir!
Código
fonte
227934 (20x20)
Minha terceira tentativa. Usa a mesma abordagem geral do @stokastic com dois caminhos para a saída. Quando o caminhante chega ao fim de um caminho, ele se fecha, exigindo que ele retorne para chegar ao final do outro caminho para sair. Minha melhoria é gerar os caminhos à medida que o caminhante progride, para que qualquer caminho que ele esteja avançando na primeira metade do processo acabe sendo mais longo que o outro caminho.
saída (com tempo):
exemplo do meu labirinto, com metade do comprimento aproximadamente igual ao caminho, mostrando o caminho esquerdo / inferior cortado da saída (canto inferior direito):
PS: Estou ciente de uma melhoria muito pequena nesse algoritmo que exige um código mais inteligente para gerar uma forma diferente para os dois caminhos, escadas em vez de zig zags de altura consistentes.
fonte
135.488.307,9 para 98x98
199094.3 para 20x20
Eu implementei uma solução que cria dois caminhos para o acabamento e fecha exatamente um deles quando o bêbado o alcança. Isso simula um comprimento de caminho que, no mínimo, será 1,5x o comprimento de um único caminho do início ao fim. Após 27 corridas, atingi uma média de cerca de 135 milhões. Infelizmente, leva vários minutos por caminhada, então terei de executá-lo pelas próximas horas. Uma ressalva - meu gerador de caminho duplo funciona apenas se o tamanho do gráfico estiver no formato 4 * n + 2, significando que o mais próximo que posso chegar de 100 é 102 ou 98. Vou postar resultados usando 98, o que espero para superar o método do caminho em zigue-zague. Mais tarde, trabalharei em um sistema de caminhos melhor. Atualmente, gera resultados na forma de (numSteps, currentAverage) após cada caminhada.
EDIT: fixo, então o código agora funciona em tamanhos de gráfico com múltiplos múltiplos de 2, em vez de 4 * n + 2.
Código: (adicione o argumento 'True' ao construtor walker na linha 187 para desenhar o gráfico da tartaruga).
Dados brutos: (numSteps atuais, média de execução)
fonte
Abordagem de 4 caminhos, 213k
A abordagem de caminho único é
e obtém uma média de
N^2
.A abordagem de dois caminhos é
mas, na primeira vez em que o bêbado chega ao ponto final, é cortado:
Ele marca uma média de
(N/2)^2 + N^2
.A abordagem de quatro caminhos usa dois cortes:
Suponha que o loop externo seja longo
xN
e o loop interno de comprimento(1-x)N
. Para simplificar, vou normalizar paraN=1
.Do início ao primeiro corte, obtém uma média de
(x/2)^2
. Do primeiro corte ao segundo corte tem duas opções, de comprimentosx
e1-x
; isso dá uma média de(1-x)x^2 + x(1-x)^2 = x-x^2
. Finalmente, o caminho restante dá1
. Então a pontuação total éN^2 (1 + x - 3/4 x^2)
.Inicialmente, assumi que manter os caminhos disponíveis de comprimento igual em cada etapa seria o ideal; portanto, minha abordagem inicial usava
x = 1/2
uma pontuação de1.3125 N^2
. Mas depois de fazer a análise acima, verifica-se que a divisão ideal é dada quandox = 2/3
com pontuação1.3333 N^2
.com código
fonte
f
parac
no seu código é de cerca deN/2
, seja atravése
(ed
) ou não, certo?N^2
, não2^N
. E sim, tornar essa dinâmica o tornará o melhor, o desafio é como torná-lo dinâmico, mantendo a propriedade de quatro caminhos. @PeterTaylor: Imagens agradáveis!Eu experimentei cortar a grade quase inteiramente em todas as
k
linhas. Isso efetivamente converte em algo semelhante a um passeio aleatório em umk
porN * N/k
grade. A opção mais eficaz é cortar cada linha para forçar o bêbado em zigue-zague.Para o caso 20x20 (
SIZE=19
) eu tenhocom código
fonte
Para quem não quer reinventar a roda
Não se preocupe! Vou reinventá-lo para você :)
A propósito, isso é em Java.
Criei uma classe Walker que lida com caminhadas aleatoriamente. Também inclui um método útil para determinar se uma movimentação é válida (se já foi analisada).
Estou assumindo que todos vocês, pessoas inteligentes, podem descobrir números aleatórios para o construtor, deixei isso para você, para que você pudesse testar certos casos. Além disso, basta chamar a função walk () para (você adivinhou!) Fazer o bêbado andar (aleatoriamente).
Implementarei a função canComeHome () em outro momento. De preferência depois de procurar a melhor maneira de fazer isso.
fonte
previouslyTraveled.add(new int[]{x,y,move[0],move[1]})
deve serx+move[0]
ey+move[1]
. OWidth-1
eHeight-1
e ineficiência em verificar os caminhos excluídos. Eu editei seu código (com função adicional para imprimir o labirinto). Sinta-se à vontade para reverter se você acha que isso é inapropriado.Edge
não implementa corretamenteComparable<Edge>
. Se você deseja que as arestas sejam comparadas como iguais, mesmo se você as reverter, precisará levar em consideração a reversão também no caso não igual. A maneira mais fácil de fazer isso seria alterar o construtor para manter os pontos ordenados.Comparable
?A
e tiverB
a mesma aresta invertida eC
for diferente, você poderá obterA.compareTo(B) == B.compareTo(A) == 0
masA.compareTo(C) < 0
eB.compareTo(C) > 0
.canComeHome()
)64.281
Atualizar desde que a grade foi alterada de 100x100 para 20x20 (1000 testes). A pontuação em 100x100 (100 testes) foi de aproximadamente 36M.
Enquanto isso não vai bater uma caminhada de 1D, eu queria brincar com uma ideia que tive.
A idéia básica é que a grade seja dividida em salas quadradas, com apenas um caminho que leva de volta para casa. O caminho aberto é o que o bêbado chegar perto da última , o que significa que ele tem que explorar todas as saídas possíveis, apenas para ter todos, exceto um deles, na cara dele.
Depois de brincar com o tamanho das salas, cheguei à mesma conclusão que Peter, cortando-o menor é melhor. As melhores pontuações vêm com um tamanho de sala 2.
O código é desleixado, não se importe com a bagunça. Você pode
SHOW
ativar o interruptor e ele mostrará uma imagem dos caminhos a cadaSHOW_INT
passo, para que você possa assisti-lo em ação. Uma execução concluída se parece com:(Esta é a imagem da grade 100x100 anterior. 20x20 é assim, mas bem menor. O código abaixo foi atualizado para novos tamanhos / execuções.)
fonte
188k, com 2 caminhos
Todas as melhores entradas parecem adotar a abordagem de gerar 2 caminhos e depois cortar um quando o bêbado se aproxima do fim do caminho. Acho que não consigo superar a entrada de justhalf, mas não pude deixar de me perguntar: por que 2 caminhos? Por que não 3, 5 ou 20?
TL; DR : 2 caminhos parecem ideais
Então eu fiz um experimento. Com base na estrutura do Stretch Maniac, escrevi uma entrada para testar vários números de caminhos. Você pode ajustar o
featureSize
parâmetro para variar o número de caminhos. AfeatureSize
de 20 fornece 1 caminho, 10 fornece 2 caminhos, 7 fornece 3, 5 fornece 4 e assim por diante.Existem algumas otimizações que eu poderia fazer, mas não o fiz, e ele não suporta nenhum dos truques adaptativos usados apenas pela metade.
De qualquer forma, aqui estão os resultados para vários
featureSize
valores:E aqui está um mapa com três caminhos:
fonte
N
seja o comprimento do caminho (que én^2-1
), o caminho único exigirá, em médiaN^2
, movimentos, enquanto os três caminhos(N/3)^2 + (2N/3)^2 + (2N/3)^2 = N^2
mais um valor relativamente pequeno, portanto, três caminhos não tem ganho significativo sobre caminho único, muito menos caminho duplo. (O cálculo é baseado no resultado da probabilidade, que afirma que o movimento aleatório no caminho 1-D de comprimentoN
requer, em média, oN^2
movimento de um extremo ao outro.)131k (20x20)
Minha primeira tentativa foi remover todas as arestas horizontais, exceto as linhas superior e inferior, e cada vez que o caminhante chegava ao fundo de uma coluna eu removia a aresta à sua frente, até que ele visitava o fundo de cada coluna e finalmente conseguir alcançar a saída. Isso resultou em uma média de 1/8 do número de etapas da abordagem de 1d caminhada de @ PeterTaylor.
Em seguida, decidi tentar algo um pouco mais tortuoso. Dividi o labirinto em uma série de divisas ocas aninhadas e exijo que ele atravesse o perímetro de cada divisa pelo menos 1,5 vezes. Isso tem um tempo médio de aproximadamente 131k etapas.
fonte
Fazer nada
Como o homem se move aleatoriamente, pode-se pensar que remover qualquer nó só aumentará suas chances de chegar em casa a longo prazo.
Primeiro, vamos dar uma olhada no caso unidimensional, isso pode ser conseguido removendo nós até que você termine com um caminho irregular, sem prazos ou ciclos, que visite (quase) todos os pontos da grade. Em uma
N x N
grade, o comprimento máximo desse caminho éL = N*N - 2 + N%2
(98 para uma grade de 10x10). Andar ao longo do caminho pode ser descrito por uma matriz de transição, conforme gerado porT1d
.(A leve assimetria torna difícil encontrar uma solução analítica, exceto para matrizes muito pequenas ou infinitas, mas obtemos uma solução numérica mais rápida do que seria necessária para diagonalizar a matriz).
O vetor de estado possui um único
1
na posição inicial e, após asK
etapas,(T1d**K) * state
fornece a distribuição de probabilidade de estar a uma certa distância desde o início (o que equivale à média de todas as2**K
possíveis caminhadas ao longo do caminho!)Executar a simulação de
10*L**2
etapas e salvar o último elemento do vetor de estado após cada etapa, o que nos dá a probabilidade de chegar à meta após um certo número total de etapas - a distribuição cumulativa de probabilidadescd(t)
. A diferenciação nos dá a probabilidadep
de atingir a meta exatamente em um determinado momento. Para encontrar o tempo médio que integramost*p(t) dt
O tempo médio para atingir a meta é proporcional a
L**2
um fator que vai muito rapidamente para 1. O desvio padrão é quase constante em cerca de 79% do tempo médio.Este gráfico mostra o tempo médio para atingir a meta para diferentes comprimentos de caminho (correspondendo a tamanhos de grade de 5x5 a 15x15)
Aqui está como é a probabilidade de atingir a meta. A segunda curva parece preenchida porque, a cada passo ímpar, a posição é ímpar e, portanto, não pode estar na meta.
A partir disso, podemos ver que a estratégia de caminho duplo equilibrada funciona melhor aqui. Para grades maiores, onde a sobrecarga de criar mais caminhos é insignificante em comparação com seu tamanho, é melhor aumentar o número de caminhos, semelhante à maneira como Peter Taylor o descreveu, mas mantendo os comprimentos equilibrados
E se não removermos nenhum nó?
Então teríamos o dobro de nós passáveis, mais quatro direções possíveis em vez de duas. Parece que dificilmente chegará a lugar algum. No entanto, as simulações mostram o contrário. Após apenas 100 etapas em uma grade de 10x10, é provável que o homem alcance seu objetivo, portanto, prendê-lo em ilhas é uma tentativa fútil, já que você está negociando um
N**2
caminho potencial longo e sinuoso com um tempo médio de conclusão deN**4
para uma ilha que é passada emN**2
etapasfonte