O period
de uma string é o menor deslocamento diferente de zero, para que a string corresponda a si mesma, ignorando quaisquer partes que excedam. Então, por exemplo, abcabcab
tem período 3
. Por convenção, dizemos que, se não houver essa mudança, uma sequência terá um período igual ao seu comprimento. Portanto, o período de abcde
é 5
e o período de a
é 1
.
Em termos mais formais, o período de uma string S
é o mínimo i > 0
para que S[1,n-i] == S[i+1,n]
(indexando de 1
).
Para uma determinada sequência S de potência de dois comprimentos, calcularemos o período de todos os seus prefixos de potência de dois comprimentos. Por exemplo, considere S = abcabcab
. Os períodos que calcularemos são:
'a', 1
'ab', 2
'abca', 3
'abcabcab', 3
Na verdade, apenas produziremos a matriz de períodos, ou seja [1, 2, 3, 3]
.
Para um determinado poder positivo de dois n
, considere todas as seqüências binárias possíveis S
. Lembre-se de que uma cadeia binária é simplesmente uma cadeia de se 1
es, 0
para que exista exatamente 2^n
essas cadeias (ou seja, 2
a potência n
). Para cada um, podemos calcular esse conjunto de períodos.
O desafio é escrever um código que tome
n
(um poder de dois) como entrada e calcule quantas matrizes distintas existem.
As respostas para n = 1, 2, 4, 8, 16, 32, 64, 128
são:
1, 2, 6, 32, 320, 6025, 216854, 15128807
O conjunto completo de matrizes de período distintas para n = 4
é:
1, 1, 1
1, 1, 3
1, 1, 4
1, 2, 2
1, 2, 3
1, 2, 4
Ponto
Vou executar o seu código no meu computador executando o Ubuntu por 10 minutos. Sua pontuação é a maior n
para a qual seu código termina nesse período. Em caso de empate, a resposta que completa as maiores n
vitórias mais rápidas conjuntas . No caso de empate em 1 segundo nos horários, a primeira resposta postada vence.
Línguas e bibliotecas
Você pode usar qualquer idioma e bibliotecas disponíveis que desejar. Por favor, inclua uma explicação completa de como executar / compilar seu código no Linux, se possível.
Seu código deve realmente calcular as respostas e não, por exemplo, apenas gerar valores pré-computados.
Entradas principais
- 2 minutos e 21 segundos para n = 128 em C # por Peter Taylor
- 9 segundos para n = 32 em Rust por isaacg
n
, você o aceitaria? Não está bem definido onde está a fronteira entre a codificação e a computação real.abcab
. Todas, exceto as últimas 3 letras, sãoabcab
. Eles correspondem e remover um número menor de letras não corresponde.Respostas:
C #, n = 128 em cerca de 2:40
Estender para n = 256 exigiria a troca
BigInteger
para as máscaras, o que provavelmente mata demais o desempenho para que n = 128 passe sem novas idéias, muito menos n = 256.No Linux, compile
mono-csc
e execute commono
.Explicação básica
Não vou fazer uma dissecção linha a linha, apenas uma visão geral dos conceitos.
Como regra geral, fico feliz em repetir a ordem de 2 50 elementos em um programa combinatório de força bruta. Para chegar a n = 128, é necessário, portanto, usar uma abordagem que não analise cada cadeia de bits. Então, em vez de avançar de seqüências de bits para seqüências de período, eu trabalho para trás: dada uma sequência de período, existe uma cadeia de bits que a realiza? Para n = 2 x, existe um limite superior fácil de 2 x (x + 1) / 2 seqüências de período (vs 2 2 x bitstrings).
Alguns dos argumentos usam o lema de periodicidade da string :
Wlog Vou assumir que todas as cadeias de bits em consideração começam com
0
.Dada uma sequência de períodos em que é o período do prefixo de comprimento 2 i ( sempre), há algumas observações fáceis sobre os possíveis valores de :
[p1 p2 ... pk]
pi
p0 = 1
pk+1
pk+1 ≥ pk
já que um período de uma sequênciaS
também é um período de qualquer prefixo deS
.pk+1 = pk
é sempre uma extensão possível: basta repetir a mesma sequência primitiva para o dobro do número de caracteres.2k < pk+1 ≤ 2k+1
é sempre uma extensão possível. Basta mostrar isso porque uma cadeia de comprimento aperiódica pode ser estendida a uma seqüência de comprimento aperiódica anexando qualquer letra que não seja sua primeira letra.pk+1 = 2k+1
L
L+1
Pegue uma sequência
Sx
de comprimento 2 k cujo período é e considere a sequência de comprimento 2 k + 1 . Claramente tem um período de 2 k +1. Suponha que seu período seja menor.pk
SxyS
SxyS
q
Então , pela periodicidade, o lema também é um período de , e como o maior divisor é menor ou igual a seus argumentos e é o menor período, precisamos ser um fator adequado de 2 k +1. Como seu quociente não pode ser 2, nós temos .
2k+1 + q ≤ 2k+1+1 ≤ 2k+1 + gcd(2k+1, q)
gcd(2k+1, q)
SxyS
q
q
q ≤ (2k+1)/3
Agora, já que é um período , deve ser um período de . Mas o período de é . Temos dois casos:
q ≤ 2k
SxyS
Sx
Sx
pk
gcd(pk, q) = pk
, ou divide exatamente em exatamente .pk
q
pk + q > 2k + gcd(pk, q)
para que o lema da periodicidade não force um período menor.Considere primeiro o segundo caso. , contradizendo a definição de como o período de . Portanto, somos forçados a concluir que esse é um fator de .
pk > 2k + gcd(pk, q) - q ≥ 2k+1 - q ≥ 2k+1 - (2k+1)/3 ≥ 2q
pk
Sx
pk
q
Mas como
q
é um período deSx
e é o período de , o prefixo de comprimento é apenas cópias do prefixo de comprimento , então vemos que também é um período de .pk
Sx
q
q/pk
pk
pk
SxyS
Portanto, o período de
SxyS
é ou 2 k +1. Mas temos duas opções para ! No máximo, uma opção dará período , portanto, pelo menos uma dará período 2 k +1. QED.pk
y
y
pk
O lema da periodicidade nos permite rejeitar algumas das possíveis extensões restantes.
Qualquer extensão que não passou em um teste de aceitação rápida ou de rejeição rápida precisa ser testada de forma construtiva.
A construção de uma cadeia de bits, dada uma sequência de período, é essencialmente um problema de satisfação, mas possui muita estrutura. Existem restrições de igualdade simples implícitas em cada período de prefixo, então eu uso uma estrutura de dados de conjunto de união para combinar bits em clusters independentes. Isso foi suficiente para enfrentar n = 64, mas para n = 128 foi necessário ir além. Emprego duas linhas de argumento úteis:
2k - pk
M
for e o prefixo de comprimento tiver um período , o prefixo de comprimento deverá terminar em . Isso é mais poderoso precisamente nos casos que, de outra forma, teriam clusters independentes, o que é conveniente.01M-1
L > M
L
L
1M
M
for e o prefixo de comprimento tiver um período com e termina em , ele deve de fato terminar em . Isso é mais poderoso no extremo oposto, quando a sequência do período começa com muitas.0M
L > M
L - d
d < M
0d
10d
Se não obtivermos uma contradição imediata forçando o cluster com o primeiro bit (assumido como zero) a ser um, então força bruta (com algumas micro otimizações) sobre os valores possíveis para os clusters não forçados. Observe que a ordem está em número decrescente de unidades porque, se o
i
th bit for um, o período não poderá seri
e queremos evitar períodos mais curtos que os que já são impostos pelo cluster. Descer aumenta as chances de encontrar uma tarefa válida com antecedência.fonte
Ferrugem, 32, 10s
11s29sno meu laptopChame-o com o tamanho do bits como argumento da linha de comando.
Técnicas inteligentes: representam cadeias de bits diretamente como números, use brincadeiras para verificar se há ciclos. Pesquise apenas a primeira metade das cadeias de bits, começando com 0, porque a matriz de períodos de uma cadeia de bits e seu inverso (0s trocados por 1s) são idênticos. Se todas as possibilidades para a posição final já ocorreram, eu não a busco.
Coisas mais inteligentes:
Para desduplicar cada bloco (cadeias em que a primeira metade dos bits são iguais) eu uso um vetor de bits, que é muito mais rápido que um hashset, pois os comprimentos finais do ciclo não precisam de hash.
Além disso, pulo as primeiras etapas da verificação do ciclo, porque sei que o ciclo final não pode ser mais curto que o penúltimo ciclo.
Depois de muitos perfis, agora posso dizer que quase todo o tempo está sendo usado de forma produtiva, portanto, melhorias algorítmicas serão necessárias para melhorar daqui, eu acho. Também mudei para números inteiros de 32 bits para economizar um pouco mais de tempo.
Colocar
bit-vec = "0.4.4"
seu Cargo.tomlSe você deseja executar isso, clone-o: github.com/isaacg1/cycle, em seguida,
Cargo build --release
para compilar e, em seguida,Cargo run --release 32
para executar.fonte
time
dá 27 segundos de usuário no meu laptop.Ferrugem , 16
Experimente online!
Compilação:
rustc -O <name>.rs
A cadeia é implementada como um vetor Bool.
A
next
função itera através das combinações;O
find_period
pega uma fatia Bool e retorna o ponto;O
compute_count_array
faz o trabalho para cada subsequência "poder de dois" de cada combinação de Bools.Teoricamente, nenhum estouro é esperado até
2^n
exceder o valor máximo de u64, ou seja,n > 64
. Esse limite pode ser ultrapassado com um teste caro em s = [verdadeiro, verdadeiro, ..., verdadeiro].A má notícia é: retorna 317 para n = 16, mas não sei por que. Também não sei se isso acontecerá em dez minutos para n = 32, pois o
Vec<bool>
não é otimizado para esse tipo de computação.EDITAR
Eu consegui remover o limite de 64 para
n
. Agora, ele não trava até quen
seja maior que o número máximo de tamanho máximo.Eu descobri por que o código anterior retornou 317 para
n=32
. Eu estava contando conjuntos de períodos e não matrizes de períodos. Havia três matrizes com os mesmos elementos:Agora funciona. Ainda é lento, mas funciona.
fonte
C - 16
Ele falha em valores maiores que 16 cuz de estouro.
Não tenho ideia de quão rápido isso é executado, porque estou em um chromebook executando-o em repl.it.
Apenas implementa a pergunta enquanto lê, analisa todas as seqüências de bits, calcula as matrizes de período e verifica se elas já foram contadas.
Basta compilá-lo com o gcc, etc.
fonte
16
quando o código foi alterado para que os doismalloc
s fossemmalloc(...int*))
e...**
respectivamente16
impressosAnswer: 320
conforme o esperado, porém32
impressosAnswer: 0
(e rapidamente).n = 8
mas depois que o resultado foi impresso, o que significa que a pilha está corrompida. Provavelmente você está gravando em algum lugar além do (s) bloco (s) de memória alocada.Haskell
Compile com
ghc -O2
. Experimente online!É executado em menos de 0,1 segundo no meu laptop de 6 anos
n=16
.n=32
leva9992min, então estou com fator 9 ou 10 de folga. Tentei armazenar em cache os períodos em uma tabela de pesquisa para não precisar recalculá-los repetidamente, mas isso fica rapidamente sem memória na minha máquina de 4 GB.fonte
Python 2 (PyPy), 16
fonte
osboxes@osboxes:~/python$ python ascii_user.py 16 None
[C ++], 32, 4 minutos
fonte