Sumário executivo
Dada entrada k
, encontrar uma partição de inteiros 1
para n
a k
subconjuntos livre de soma para o maior n
possível dentro de 10 minutos.
Contexto: números de Schur
Um conjunto A
é livre de soma se sua soma automática A + A = { x + y | x, y in A}
não tiver elementos em comum com ele.
Para todo número inteiro positivo, k
existe um número inteiro maior, de S(k)
modo que o conjunto {1, 2, ..., S(k)}
pode ser particionado em k
subconjuntos sem soma. Esse número é chamado de k- ésimo número de Schur (OEIS A045652 ).
Por exemplo S(2) = 4
,. Podemos particionar {1, 2, 3, 4}
como {1, 4}, {2, 3}
, e essa é a partição exclusiva em dois subconjuntos sem soma, mas agora não podemos adicionar um 5
a nenhuma das partes.
Desafio
Escreva um programa determinístico que faça o seguinte:
- Tome um número inteiro positivo
k
como entrada - Escreva o timestamp atual do Unix no stdout
- Saídas uma sequência de partições de
1
an
emk
subconjuntos livre de soma para o aumenton
, após cada sequência com o timestamp Unix.
O vencedor será o programa que imprimirá uma partição para a maior n
dentro de 10 minutos no meu computador quando receber uma entrada 5
. Os laços serão quebrados no momento mais rápido para encontrar uma partição para a maior n
média, em três execuções: é por isso que a saída deve incluir registros de data e hora.
Detalhes importantes:
- Eu tenho o Ubuntu Precise, portanto, se seu idioma não for suportado, não poderei pontuá-lo.
- Eu tenho uma CPU Intel Core2 Quad, portanto, se você quiser usar multithreading, não faz sentido usar mais de 4 threads.
- Se você quiser que eu use quaisquer sinalizadores ou implementações específicas do compilador, documente isso claramente em sua resposta.
- Você não deve especializar seu código para manipular a entrada
5
. - Você não é obrigado a produzir todas as melhorias encontradas. Por exemplo, para a entrada
2
você poderia saída apenas a partição paran = 4
. No entanto, se você não produzir nada nos primeiros 10 minutos, pontuarei isso comon = 0
.
fonte
n=59
, e classificando pelo maior número de números permitidos menor que o quenextN
fornecen=64
. A classificação pelo comprimento da lista de números não permitidos (que pode ter repetições) leva muito rapidamente a umn=30
padrão elegante .Tue Nov 10 00:44:25 2015
), mas vin=92
em menos de 2 segundos.ctime
maistime
porque a saída era mais bonita quandotime
foi exatamente o que eu deveria ter escolhido.n=121
. oOPython 3, 121, <0,001s
Graças à heurística aprimorada, Martin Buttner significa que nem precisamos de aleatoriedade.
Resultado:
Código:
Python 3, 112
Classificar pela soma dos 2 primeiros elementos + inclinação
Copiei a estrutura de dados de El'endia Starman, que consiste em uma lista de pares, em que o primeiro elemento do par são os elementos desse balde e o segundo é a soma desse balde.
Começo com a mesma abordagem "rastrear quais somas estão disponíveis". Minha heurística de classificação é simplesmente a soma dos dois menores elementos de uma determinada lista. Também adiciono uma pequena inclinação aleatória para tentar diferentes possibilidades.
Cada iteração simplesmente coloca o novo número na primeira posição disponível, semelhante à ganância aleatória. Quando isso falha, simplesmente reinicia.
fonte
Java 8, n =
142144Última saída:
Executa uma pesquisa aleatória distribuída em 4 threads. Quando não consegue encontrar uma partição para caber
n
, tenta liberar espaço paran
uma partição de cada vez, despejando o máximo possível nas outras partições.edit: ajustou o algoritmo para liberar espaço para
n
, também adicionou a capacidade de voltar para uma opção anterior e escolher novamente.nota: a saída não é estritamente determinística porque há vários threads envolvidos e eles podem acabar atualizando os melhores
n
encontrados até agora em ordem confusa; a pontuação final de 144 é determinística e é alcançada rapidamente: 30 segundos no meu computador.O código é:
fonte
C, Ganancioso Aleatório, n = 91
Apenas para fornecer uma solução de linha de base, ele repete
n
, acompanhando os compartimentos e suas somas e adicionan
a uma lixeira aleatória onde ainda não aparece como uma soma. Ele termina uma vez quen
aparece em todas ask
somas e se o resultado resultanten
foi melhor do que qualquer tentativa anterior, imprime-o em STDOUT.A entrada
k
é fornecida através de um argumento de linha de comando. O máximo possívelk
é codificado para 10, porque eu estava com preguiça de adicionar alocação dinâmica de memória, mas isso poderia ser corrigido facilmente.Acho que eu poderia procurar uma semente melhor agora, mas essa resposta provavelmente não é particularmente competitiva, então meh.
Aqui está a partição para
n = 91
:E finalmente, aqui está o código:
fonte
n=91
, encontrado em 138 segundos. Se necessário para desempate, eu voltarei a trabalhar para evitar grandes erros devido à carga da CPU diferente.C ++, 135
Anexa o próximo n a um subconjunto escolhido aleatoriamente. Se isso não for possível, ele remove números aleatórios de subconjuntos e os anexa a outros na esperança de que isso permita acrescentar n em algum lugar.
Eu prototipei isso no awk e, como parecia promissor, eu o traduzi para C ++ para acelerar. Usando um
std::set
deve até acelerar mais.Saída para n = 135 (após cerca de 230 segundos na minha máquina [antiga])
Não verifiquei novamente a validade, mas deve estar tudo bem.
fonte
Python 3, ganancioso aleatório, n = 61
Última saída:
Utiliza efetivamente o mesmo algoritmo que Martin Büttner , mas eu o desenvolvi de forma independente.
Existem
k
bandejas que possuem os números até agora e os números que não podem mais ser inseridos. A cada profundidade da iteração (é basicamente uma pesquisa de profundidade), a ordemnextN
das posições é embaralhada e o próximo número ( ) é (sequencialmente) colocado nas caixas que podem levá-la e, em seguida, é um passo mais profundo. Se não houver, ele retornará, fazendo backup de uma etapa.fonte
Python, n = 31
Ok, então, obviamente, não é um vencedor, mas eu senti que pertencia aqui de qualquer maneira. Tomei a liberdade de não incluir registros de data e hora, pois termina instantaneamente e não é um candidato real.
Primeiro, observe que a soma de quaisquer dois números ímpares é par, para que possamos despejar todos os números ímpares no primeiro bloco. Então, como todos os números restantes são pares, podemos dividi-los por 2. Mais uma vez, podemos lançar todos os números ímpares resultantes no segundo bloco (depois de re-multiplicá-los por 2), divida os números restantes por 2 (ou seja, , por 4 no total), jogue os ímpares no terceiro bloco (depois de multiplicá-los por 4) e assim por diante ... Ou, para colocar as palavras que vocês entendem, colocamos todos os números cujo conjunto menos significativo bit é o primeiro bit no primeiro bloco, todos os números cujo bit de conjunto menos significativo é o segundo bit no segundo bloco e assim por diante ...
Para os blocos k , enfrentamos problemas quando atingimos n = 2 k , já que o bit conjunto menos significativo de n é
o ( k + 1) -ésimo bit, que não corresponde a nenhum bloco. Em outras palavras, este esquema funciona até
para n = 2 k - 1. Assim, enquanto que para k = 5 nós só temos um magro n = 31 , este número cresce exponencialmente com k . Também estabelece que S ( k ) ≥ 2 k - 1 (mas podemos encontrar um limite inferior melhor do que esse facilmente).
Para referência, aqui está o resultado para k = 5:
fonte
[1], [2,3], [4,5,6,7], ...
, o que provavelmente é mais simples, apenas com ordem inversa de bits e bloco. É fácil ver como este pode ser estendido.