Votre tâche consiste à résoudre le problème de la séquence la plus longue commune pour n chaînes de longueur 1000.
Une solution valable au problème LCS pour deux ou plusieurs chaînes de 1 , ... S n est une chaîne T de longueur maximale telle que les caractères de T apparaissent dans tous les S i , dans le même ordre que dans T .
Notez que T ne doit pas nécessairement être une sous- chaîne de S i .
Nous avons déjà résolu ce problème dans la quantité de code la plus courte . Cette fois, la taille n'a pas d'importance.
Exemple
Les chaînes axbycz
et xaybzc
ont 8 sous-séquences communes de longueur 3:
abc abz ayc ayz xbc xbz xyc xyz
N'importe lequel d'entre eux serait une solution valable pour le problème LCS.
Détails
Écrivez un programme complet qui résout le problème LCS, comme expliqué ci-dessus, en respectant les règles suivantes:
L'entrée consistera en deux ou plusieurs chaînes de longueur 1000, composées de caractères ASCII avec des points de code compris entre 0x30 et 0x3F.
Vous devez lire l'entrée de STDIN.
Vous avez deux choix pour le format d'entrée:
Chaque chaîne (y compris la dernière) est suivie d'un saut de ligne.
Les chaînes sont enchaînées sans séparateur ni saut de ligne arrière.
Le nombre de chaînes sera transmis en tant que paramètre de ligne de commande à votre programme.
Vous devez écrire la sortie, c'est-à-dire l'une des solutions valides du LCS, dans STDOUT, suivie d'un saut de ligne.
Votre langue de choix doit avoir un compilateur / interprète gratuit (comme dans la bière) pour mon système d'exploitation (Fedora 21).
Si vous avez besoin de drapeaux de compilation ou d'un interprète spécifique, veuillez le mentionner dans votre message.
Notation
Je vais exécuter votre code avec 2, 3, etc. chaînes jusqu'à ce qu'il prenne plus de 120 secondes pour imprimer une solution valide. Cela signifie que vous disposez de 120 secondes pour chaque valeur de n .
Le nombre le plus élevé de chaînes pour lesquelles votre code s'est terminé dans le temps est votre score.
En cas d'égalité de n , la soumission ayant résolu le problème pour n chaînes dans les plus brefs délais sera déclarée gagnante.
Toutes les soumissions seront chronométrées sur ma machine (Intel Core i7-3770, 16 Go de RAM, pas de swap).
Les n chaînes du (n-1) e test seront générées en appelant rand n
(et en supprimant les sauts de ligne, si demandé), où rand
est défini comme suit:
rand()
{
head -c$[500*$1] /dev/zero |
openssl enc -aes-128-ctr -K 0 -iv $1 |
xxd -c500 -ps |
tr 'a-f' ':-?'
}
La clé est 0
dans le code ci-dessus, mais je me réserve le droit de la changer en une valeur non divulguée si je soupçonne quelqu'un de coder en dur (partiellement) la sortie.
la source
public static void main(...)
?Réponses:
C, n = 3 en ~ 7 secondes
Algorithme
L'algorithme est une généralisation directe de la solution de programmation dynamique standard aux
n
séquences. Pour 2 chaînesA
etB
, la récurrence standard ressemble à ceci:Pour 3 cordes
A
,B
,C
je l' utilise:Le code implémente cette logique pour les valeurs arbitraires de
n
.Efficacité
La complexité de mon code est O (s ^ n), avec
s
la longueur des chaînes. D'après ce que j'ai trouvé, il semble que le problème soit NP-complet. Ainsi, bien que l'algorithme affiché soit très inefficace pour des valeurs plus grandes den
, il pourrait en fait ne pas être possible de faire beaucoup mieux. La seule chose que j'ai vue, ce sont des approches qui améliorent l'efficacité des petits alphabets. Puisque l'alphabet est modérément petit ici (16), cela pourrait conduire à une amélioration. Je prédis toujours que personne ne trouvera une solution légitime qui va plus haut qu'enn = 4
2 minutes et quin = 4
a déjà l'air ambitieuse.J'ai réduit l'utilisation de la mémoire dans l'implémentation initiale, afin qu'elle puisse gérer
n = 4
suffisamment de temps. Mais cela n'a produit que la longueur de la séquence, pas la séquence elle-même. Consultez l'historique des révisions de ce message pour voir ce code.Code
Étant donné que les boucles sur des matrices à n dimensions nécessitent plus de logique que les boucles fixes, j'utilise une boucle fixe pour la dimension la plus basse et n'utilise la logique de boucle générique que pour les dimensions restantes.
Instructions pour courir
Courir:
lcs.c
.Compilez avec des options d'optimisation élevées. J'ai utilisé:
Sous Linux, j'essaierais:
Exécutez avec 2 à 4 séquences données comme arguments de ligne de commande:
Citez les arguments de la ligne de commande si nécessaire, car l'alphabet utilisé pour les exemples contient des caractères spéciaux shell.
Résultats
test2.sh
ettest3.sh
sont les séquences de test de Dennis. Je ne connais pas les résultats corrects, mais la sortie semble au moins plausible.la source
N_MAX
une macro et ajouter l'indicateur du compilateur-std=c99
pour compiler votre code avec GCC.Cette réponse est actuellement interrompue en raison d'un bogue. Correction bientôt ...
C, 2 cordes en ~ 35 secondesC'est vraiment un travail en cours (comme le montre l'horrible désordre), mais j'espère que cela suscitera de bonnes réponses!
Le code:
La fonction pertinente qui effectue tout le calcul LCS est la fonction
LCS
. Le code ci-dessus chronomètre son propre appel à cette fonction.Enregistrez sous
main.c
et compilez avec:gcc -Ofast main.c -o FLCS
Le code peut être exécuté avec des arguments de ligne de commande ou via stdin. Lors de l'utilisation de stdin, il attend un certain nombre de chaînes suivies des chaînes elles-mêmes.
Ou:
Sur un boîtier Mac OS X avec un Intel Core i7 1,7 GHz et le scénario de test fourni par Dennis, nous obtenons la sortie suivante pour 2 chaînes:
L'approche est très similaire à mon approche du défi précédent, ici . En plus de l'optimisation précédente, nous vérifions maintenant le nombre total de caractères partagés entre les chaînes à chaque récursivité et sortons tôt s'il n'y a aucun moyen d'obtenir une sous-chaîne plus longue que celle qui existe déjà.
Pour l'instant, il gère bien 2 chaînes mais a tendance à se bloquer plus. Plus d'améliorations et une meilleure explication à venir!
la source