Ontem fiz essa pergunta em um teste de algoritmos e não consigo descobrir a resposta. Isso está me deixando absolutamente louco, porque valeu cerca de 40 pontos. Eu acho que a maioria da turma não a resolveu corretamente, porque não encontrei uma solução nas últimas 24 horas.
Dada uma sequência binária arbitrária de comprimento n, encontre três espaçadas igualmente dentro da sequência, se existirem. Escreva um algoritmo que resolva isso em O (n * log (n)).
Portanto, seqüências de caracteres como essas têm três que são "espaçadas igualmente": 11100000, 0100100100
edit: como é um número aleatório, deve poder trabalhar para qualquer número. Os exemplos que dei foram para ilustrar a propriedade "uniformemente espaçada". Então 1001011 é um número válido. Com 1, 4 e 7 sendo os que são espaçados igualmente.
Respostas:
Finalmente! Seguindo os leads na resposta do sdcvvc , temos: o algoritmo O (n log n) para o problema! Também é simples, depois que você entende. Quem adivinhou a FFT estava certo.
O problema: recebemos uma sequência binária
S
de comprimento n e queremos encontrar três 1s com espaçamento uniforme. Por exemplo,S
pode ser110110010
, onde n = 9. Ele espaçou 1s uniformemente nas posições 2, 5 e 8.Digitalize da
S
esquerda para a direita e faça uma listaL
de posições de 1. Para oS=110110010
exposto, temos a lista L = [1, 2, 4, 5, 8]. Este passo é O (n). O problema é agora encontrar uma progressão aritmética de comprimento 3 emL
, ou seja, para encontrar distinta a, b, c emL
tais que bA = CB , ou de forma equivalente a + c = 2b . Para o exemplo acima, queremos encontrar a progressão (2, 5, 8).Faça um polinômio
p
com os termos x k para cada k polL
. Para o exemplo acima, criamos o polinômio p (x) = (x + x 2 + x 4 + x 5 + x 8 ) . Este passo é O (n).Encontre o polinômio
q
= p 2 , usando a Transformada rápida de Fourier . Para o exemplo acima, obtemos o polinômio q (x) = x 16 + 2x 13 + 2x 12 + 3x 10 + 4x 9 + x 8 + 2x 7 + 4x 6 + 2x 5 + x 4 + 2x 3 + x 2 . Esta etapa é O (n log n).Ignore todos os termos, exceto aqueles correspondentes a x 2k por alguns k in
L
. Para o exemplo acima, obtemos os termos x 16 , 3x 10 , x 8 , x 4 , x 2 . Esta etapa é O (n), se você optar por fazê-lo.Aqui está o ponto crucial: o coeficiente de qualquer x 2b para b in
L
é precisamente o número de pares (a, c) deL
modo que a + c = 2b . [CLRS, Ex. 30.1-7] Um desses pares é (b, b) sempre (portanto, o coeficiente é pelo menos 1), mas se existe algum outro par (a, c) , então o coeficiente é pelo menos 3, de (a, c ) e (c, a) . Para o exemplo acima, temos o coeficiente de x 10 para ser 3 precisamente por causa do PA (2,5,8). (Esses coeficientes x 2bsempre serão números ímpares, pelas razões acima. E todos os outros coeficientes em q sempre serão pares.)Portanto, o algoritmo deve examinar os coeficientes desses termos x 2b e verificar se algum deles é maior que 1. Se não houver nenhum, não haverá 1s espaçados uniformemente. Se não é uma b em
L
para os quais o coeficiente de x 2b é maior do que 1, então sabe que há alguns par (a, c) - diferente (B, b) - para que a + c = 2b . Para encontrar o par real, simplesmente tentamos cada a emL
(o c correspondente seria 2b-a ) e verificamos se existe um 1 na posição 2b-a emS
. Este passo é O (n).Isso é tudo, pessoal.
Alguém pode perguntar: precisamos usar a FFT? Muitas respostas, como beta , flybywire e rsp , sugerem que a abordagem que verifica cada par de 1s e vê se existe um 1 na "terceira posição", pode funcionar em O (n log n), com base na intuição que, se houver 1s demais, encontraríamos um triplo com facilidade e, se houver 1s muito baixo, verificar todos os pares leva pouco tempo. Infelizmente, embora essa intuição esteja correta e a abordagem simples seja melhor que O (n 2 ), ela não é significativamente melhor. Como na resposta do sdcvvc , podemos usar o "conjunto Cantor-like" de strings de comprimento n = 3 k, com 1s nas posições cuja representação ternária possui apenas 0s e 2s (sem 1s). Essa string possui 2 k = n (log 2) / (log 3) ≈ n 0,63 e não 1s espaçados de maneira uniforme; portanto, a verificação de todos os pares seria da ordem do quadrado do número de 1s nela: 4 k ≈ n 1,26 que infelizmente é assintoticamente muito maior que (n log n). De fato, o pior caso é ainda pior: Leo Moser, em 1953, construiu (efetivamente) aquelas cordas que possuem n 1-c / √ (log n) 1s nelas, mas não 1s espaçadas uniformemente, o que significa que nessas cordas, o simples abordagem levaria Θ (n 2-2c / √ (log n) )- apenas uma pequena pouco melhor do que Θ (n 2 ) , surpreendentemente!
Sobre o número máximo de 1s em uma sequência de comprimento n sem 3 espaçados uniformemente (que vimos acima era pelo menos n 0,63 da construção fácil do tipo Cantor e pelo menos n 1-c / √ (log n) com Moser) - este é o OEIS A003002 . Também pode ser calculado diretamente do OEIS A065825 como k, de modo que A065825 (k) ≤ n <A065825 (k + 1). Eu escrevi um programa para encontrá-los, e acontece que o algoritmo ganancioso não fornece a maior seqüência de caracteres. Por exemplo, para n = 9, podemos obter 5 1s (110100011), mas o ganancioso fornece apenas 4 (110110000), para n= 26, podemos obter 11 1s (11001010001000010110001101), mas o ganancioso fornece apenas 8 (110110000110110000000000000000), e para n = 74, podemos obter 22 1s (11000010110001000001010100000000000000000000010001011010000010001101000000000000000000000000000000000010000100001000100010001000100010001000100010001) Eles concordam em alguns lugares até 50 (por exemplo, de 38 a 50). Como as referências da OEIS dizem, parece que Jaroslaw Wroblewski está interessado nessa questão, e ele mantém um site sobre esses conjuntos não médios . Os números exatos são conhecidos apenas até 194.
fonte
Seu problema é chamado de MÉDIA neste documento (1999):
Wikipedia :
Isso é suficiente para resolver seu problema :).
O que é muito importante é que O (n log n) é complexidade em termos de número de zeros e uns, não a contagem de uns (que pode ser dada como uma matriz, como [1,5,9,15]). Verificar se um conjunto tem uma progressão aritmética, termos de número de 1's, é difícil e, de acordo com esse artigo, em 1999, nenhum algoritmo mais rápido que O (n 2 ) é conhecido e é conjecturado que ele não exista. Todo mundo que não leva isso em consideração está tentando resolver um problema em aberto.
Outras informações interessantes, principalmente irrelevantes:
Limite inferior:
Um limite inferior fácil é o conjunto do tipo Cantor (números 1..3 ^ n-1 que não contém 1 em sua expansão ternária) - sua densidade é n ^ (log_3 2) (cerca de 0,631). Portanto, qualquer verificação se o conjunto não é muito grande e a verificação de todos os pares não são suficientes para obter O (n log n). Você precisa investigar a sequência de maneira mais inteligente. Um limite inferior melhor é citado aqui - é n 1-c / (log (n)) ^ (1/2) . Isso significa que o conjunto Cantor não é o ideal.
Limite superior - meu antigo algoritmo:
Sabe-se que para n grande, um subconjunto de {1,2, ..., n} que não contém progressão aritmética tem no máximo n / (log n) ^ (1/20) elementos. O artigo Sobre triplos na progressão aritmética prova mais: o conjunto não pode conter mais do que n * 2 28 * (log log n / log n) 1/2 elementos. Assim, você pode verificar se esse limite é alcançado e, se não, verificar ingenuamente os pares. Esse é o algoritmo O (n 2 * log log n / log n), mais rápido que O (n 2 ). Infelizmente "On triples ..." está no Springer - mas a primeira página está disponível e a exposição de Ben Green está disponível aqui , página 28, teorema 24.
A propósito, os trabalhos são de 1999 - o mesmo ano que o primeiro que mencionei, provavelmente é por isso que o primeiro não menciona esse resultado.
fonte
Esta não é uma solução, mas uma linha de pensamento semelhante ao que Olexiy estava pensando
Eu estava brincando com a criação de sequências com o número máximo de unidades, e todas são bastante interessantes, tenho até 125 dígitos e aqui estão os três primeiros números encontrados ao tentar inserir o maior número possível de bits '1':
Observe que todos eles são fractais (não muito surpreendentes, dadas as restrições). Pode haver algo em pensar de trás para frente, talvez se a sequência não for um fractal com uma característica, ela deve ter um padrão repetitivo?
Obrigado ao beta pelo melhor termo para descrever esses números.
Atualização: Infelizmente, parece que o padrão falha ao iniciar com uma sequência inicial grande o suficiente, como: 10000000000001:
fonte
Suspeito que uma abordagem simples que se pareça com O (n ^ 2) realmente produza algo melhor, como O (n ln (n)). As seqüências que demoram mais para serem testadas (para qualquer n) são as que não contêm trios, e isso impõe severas restrições ao número de 1s que podem estar na sequência.
Eu vim com alguns argumentos de acenar com a mão, mas não consegui encontrar uma prova clara. Vou dar uma facada no escuro: a resposta é uma idéia muito inteligente que o professor conhece há tanto tempo que parece óbvia, mas é muito difícil para os alunos. (Ou você dormiu durante a palestra que a abordou.)
fonte
Revisão: 17-10-2009 23:00
Eu executei isso em grandes números (tipo, cadeias de 20 milhões) e agora acredito que esse algoritmo não é O (n logn). Não obstante, é uma implementação bastante interessante e contém várias otimizações que a tornam muito rápida. Ele avalia todos os arranjos de cadeias binárias com 24 ou menos dígitos em menos de 25 segundos.
Atualizei o código para incluir a
0 <= L < M < U <= X-1
observação de hoje mais cedo.Original
Isso é, em conceito, semelhante a outra pergunta que respondi . Esse código também analisou três valores em uma série e determinou se um trigêmeo satisfazia uma condição. Aqui está o código C # adaptado disso:
As principais diferenças são:
Este código gera um conjunto de dados poderoso para encontrar as entradas mais difíceis de resolver para esse algoritmo.
O código da pergunta anterior gerou todas as soluções usando um gerador python. Este código apenas exibe o mais difícil para cada comprimento de padrão.
Este código verifica a distância entre o elemento do meio e as extremidades esquerda e direita. O código python testou se uma soma estava acima ou abaixo de 0.
O código atual trabalha do meio para a borda para encontrar um candidato. O código no problema anterior funcionava das bordas em direção ao meio. Essa última alteração fornece uma grande melhoria de desempenho.
Com base nas observações no final deste artigo, o código pesquisa pares de números pares de pares de números ímpares para encontrar L e U, mantendo M fixo. Isso reduz o número de pesquisas pré-computando informações. Assim, o código usa dois níveis de indireção no loop principal do FindCandidate e requer duas chamadas para o FindCandidate para cada elemento do meio: uma para números pares e outra para números ímpares.
A idéia geral é trabalhar em índices, não na representação bruta dos dados. O cálculo de uma matriz em que os 1s aparecem permite que o algoritmo seja executado no tempo proporcional ao número de 1s nos dados, e não no tempo proporcional ao comprimento dos dados. Essa é uma transformação padrão: crie uma estrutura de dados que permita uma operação mais rápida, mantendo o problema equivalente.
Os resultados estão desatualizados: removidos.
Edit: 2009-10-16 18:48
Nos dados de yx, que recebem alguma credibilidade nas outras respostas como representativas dos dados concretos para calcular, eu obtenho esses resultados ... Eu os removi. Eles estão desatualizados.
Eu apontaria que esses dados não são os mais difíceis para o meu algoritmo, então acho que a suposição de que os fractais de yx são os mais difíceis de resolver está equivocada. O pior caso para um algoritmo específico, espero, dependerá do próprio algoritmo e provavelmente não será consistente entre diferentes algoritmos.
Edit: 2009-10-17 13:30
Outras observações sobre isso.
Primeiro, converta a sequência de 0 e 1 em uma matriz de índices para cada posição do 1. Digamos que o comprimento dessa matriz A seja X. Então o objetivo é encontrar
de tal modo que
ou
Como A [L] e A [U] somam um número par, eles não podem ser (pares, ímpares) ou (ímpares, pares). A busca por uma correspondência pode ser aprimorada dividindo A [] em conjuntos ímpares e pares e procurando correspondências em A [M] nos conjuntos de candidatos pares e ímpares.
No entanto, isso é mais uma otimização de desempenho do que uma melhoria algorítmica, eu acho. O número de comparações deve cair, mas a ordem do algoritmo deve ser a mesma.
Edit 2009-10-18 00:45
Outra otimização me ocorre, na mesma linha que separa os candidatos em pares e ímpares. Como os três índices precisam ser adicionados a um múltiplo de 3 (a, a + x, a + 2x - mod 3 é 0, independentemente de aex), é possível separar L, M e U nos valores do mod 3 :
De fato, você pode combinar isso com a observação par / ímpar e separá-los em seus valores do mod 6:
e assim por diante. Isso forneceria uma otimização de desempenho adicional, mas não uma aceleração algorítmica.
fonte
Ainda não conseguiu encontrar a solução :(, mas tenho algumas idéias.
E se começarmos de um problema inverso: construa uma sequência com o número máximo de 1s e SEM trios uniformemente espaçados. Se você puder provar que o número máximo de 1s é o (n), poderá melhorar sua estimativa iterando apenas através da lista de 1s.
fonte
Isso pode ajudar ....
Esse problema se reduz ao seguinte:
Por exemplo, dada uma sequência de
[ 3, 5, 1, 3, 6, 5, 2, 2, 3, 5, 6, 4 ]
, encontraríamos uma subsequência de[ 3, 6, 5, 2, 2]
com um prefixo de[ 3, 6 ]
com prefixo soma de9
e um sufixo de[ 5, 2, 2 ]
com sufixo soma de9
.A redução é a seguinte:
Por exemplo, dada uma sequência de
[ 0, 1, 1, 0, 0, 1, 0, 0, 0, 1 0 ]
, encontraríamos a redução de[ 1, 3, 4]
. A partir dessa redução, calculamos a subsequência contígua de[ 1, 3, 4]
, o prefixo de[ 1, 3]
com soma de4
e o sufixo de[ 4 ]
com soma de4
.Essa redução pode ser calculada em
O(n)
.Infelizmente, não sei para onde ir a partir daqui.
fonte
Para o tipo de problema simples (ou seja, você pesquisa três "1" com apenas (ou seja, zero ou mais) "0" entre eles), é bastante simples: você pode dividir a sequência a cada "1" e procurar duas subsequências adjacentes o mesmo comprimento (a segunda subsequência não é a última, é claro). Obviamente, isso pode ser feito em O (n) tempo.
Para a versão mais complexa (ou seja, você pesquisa um índice ie uma diferença g > 0 tal que
s[i]==s[i+g]==s[i+2*g]=="1"
), não tenho certeza, se existe uma solução O (n log n) , uma vez que existem possivelmente trigêmeos O (n²) tendo essa propriedade (pense em uma série de todas, existem aproximadamente n / 2 desses trigêmeos). Claro, você está procurando apenas um desses, mas atualmente não tenho idéia de como encontrá-lo ...fonte
Uma pergunta divertida, mas depois que você percebe que o padrão real entre dois '1s não importa, o algoritmo se torna:
No código, da maneira JTest, (observe que este código não foi escrito para ser mais eficiente e eu adicionei alguns println's para ver o que acontece.)
fonte
Pensei em uma abordagem de dividir e conquistar que poderia funcionar.
Primeiro, no pré-processamento, você precisa inserir todos os números com menos da metade do seu tamanho de entrada ( n / 3) em uma lista.
Dada uma sequência:
0000010101000100
(observe que este exemplo em particular é válido)Insira todos os números primos (e 1) de 1 a (16/2) em uma lista: {1, 2, 3, 4, 5, 6, 7}
Depois divida ao meio:
100000101 01000100
Continue fazendo isso até chegar às cadeias de tamanho 1. Para todas as cadeias de tamanho um com um 1, adicione o índice da cadeia à lista de possibilidades; caso contrário, retorne -1 para falha.
Você também precisará retornar uma lista de distâncias de espaçamento ainda possíveis, associadas a cada índice inicial. (Comece com a lista que você criou acima e remova os números à medida que avança) Aqui, uma lista vazia significa que você está lidando apenas com 1 e, portanto, qualquer espaçamento é possível neste momento; caso contrário, a lista inclui espaçamentos que devem ser descartados.
Então, continuando com o exemplo acima:
1000 0101 0100 0100
10 00 01 01 01 00 01 00
1 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0
No primeiro passo da combinação, temos oito conjuntos de dois agora. No primeiro, temos a possibilidade de um conjunto, mas aprendemos que o espaçamento de 1 é impossível por causa do outro zero estar lá. Portanto, retornamos 0 (para o índice) e {2,3,4,5,7} pelo fato de que o espaçamento por 1 é impossível. No segundo, não temos nada e, portanto, retornamos -1. No terceiro, temos uma partida sem espaçamento eliminada no índice 5, então retorne 5, {1,2,3,4,5,7}. No quarto par, retornamos 7 {1,2,3,4,5,7}. No quinto, retorne 9 {1,2,3,4,5,7}. No sexto, retorne -1. No sétimo, retorne 13, {1,2,3,4,5,7}. No oitavo, retorne -1.
Combinando novamente em quatro conjuntos de quatro, temos:
1000
: Retorno (0, {4,5,6,7})0101
: Retorno (5, {2,3,4,5,6,7}), (7, {1,2,3,4,5,6 , 7})0100
: Retorno (9, {3,4,5,6,7})0100
: Retorno (13, {3,4,5,6,7})Combinando em conjuntos de oito:
10000101
: Retorno (0, {5,7}), (5, {2,3,4,5,6,7}), (7, {1,2,3,4,5,6,7})01000100
: Retorno (9, {4,7}), (13, {3,4,5,6,7})Combinando um conjunto de dezesseis:
10000101 01000100
À medida que progredimos, continuamos verificando todas as possibilidades até o momento. Até essa etapa, deixamos coisas que iam além do final da string, mas agora podemos verificar todas as possibilidades.
Basicamente, verificamos o primeiro 1 com espaçamento de 5 e 7 e descobrimos que eles não estão alinhados com o número 1. (Observe que cada verificação é CONSTANTE, e não o tempo linear). Depois, verificamos a segunda (índice 5) com espaçamentos de 2, 3, 4, 5, 6 e 7 - ou o faria, mas podemos parar em 2 desde que realmente combina.
Ufa! Esse é um algoritmo bastante longo.
Eu não sei 100% se é O (n log n) por causa da última etapa, mas tudo até lá é definitivamente O (n log n) , tanto quanto eu posso dizer. Voltarei a isso mais tarde e tentarei refinar o último passo.
EDIT: Alterei minha resposta para refletir o comentário de Welbog. Desculpe pelo erro. Também escreverei algum pseudocódigo mais tarde, quando tiver um pouco mais de tempo para decifrar o que escrevi novamente. ;-)
fonte
100010001
? Se eu entendi sua abordagem corretamente, ela não será compatível porque a resposta correta(0,{4})
não é possível calcular. Dado que você precisa de números não primos em sua lista, é fácil criar seqüências patológicas que aumentem as listas de possibilidades que você precisa verificar para maiores que O (n log (n)), eu acho.Vou dar um palpite aqui e deixar que aqueles que são melhores em calcular a complexidade me ajudem a saber como meu algoritmo se sai em termos de notação O
Não tenho idéia de como calcular a complexidade para isso, alguém pode ajudar?
edit: adicione algum código para ilustrar minha ideia
edit2: tentei compilar meu código e encontrei alguns erros importantes, corrigidos
fonte
Eu vim com algo assim:
Isso é inspirado no andycjw.
Quanto à complexidade, isso pode ser O (nlogn), pois em cada recursão estamos dividindo por dois.
Espero que ajude.
fonte
Ok, vou dar outra facada no problema. Eu acho que posso provar um algoritmo O (n log (n)) que é semelhante aos já discutidos usando uma árvore binária balanceada para armazenar distâncias entre 1s. Essa abordagem foi inspirada na observação de Justice sobre como reduzir o problema a uma lista de distâncias entre os 1s.
Poderíamos fazer a varredura da sequência de entrada para construir uma árvore binária balanceada em torno da posição dos 1s, de modo que cada nó armazene a posição do 1 e cada borda seja rotulada com a distância do 1 adjacente para cada nó filho. Por exemplo:
Isso pode ser feito em O (n log (n)), pois, para uma sequência de tamanho n, cada inserção recebe O (log (n)) no pior caso.
Em seguida, o problema é procurar na árvore para descobrir se, em qualquer nó, existe um caminho desse nó através do filho esquerdo que tem a mesma distância que um caminho através do filho direito. Isso pode ser feito recursivamente em cada subárvore. Ao mesclar duas subárvores na pesquisa, devemos comparar as distâncias dos caminhos na subárvore esquerda com as distâncias dos caminhos à direita. Como o número de caminhos em uma subárvore será proporcional ao log (n) e o número de nós é n, acredito que isso possa ser feito no tempo O (n log (n)).
Eu perdi alguma coisa?
fonte
Parecia um problema divertido, então decidi tentar.
Estou assumindo que 111000001 encontraria os 3 primeiros e seria bem-sucedido. Essencialmente, o número de zeros após o 1 é importante, pois 0111000 é o mesmo que 111000, de acordo com sua definição. Depois de encontrar dois casos de 1, o próximo 1 encontrado completa a trilogia.
Aqui está em Python:
Esta é uma primeira tentativa, por isso tenho certeza de que isso pode ser escrito de uma maneira mais limpa. Liste os casos em que esse método falha abaixo.
fonte
Presumo que o motivo é nlog (n) devido ao seguinte:
Então, você tem n, log (n) e 1 ... O (nlogn)
Edit: Opa, meu mal. Meu cérebro definiu que n / 2 era logn ... o que obviamente não é (dobrar o número de itens ainda dobra o número de iterações no loop interno). Isso ainda está em n ^ 2, não resolvendo o problema. Bem, pelo menos eu tenho que escrever algum código :)
Implementação em Tcl
fonte
Acho que encontrei uma maneira de resolver o problema, mas não consigo construir uma prova formal. A solução que eu fiz foi escrita em Java e usa um contador 'n' para contar quantos acessos à lista / matriz ele faz. Portanto, n deve ser menor ou igual a stringLength * log (stringLength) se estiver correto. Eu tentei para os números 0 a 2 ^ 22, e funciona.
Ele começa iterando sobre a sequência de entrada e fazendo uma lista de todos os índices que contêm uma. Este é apenas O (n).
Em seguida, na lista de índices, ele escolhe um firstIndex e um secondIndex que é maior que o primeiro. Esses dois índices devem conter um, porque estão na lista de índices. A partir daí, o thirdIndex pode ser calculado. Se o inputString [thirdIndex] for 1, ele será interrompido.
}
nota adicional: o contador n não é incrementado quando itera sobre a sequência de entrada para construir a lista de índices. Esta operação é O (n), portanto não afetará a complexidade do algoritmo.
fonte
O(n^2)
algoritmo.Uma incursão no problema é pensar em fatores e mudanças.
Com a mudança, você compara a sequência de uns e zeros com uma versão deslocada de si mesma. Você pega os correspondentes. Tomemos este exemplo deslocado por dois:
Os 1s resultantes (AND bit a bit) devem representar todos os 1s que são espaçados igualmente por dois. O mesmo exemplo mudou em três:
Nesse caso, não há 1's que estão uniformemente espaçados três.
Então, o que isso lhe diz? Bem, você só precisa testar turnos que são números primos. Por exemplo, digamos que você tenha dois 1s, que são seis separados. Você só teria que testar 'dois' turnos e 'três' turnos (uma vez que estes dividem seis). Por exemplo:
Portanto, os únicos turnos que você precisa verificar são 2,3,5,7,11,13 etc. Até o primo mais próximo da raiz quadrada do tamanho da sequência de dígitos.
Quase resolvido?
Eu acho que estou mais perto de uma solução. Basicamente:
Penso que a maior pista para a resposta é que os algoritmos de classificação mais rápidos são O (n * log (n)).
ERRADO
O passo 1 está errado, conforme indicado por um colega. Se tivermos 1's nas posições 2,12 e 102. Então, tomando um módulo de 10, todos terão os mesmos remanescentes e, no entanto, não serão igualmente espaçados! Desculpe.
fonte
Aqui estão alguns pensamentos que, apesar de meus esforços, parecem não se envolver. Ainda assim, eles podem ser um ponto de partida útil para a análise de alguém.
Considere a solução proposta da seguinte forma, que é a abordagem sugerida por várias pessoas, inclusive eu em uma versão anterior desta resposta.
:)
Agora considere as seqüências de caracteres de entrada como as seguintes, que não terão uma solução:
Em geral, esta é a concatenação de k strings da forma j 0's, seguida de um 1 para j de zero a k-1.
Observe que os comprimentos das substrings são 1, 2, 3 etc. Portanto, o tamanho do problema n possui substrings de comprimentos 1 a k, de modo que n = k (k + 1) / 2.
Observe que k também rastreia o número de 1s que devemos considerar. Lembre-se de que toda vez que vemos um 1, precisamos considerar todos os 1 vistos até agora. Portanto, quando vemos o segundo 1, consideramos apenas o primeiro, quando vemos o terceiro 1, reconsideramos os dois primeiros, quando vemos o quarto 1, precisamos reconsiderar os três primeiros e assim por diante. No final do algoritmo, consideramos k (k-1) / 2 pares de 1's. Chame isso de p.
A relação entre n e p é que n = p + k.
O processo de passar pela string leva O (n) tempo. Sempre que um 1 é encontrado, são feitas comparações no máximo (k-1). Como n = k (k + 1) / 2, n> k ** 2, então sqrt (n)> k. Isso nos dá O (n sqrt (n)) ou O (n ** 3/2). Observe, no entanto, que pode não ser um limite muito restrito, porque o número de comparações varia de 1 a um máximo de k, não é k o tempo todo. Mas não sei como explicar isso em matemática.
Ainda não é O (n log (n)). Além disso, não posso provar que esses dados são os piores casos, embora eu suspeite que sejam. Eu acho que um empacotamento mais denso de 1 para a frente resulta em um empacotamento ainda mais esparso no final.
Como alguém ainda pode achar útil, aqui está o meu código para essa solução no Perl:
fonte
Ao digitalizar 1s, adicione suas posições a uma Lista. Ao adicionar os segundos 1s e sucessivos, compare-os com cada posição da lista até agora. O espaçamento é igual a currentOne (centro) - previousOne (esquerda). O bit do lado direito é currentOne + espaçamento. Se é 1, o fim.
A lista de unidades cresce inversamente com o espaço entre elas. Em termos simples, se você tiver muitos 0s entre os 1s (na pior das hipóteses), sua lista de 1s conhecidos crescerá muito lentamente.
fonte
Pensei em adicionar um comentário antes de postar a 22ª solução ingênua para o problema. Para a solução ingênua, não precisamos mostrar que o número de 1s na string é no máximo O (log (n)), mas sim que é no máximo O (sqrt (n * log (n)).
Solver:
É basicamente um pouco parecido com a idéia e a implementação do flybywire, embora olhando para a frente em vez de para trás.
Construtor ganancioso de cordas:
(Em minha defesa, ainda estou no estágio de entendimento 'aprender python')
Além disso, saída potencialmente útil da construção gananciosa de cordas, há um salto bastante consistente depois de atingir uma potência de 2 no número de 1's ... que eu não estava disposto a esperar para testemunhar atingindo 2096.
fonte
Vou tentar apresentar uma abordagem matemática. Isso é mais um começo do que um fim; portanto, qualquer ajuda, comentário ou mesmo contradição será profundamente apreciada. No entanto, se essa abordagem for comprovada - o algoritmo é uma pesquisa direta na string.
Dado um número fixo de espaços
k
e uma stringS
, a busca por um trigêmeo com espaçamento k levaO(n)
- Nós simplesmente testamos todos os0<=i<=(n-2k)
ifS[i]==S[i+k]==S[i+2k]
. O teste levaO(1)
e fazemosn-k
vezes quek
é uma constante, por isso levaO(n-k)=O(n)
.Vamos supor que exista uma proporção inversa entre o número de
1
's e o máximo de espaços que precisamos procurar. Ou seja, se existem muitos1
, deve haver um trigêmeo e deve ser bastante denso; Se houver apenas alguns1
, o trigêmeo (se houver) pode ser bastante escasso. Em outras palavras, posso provar que, se tenho o suficiente1
, esse trigêmeo deve existir - e quanto mais1
eu tenho, um trigêmeo mais denso deve ser encontrado. Isso pode ser explicado pelo princípio Pigeonhole - Espero elaborar mais adiante.Digamos que tenha um limite superior
k
no número possível de espaços que tenho que procurar. Agora, para cada1
localizado emS[i]
, precisamos fazer check-1
inS[i-1]
eS[i+1]
,S[i-2]
eS[i+2]
, ...S[i-k]
eS[i+k]
. Isso levaO((k^2-k)/2)=O(k^2)
para cada1
emS
- devido a Gauss' Formula Series Soma . Observe que isso difere da seção 1 - estou tendok
como limite superior o número de espaços, não como espaço constante.Nós precisamos provar
O(n*log(n))
. Ou seja, precisamos mostrar quek*(number of 1's)
é proporcional alog(n)
.Se pudermos fazer isso, o algoritmo é trivial - para cada um
1
emS
cujo índice estejai
, basta procurar1
os de cada lado até a distânciak
. Se dois foram encontrados na mesma distância, retornei
ek
. Novamente, a parte complicada seria encontrark
e provar a correção.Eu realmente aprecio seus comentários aqui - eu tenho tentado encontrar a relação entre
k
e o número de1
's no meu quadro, até agora sem sucesso.fonte
Suposição:
Apenas errado, falando sobre o número de log (n) do limite superior de unidades
EDITAR:
Agora, descobri que, usando os números do Cantor (se correto), a densidade no set é (2/3) ^ Log_3 (n) (que função estranha) e concordo que a densidade do log (n) / n é muito alta.
Se esse for o limite superior, existe um algoritmo que resolve esse problema em pelo menos O (n * (3/2) ^ (log (n) / log (3))) complexidade de tempo e O ((3/2) ^ ( log (n) / log (3))) complexidade do espaço. (verifique a resposta da Justiça para o algoritmo)
Isso ainda é muito melhor que O (n ^ 2)
Essa função ((3/2) ^ (log (n) / log (3))) realmente se parece com n * log (n) à primeira vista.
Como consegui essa fórmula?
Visualizando o número dos cantores na string.
Suponha que o comprimento da string seja 3 ^ p == n
Em cada etapa da geração da string Cantor, você mantém 2/3 do número anterior de unidades. Aplique este p vezes.
Isso significa que (n * ((2/3) ^ p)) -> (((3 ^ p)) * ((2/3) ^ p)) os restantes e após a simplificação 2 ^ p. Isso significa 2 ^ p ones em 3 ^ p string -> (3/2) ^ p ones. Substitua p = log (n) / log (3) e obtenha
((3/2) ^ (log (n) / log (3)))
fonte
Que tal uma solução O (n) simples, com espaço O (n ^ 2)? (Usa a suposição de que todos os operadores bit a bit funcionam em O (1).)
O algoritmo basicamente funciona em quatro estágios:
Etapa 1: para cada bit em seu número original, descubra a que distância estão os números, mas considere apenas uma direção. (Eu considerei todos os bits na direção do bit menos significativo.)
Etapa 2: Inverta a ordem dos bits na entrada;
Etapa 3: Execute novamente a etapa 1 na entrada reversa.
Etapa 4: Compare os resultados da Etapa 1 e da Etapa 3. Se algum bit estiver igualmente espaçado acima E abaixo, devemos obter um resultado.
Lembre-se de que nenhuma etapa do algoritmo acima leva mais tempo que O (n). ^ _ ^
Como um benefício adicional, este algoritmo encontrará TODOS os igualmente espaçados de CADA número. Por exemplo, se você obtiver um resultado de "0x0005", haverá espaços igualmente espaçados em ambas as unidades 1 e 3
Eu realmente não tentei otimizar o código abaixo, mas é um código C # compilável que parece funcionar.
Alguém provavelmente comentará que, para qualquer número suficientemente grande, as operações bit a bit não podem ser feitas em O (1). Você estaria certo. No entanto, eu suporia que toda solução que usa adição, subtração, multiplicação ou divisão (que não pode ser feita por deslocamento) também teria esse problema.
fonte
Abaixo está uma solução. Pode haver alguns pequenos erros aqui e ali, mas a idéia é sólida.
Edit: Não é n * log (n)
PSEUDO-CÓDIGO:
Código c #:
Como funciona:
fonte
Obviamente, precisamos pelo menos verificar grupos de trigêmeos ao mesmo tempo, portanto, precisamos comprimir os cheques de alguma forma. Eu tenho um algoritmo candidato, mas a análise da complexidade do tempo está além da minha capacidade * limite de tempo.
Crie uma árvore em que cada nó tenha três filhos e cada nó contenha o número total de 1s em suas folhas. Crie uma lista vinculada ao longo dos 1s também. Atribua a cada nó um custo permitido proporcional ao intervalo que ele cobre. Enquanto o tempo que gastamos em cada nó estiver dentro do orçamento, teremos um algoritmo O (n lg n).
-
Comece pela raiz. Se o quadrado do número total de 1s abaixo for menor que o custo permitido, aplique o algoritmo ingênuo. Caso contrário, recorrer a seus filhos.
Agora, retornamos dentro do orçamento ou sabemos que não há trigêmeos válidos totalmente contidos em uma das crianças. Portanto, devemos verificar os trigêmeos entre nós.
Agora as coisas ficam incrivelmente bagunçadas. Queremos, essencialmente, recuar sobre os possíveis conjuntos de filhos, limitando o alcance. Assim que o intervalo for restrito o suficiente para que o algoritmo ingênuo seja executado dentro do orçamento, você o fará. Desfrute de implementar isso, porque eu garanto que será tedioso. Há uma dúzia de casos.
-
A razão pela qual acho que o algoritmo funcionará é porque as seqüências sem trigêmeos válidos parecem alternar entre grupos de 1 e muitos 0. Ele efetivamente divide o espaço de pesquisa próximo e a árvore emula essa divisão.
O tempo de execução do algoritmo não é óbvio. Ele se baseia nas propriedades não triviais da sequência. Se os 1s são realmente escassos, o algoritmo ingênuo funcionará dentro do orçamento. Se os 1s são densos, uma correspondência deve ser encontrada imediatamente. Mas se a densidade é "perfeita" (por exemplo, perto de ~ n ^ 0,63, que você pode obter definindo todos os bits em posições sem o dígito '2' na base 3), não sei se funcionará. Você teria que provar que o efeito de divisão é forte o suficiente.
fonte
Nenhuma resposta teórica aqui, mas escrevi um programa Java rápido para explorar o comportamento em tempo de execução em função de k e n, em que n é o comprimento total de bits e k é o número de 1s. Estou com alguns dos respondentes que estão dizendo que o algoritmo "regular" que verifica todos os pares de posições de bits e procura o terceiro bit, mesmo que exija O (k ^ 2) na pior das hipóteses, em A realidade porque o pior caso precisa de cadeias de bits esparsas, é O (n ln n).
Enfim, aqui está o programa abaixo. É um programa no estilo Monte-Carlo que executa um grande número de tentativas NTRIALS para constante n e gera aleatoriamente conjuntos de bits para uma faixa de valores k usando processos de Bernoulli com densidade de unidades restrita entre limites que podem ser especificados e registra o tempo de execução de encontrar ou não encontrar um triplo de espaçados uniformemente, tempo medido nas etapas NÃO no tempo da CPU. Executei-o para n = 64, 256, 1024, 4096, 16384 * (ainda em execução), primeiro um teste com 500000 tentativas para ver quais valores de k levam mais tempo, depois outro teste com 5000000 tentativas com estreitas- densidade para ver como são esses valores. Os tempos de execução mais longos ocorrem com uma densidade muito esparsa (por exemplo, para n = 4096, os picos do tempo de execução estão na faixa k = 16-64, com um pico suave para o tempo de execução médio em 4212 etapas @ k = 31, tempo de execução máximo atingiu 5101 etapas @ k = 58). Parece que seriam necessários valores extremamente grandes de N para a pior etapa O (k ^ 2) se tornar maior que a etapa O (n) em que você varre a cadeia de bits para encontrar os índices de posição do 1.
fonte
Estou tendo problemas com os piores cenários com milhões de dígitos. A difusão de
/dev/urandom
essencialmente fornece O (n), mas eu sei que o pior caso é pior que isso. Eu simplesmente não posso dizer o quanto pior. Para os pequenosn
, é trivial encontrar entradas em torno de3*n*log(n)
, mas é surpreendentemente difícil diferenciá-las de alguma outra ordem de crescimento para esse problema em particular.Alguém que estava trabalhando nas entradas dos piores casos pode gerar uma string com comprimento maior que, digamos, cem mil?
fonte
Uma adaptação do algoritmo Rabin-Karp pode ser possível para você. Sua complexidade é 0 (n) para que possa ajudá-lo.
Dê uma olhada http://en.wikipedia.org/wiki/Rabin-Karp_string_search_algorithm
fonte
Isso poderia ser uma solução? Não tenho certeza se é O (nlogn), mas na minha opinião é melhor que O (n²) porque a única maneira de não encontrar um triplo seria uma distribuição de números primos.
Há espaço para melhorias, o segundo encontrado 1 pode ser o próximo primeiro 1. Também não há verificação de erros.
fonte
Eu acho que esse algoritmo tem complexidade O (n log n) (C ++, DevStudio 2k5). Agora, não sei os detalhes de como analisar um algoritmo para determinar sua complexidade, por isso adicionei algumas informações de coleta de métricas ao código. O código conta o número de testes feitos na sequência de 1 e 0 para qualquer entrada (espero que eu não tenha feito as bolas do algoritmo). Podemos comparar o número real de testes com o valor O e ver se há uma correlação.
Este programa gera o número de testes para cada comprimento de cadeia com até 32 caracteres. Aqui estão os resultados:
Eu adicionei os valores 'n log n' também. Plote-os usando sua ferramenta gráfica de escolha para ver uma correlação entre os dois resultados. Essa análise se estende a todos os valores de n? Eu não sei.
fonte