Sua tarefa é resolver o problema de Subseqüência Comum Mais Longa para n seqüências de comprimento 1000.
A solução válida para o problema LCS para duas ou mais cadeias S 1 , ... S n é qualquer string T de comprimento máximo de tal forma que os personagens de T aparecer em todos S i , na mesma ordem como em T .
Note-se que T não tem que ser uma sub seqüência de S i .
Já resolvemos esse problema na menor quantidade de código . Desta vez, o tamanho não importa.
Exemplo
As cadeias axbycz
e xaybzc
têm 8 subsequências comuns de comprimento 3:
abc abz ayc ayz xbc xbz xyc xyz
Qualquer uma dessas seria uma solução válida para o problema do LCS.
Detalhes
Escreva um programa completo que resolva o problema do LCS, conforme explicado acima, cumprindo as seguintes regras:
A entrada consistirá em duas ou mais cadeias de comprimento 1000, consistindo em caracteres ASCII com pontos de código entre 0x30 e 0x3F.
Você precisa ler a entrada de STDIN.
Você tem duas opções para o formato de entrada:
Cada sequência (incluindo a última) é seguida por um avanço de linha.
As cadeias são encadeadas sem separador e sem avanço de linha à direita.
O número de seqüências de caracteres será passado como um parâmetro de linha de comando para o seu programa.
Você deve escrever a saída, ou seja, qualquer uma das soluções válidas para o LCS, para STDOUT, seguida por um avanço de linha.
Seu idioma de escolha deve ter um compilador / intérprete gratuito (como na cerveja) para o meu sistema operacional (Fedora 21).
Se você precisar de sinalizadores de compilador ou de um intérprete específico, mencione-o em sua postagem.
Pontuação
Executarei seu código com 2, 3 etc. seqüências de caracteres até que demore mais de 120 segundos para imprimir uma solução válida. Isso significa que você tem 120 segundos para cada valor de n .
A maior quantidade de strings para as quais seu código terminou no tempo é a sua pontuação.
No caso de uma pontuação empatada de n , a finalização que resolveu o problema de n strings no menor tempo será declarada a vencedora.
Todos os envios serão cronometrados na minha máquina (Intel Core i7-3770, 16 GiB de RAM, sem troca).
As n seqüências do (n-1) ésimo teste serão geradas chamando rand n
(e eliminando os feeds de linha, se solicitado), onde rand
é definido da seguinte maneira:
rand()
{
head -c$[500*$1] /dev/zero |
openssl enc -aes-128-ctr -K 0 -iv $1 |
xxd -c500 -ps |
tr 'a-f' ':-?'
}
A chave está 0
no código acima, mas reservo-me o direito de alterá-lo para um valor não revelado se suspeitar que alguém (parcialmente) codifique a saída.
fonte
public static void main(...)
?Respostas:
C, n = 3 em ~ 7 segundos
Algoritmo
O algoritmo é uma generalização direta da solução de programação dinâmica padrão para
n
seqüências. Para 2 stringsA
eB
, a recorrência padrão é assim:Para 3 cordas
A
,B
,C
eu uso:O código implementa essa lógica para valores arbitrários de
n
.Eficiência
A complexidade do meu código é O (s ^ n), com
s
o comprimento das strings. Com base no que descobri, parece que o problema está completo. Portanto, embora o algoritmo publicado seja muito ineficiente para valores maiores den
, talvez não seja possível obter um desempenho massivamente melhor. A única coisa que vi foram algumas abordagens que melhoram a eficiência de pequenos alfabetos. Como o alfabeto é moderadamente pequeno aqui (16), isso poderia levar a uma melhoria. Eu ainda prevejo que ninguém encontrará uma solução legítima que seja mais alta quen = 4
em 2 minutos e quen = 4
já pareça ambiciosa.Reduzi o uso de memória na implementação inicial, para que ele pudesse lidar
n = 4
com tempo suficiente. Mas apenas produziu o comprimento da sequência, não a própria sequência. Verifique o histórico de revisões desta postagem para ver esse código.Código
Como os loops sobre matrizes n-dimensionais exigem mais lógica do que os loops fixos, estou usando um loop fixo para a dimensão mais baixa e use apenas a lógica de loop genérica para as dimensões restantes.
Instruções de corrida
Para correr:
lcs.c
.Compile com opções de alta otimização. Eu usei:
No Linux, eu tentaria:
Execute com 2 a 4 seqüências fornecidas como argumentos de linha de comando:
Coloque aspas simples nos argumentos da linha de comando, se necessário, pois o alfabeto usado para os exemplos contém caracteres especiais do shell.
Resultados
test2.sh
etest3.sh
são as sequências de teste de Dennis. Não sei os resultados corretos, mas a saída parece pelo menos plausível.fonte
N_MAX
como uma macro e adicionar o sinalizador do compilador-std=c99
para compilar seu código com o GCC.Esta resposta está quebrada no momento devido a um erro. A corrigir em breve ...
C, 2 cordas em ~ 35 segundosEste é um trabalho em andamento (como mostra a horrível confusão), mas espero que acenda algumas boas respostas!
O código:
A função relevante que realiza todo o cálculo LCS é a função
LCS
. O código acima cronometrará sua própria chamada para esta função.Salvar como
main.c
e compilar com:gcc -Ofast main.c -o FLCS
O código pode ser executado com argumentos de linha de comando ou através do stdin. Ao usar stdin, espera-se um número de cadeias seguidas pelas próprias cadeias.
Ou:
Em uma caixa do Mac OS X com um Intel Core i7 de 1,7 Ghz e o caso de teste fornecido por Dennis, obtemos a seguinte saída para 2 strings:
A abordagem é muito semelhante à minha abordagem ao desafio anterior, aqui . Além da otimização anterior, agora verificamos um número total de caracteres compartilhados entre as strings a cada recursão e saímos mais cedo, se não houver maneira de obter uma substring mais longa do que o que já existe.
Por enquanto, ele lida com duas seqüências de caracteres, mas tende a ficar travado com mais. Mais melhorias e uma melhor explicação para vir!
fonte