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.