História
Minha empresa envia um boletim semanal a todos dentro da empresa. Incluído nesses boletins há um enigma, juntamente com uma mensagem para quem foi o primeiro a enviar por e-mail / fornecer uma solução para o enigma da semana passada. A maioria desses enigmas é bastante trivial e honestamente bastante chata para uma empresa de tecnologia, mas houve um, vários meses atrás, que chamou minha atenção.
O enigma original:
Dada a forma abaixo:
Você tem os números naturais de 1 a 16. Coloque-os todos nessa forma, de modo que todas as linhas e colunas contíguas totalizem 29.
Por exemplo, uma dessas soluções para esse quebra-cabeça (que era a solução "canônica" que enviei para o boletim) foi a seguinte:
No entanto, no curso da solução, encontrei algumas informações bastante interessantes:
- Existem significativamente mais soluções do que apenas essa; de fato, existem 9.368 soluções.
- Se você expandir o conjunto de regras para exigir apenas que linhas e colunas sejam iguais umas às outras, não necessariamente 29, você obtém 33.608 soluções:
- 4.440 Soluções para uma soma de 27.
- 7.400 Soluções, num total de 28.
- 9.368 Soluções para uma soma de 29.
- 6.096 Soluções para uma soma de 30.
- 5.104 Soluções para uma soma de 31.
- 1.200 soluções, totalizando 32.
Então, eu e meus colegas de trabalho (embora principalmente apenas meu gerente, já que ele era a única outra pessoa além de mim com habilidades de programação para "Propósitos Gerais") partimos para um desafio que durou a maior parte do mês - tivemos outros empregos reais. obrigações relacionadas que tínhamos que cumprir - para tentar escrever um programa que encontrasse todas as soluções da maneira mais rápida possível.
Estatísticas originais
O primeiro programa que escrevi para resolver o problema simplesmente checava soluções aleatórias repetidas vezes e parava quando encontrava uma solução. Se você fez uma análise matemática sobre esse problema, provavelmente já sabe que isso não deveria ter funcionado; mas, de alguma forma, tive sorte e o programa levou apenas um minuto para encontrar uma única solução (a que postei acima). As execuções repetidas do programa costumavam levar 10 ou 20 minutos, portanto, obviamente, essa não era uma solução rigorosa para o problema.
Mudei para uma solução recursiva que iterava em todas as permutações possíveis do quebra-cabeça e descartava muitas soluções ao mesmo tempo, eliminando somas que não estavam sendo adicionadas. Ou seja, se a primeira linha / coluna que eu estava comparando já não fosse igual, eu poderia parar de verificar esse ramo imediatamente, sabendo que nada mais permeado no quebra-cabeça mudaria isso.
Usando esse algoritmo, obtive o primeiro sucesso "adequado": o programa poderia gerar e cuspir todas as 33.608 soluções em cerca de 5 minutos.
Meu gerente teve uma abordagem diferente: sabendo, com base no meu trabalho, que as únicas soluções possíveis tinham somas de 27, 28, 29, 30, 31 ou 32, ele escreveu uma solução multiencadeada que verificava somas possíveis apenas para esses valores específicos. Ele conseguiu que seu programa fosse executado em apenas 2 minutos. Então eu iterou novamente; Misturei todas as somas possíveis de 3/4 dígitos (no início do programa; é contado no tempo de execução total) e usei a "soma parcial" de uma linha para pesquisar o valor restante com base em uma linha concluída anteriormente, em vez de testando todos os valores restantes e diminuindo o tempo para 72 segundos. Então, com alguma lógica de multiencadeamento, reduzi para 40 segundos. Meu gerente levou o programa para casa, realizou algumas otimizações na execução do programa e reduziu o tempo para 12 segundos. Reordenei a avaliação das linhas e colunas,
O mais rápido que qualquer um de nós conseguiu nossos programas após um mês foi de 0,15 segundo para meu gerente e 0,33 segundos para mim. Acabei afirmando que meu programa era mais rápido, pois o programa do meu gerente, embora encontrasse todas as soluções, não as imprimia em um arquivo de texto. Se ele adicionou essa lógica ao seu código, muitas vezes levava mais de 0,4-0,5 segundos.
Desde então, permitimos que nosso desafio intra-pessoal subsistisse, mas, é claro, a questão permanece: esse programa pode ser feito mais rápido?
Esse é o desafio que vou fazer para vocês.
O seu desafio
Os parâmetros sob os quais trabalhamos relaxaram a regra da "soma de 29" para serem "todas as somas de linhas / colunas iguais", e vou definir essa regra para vocês também. O desafio, portanto, é: Escreva um programa que encontre (e imprima!) Todas as soluções para esse enigma no menor tempo possível. Vou estabelecer um limite para as soluções enviadas: se o programa demorar mais de 10 segundos em um computador relativamente decente (<8 anos), provavelmente será muito lento para ser contado.
Além disso, tenho alguns bônus para o quebra-cabeça:
- Você pode generalizar a solução para que funcione para qualquer conjunto de 16 números, não apenas
int[1,16]
? A pontuação de tempo seria avaliada com base no conjunto de números de prompt original, mas passaria por esse caminho de código. (-10%) - Você pode escrever o código de uma maneira que ele manipule e resolva normalmente com números duplicados? Isso não é tão simples quanto pode parecer! Soluções "visualmente idênticas" devem ser exclusivas no conjunto de resultados. (-5%)
- Você consegue lidar com números negativos? (-5%)
Você também pode tentar gerar uma solução que lide com números de ponto flutuante, mas é claro, não fique chocado se isso falhar completamente. Se você encontrar uma solução robusta, isso pode valer um grande bônus!
Para todos os efeitos, "Rotações" são consideradas soluções únicas. Portanto, uma solução que é apenas uma rotação de uma solução diferente conta como sua própria solução.
Os IDEs que tenho trabalhando no meu computador são Java e C ++. Posso aceitar respostas de outros idiomas, mas talvez seja necessário fornecer um link para onde eu possa obter um ambiente de tempo de execução fácil de configurar para o seu código.
fonte
Respostas:
C - perto de 0,5 s
Este programa muito ingênuo fornece todas as soluções em meio segundo no meu laptop de 4 anos. Sem multithread, sem hash.
Windows 10, Visual Studio 2010, núcleo da CPU I7 de 64 bits
Experimente online no ideone
fonte
int inuse[16];
por justint inuse;
e depois usar operadores bit a bit para manipulá-lo. Ele não parece aumentar a velocidade que muito, mas ajuda um pouco.dumbbench --precision=.01 -vvv --initial=500 ./solve
C ++ - 300 milissegundos
Por solicitação, adicionei meu próprio código para resolver esse quebra-cabeça. No meu computador, ele funciona a uma média de 0,310 segundos (310 milissegundos), mas dependendo da variação pode ser executado tão rapidamente quanto 287 milissegundos. Raramente vejo isso subir acima de 350 milissegundos, geralmente apenas se meu sistema estiver atolado com uma tarefa diferente.
Esses horários são baseados no auto-relatório usado no programa, mas eu também testei usando um timer externo e obtive resultados semelhantes. A sobrecarga no programa parece adicionar cerca de 10 milissegundos.
Além disso, o meu código não completamente lidar com duplicatas corretamente. Ele pode resolver usá-los, mas não elimina soluções "visualmente idênticas" do conjunto de soluções.
fonte
0.1038s +/- 0.0002
E aqui está a hora do seu código com saída simplificada:0.0850s +/- 0.0001
Então, você pode economizar ~ 18ms, pelo menos na minha máquina. Corri as duas versões mais de 500 vezes com valores atípicos jogados fora, usando dumbbenchProlog - 3 minutos
Esse tipo de quebra-cabeça parece ser um caso de uso perfeito para o Prolog. Então, eu codifiquei uma solução no Prolog! Aqui está:
Infelizmente, não é tão rápido quanto eu esperava. Talvez alguém mais versado em programação declarativa (ou Prolog especificamente) possa oferecer algumas dicas de otimização. Você pode invocar a regra
puzzle
com o seguinte comando:Experimente online aqui . Você pode substituir qualquer número no lugar de
29
s no código para gerar todas as soluções. Tal como está, todas as 29 soluções são encontradas em aproximadamente 30 segundos; portanto, para encontrar todas as soluções possíveis, deve ser de aproximadamente 3 minutos.fonte