Donc, cela fait partie d'une fonction que j'essaie de créer.
Je ne veux pas que le code soit trop compliqué.
J'ai une liste de mots, par exemple.
words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']
L'idée de la séquence de chaînes de mots est que le prochain mot commence par la lettre dans laquelle le dernier mot s'est terminé.
(Edit: chaque mot ne peut pas être utilisé plus d'une fois. À part cela, il n'y a pas d'autres contraintes.)
Je veux que la sortie donne la plus longue séquence de chaînes Word, qui dans ce cas est:
['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']
Je ne sais pas trop comment faire, j'ai eu plusieurs tentatives pour essayer cela. L'un d'eux...
Ce code trouve correctement la chaîne Word si nous commençons par un mot spécifique de la liste, par exemple. mots [0] (donc 'girafe'):
words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']
Word_chain = []
Word_chain.append(words[0])
for Word in words:
for char in Word[0]:
if char == Word_chain[-1][-1]:
Word_chain.append(Word)
print(Word_chain)
Sortie:
['giraffe', 'elephant', 'tiger', 'racoon']
MAIS, je veux trouver la chaîne de mots la plus longue possible (expliquée ci-dessus).
Ma méthode: Alors, j'ai essayé d'utiliser le code de travail ci-dessus que j'ai écrit et parcouru, en utilisant chaque mot de la liste comme point de départ et en trouvant la chaîne de mots pour chaque mot [0], Word [1], Word [2] etc. J'ai ensuite essayé de trouver la chaîne de mots la plus longue en utilisant une instruction if et de comparer la longueur à la chaîne la plus longue précédente, mais je ne peux pas le faire correctement et je ne sais pas vraiment. où cela va.
words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']
Word_chain = []
max_length = 0
for starting_Word_index in range(len(words) - 1):
Word_chain.append(words[starting_Word_index])
for Word in words:
for char in Word[0]:
if char == Word_chain[-1][-1]:
Word_chain.append(Word)
# Not sure
if len(Word_chain) > max_length:
final_Word_chain = Word_chain
longest = len(Word_chain)
Word_chain.clear()
print(final_Word_chain)
C’est ma nième tentative, je pense que celle-ci imprime une liste vide, j’avais eu auparavant différentes tentatives qui n’avaient pas réussi à effacer correctement la liste Word_chain et qui finissaient par répéter des mots.
Toute aide très appréciée. Espérons que je n'ai pas fait cela trop fastidieux ou déroutant ... Merci!
Vous pouvez utiliser la récursivité pour explorer chaque "branche" qui apparaît lorsque chaque lettre possible contenant le caractère initial correct est ajoutée à une liste en cours d'exécution:
words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']
def get_results(_start, _current, _seen):
if all(c in _seen for c in words if c[0] == _start[-1]):
yield _current
else:
for i in words:
if i[0] == _start[-1]:
yield from get_results(i, _current+[i], _seen+[i])
new_d = [list(get_results(i, [i], []))[0] for i in words]
final_d = max([i for i in new_d if len(i) == len(set(i))], key=len)
Sortie:
['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']
Cette solution fonctionne de manière similaire à la recherche en largeur d'abord, car la fonction get_resuls
continuera à parcourir la liste entière tant que la valeur actuelle n’a pas été appelée auparavant. Les valeurs observées par la fonction sont ajoutées au _seen
liste, finissant par mettre fin au flux d'appels récursifs.
Cette solution ignorera également les résultats contenant des doublons:
words = ['giraffe', 'elephant', 'ant', 'ning', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse',]
new_d = [list(get_results(i, [i], []))[0] for i in words]
final_d = max([i for i in new_d if len(i) == len(set(i))], key=len)
Sortie:
['ant', 'tiger', 'racoon', 'ning', 'giraffe', 'elephant']
Comme mentionné par d'autres, le problème est de trouver le le plus long chemin d'un graphe acyclique dirigé .
Pour tout ce qui concerne les graphes liés en Python, networkx est votre ami.
Vous devez simplement initialiser le graphique, ajouter les nœuds, ajouter les arêtes et lancer dag_longest_path
:
import networkx as nx
import matplotlib.pyplot as plt
words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat',
'hedgehog', 'mouse']
G = nx.DiGraph()
G.add_nodes_from(words)
for Word1 in words:
for Word2 in words:
if Word1 != Word2 and Word1[-1] == Word2[0]:
G.add_Edge(Word1, Word2)
nx.draw_networkx(G)
plt.show()
print(nx.algorithms.dag.dag_longest_path(G))
Il produit:
['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']
Remarque: cet algorithme ne fonctionne que s'il n'y a pas de cycles (boucles) dans le graphique. Cela signifie que ça va échouer avec ['ab', 'ba']
car il y aurait un chemin de longueur infinie: ['ab', 'ba', 'ab', 'ba', 'ab', 'ba', ...]
Dans l’esprit des solutions de force brute, vous pouvez vérifier toutes les permutations de la liste words
et choisir la meilleure séquence de démarrage en continu:
from itertools import permutations
def continuous_starting_sequence(words):
chain = [words[0]]
for i in range(1, len(words)):
if not words[i].startswith(words[i - 1][-1]):
break
chain.append(words[i])
return chain
words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']
best = max((continuous_starting_sequence(seq) for seq in permutations(words)), key=len)
print(best)
# ['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']
Puisque nous examinons toutes les permutations, nous savons qu'il doit y avoir une permutation qui commence par la plus grande chaîne de mots.
Ceci, bien sûr, a O (n n!) complexité temporelle: D
Cette fonction crée un type d'itérateur appelé générateur (voir: Que fait le mot-clé "rendement"? ). Il crée de manière récursive d'autres instances du même générateur pour explorer toutes les séquences de queue possibles:
words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']
def chains(words, previous_Word=None):
# Consider an empty sequence to be valid (as a "tail" or on its own):
yield []
# Remove the previous Word, if any, from consideration, both here and in any subcalls:
words = [Word for Word in words if Word != previous_Word]
# Take each remaining Word...
for each_Word in words:
# ...provided it obeys the chaining rule
if not previous_Word or each_Word.startswith(previous_Word[-1]):
# and recurse to consider all possible tail sequences that can follow this particular Word:
for tail in chains(words, previous_Word=each_Word):
# Concatenate the Word we're considering with each possible tail:
yield [each_Word] + tail
all_legal_sequences = list(chains(words)) # convert the output (an iterator) to a list
all_legal_sequences.sort(key=len) # sort the list of chains in increasing order of chain length
for seq in all_legal_sequences: print(seq)
# The last line (and hence longest chain) prints as follows:
# ['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']
Ou, pour aller directement à la plus longue chaîne plus efficacement:
print(max(chains(words), key=len)
Enfin, voici une version alternative qui autorise la répétition de mots (par exemple, si vous incluez un mot N fois, vous pouvez l’utiliser jusqu’à N fois dans la chaîne):
def chains(words, previous_Word_index=None):
yield []
if previous_Word_index is not None:
previous_letter = words[previous_Word_index][-1]
words = words[:previous_Word_index] + words[previous_Word_index + 1:]
for i, each_Word in enumerate( words ):
if previous_Word_index is None or each_Word.startswith(previous_letter):
for tail in chains(words, previous_Word_index=i):
yield [each_Word] + tail
J'ai une approche arborescente pour cette question qui pourrait être plus rapide. Je travaille toujours sur la mise en œuvre du code mais voici ce que je ferais:
1. Form a tree with the root node as first Word.
2. Form the branches if there is any Word or words that starts
with the alphabet with which this current Word ends.
3. Exhaust the entire given list based on the ending alphabet
of current Word and form the entire tree.
4. Now just find the longest path of this tree and store it.
5. Repeat steps 1 to 4 for each of the words given in the list
and print the longest path among the longest paths we got above.
J'espère que cela pourrait donner une meilleure solution au cas où une longue liste de mots serait donnée. Je mettrai à jour ceci avec l'implémentation réelle du code.
Voici une approche de force brute récursive qui fonctionne:
def brute_force(pool, last=None, so_far=None):
so_far = so_far or []
if not pool:
return so_far
candidates = []
for w in pool:
if not last or w.startswith(last):
c_so_far, c_pool = list(so_far) + [w], set(pool) - set([w])
candidates.append(brute_force(c_pool, w[-1], c_so_far))
return max(candidates, key=len, default=so_far)
>>> brute_force(words)
['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']
À chaque appel récursif, ceci essaie de continuer la chaîne avec chaque mot éligible du pool restant. Il choisit ensuite la plus longue de ces prolongations.
Une autre réponse utilisant une approche récursive:
def Word_list(w_list, remaining_list):
max_result_len=0
res = w_list
for Word_index in range(len(remaining_list)):
# if the last letter of the Word list is equal to the first letter of the Word
if w_list[-1][-1] == remaining_list[Word_index][0]:
# make copies of the lists to not alter it in the caller function
w_list_copy = w_list.copy()
remaining_list_copy = remaining_list.copy()
# removes the used Word from the remaining list
remaining_list_copy.pop(Word_index)
# append the matching Word to the new Word list
w_list_copy.append(remaining_list[Word_index])
res_aux = Word_list(w_list_copy, remaining_list_copy)
# Keep only the longest list
res = res_aux if len(res_aux) > max_result_len else res
return res
words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']
Word_list(['dog'], words)
sortie:
['dog', 'giraffe', 'elephant', 'tiger', 'racoon']
Espérons une manière plus intuitive de le faire sans récursivité. Parcourez la liste et laissez le type et la compréhension de la liste de Python se charger du travail:
words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']
def chain_longest(pivot, words):
new_words = []
new_words.append(pivot)
for Word in words:
potential_words = [i for i in words if i.startswith(pivot[-1]) and i not in new_words]
if potential_words:
next_Word = sorted(potential_words, key = lambda x: len)[0]
new_words.append(next_Word)
pivot = next_Word
else:
pass
return new_words
max([chain_longest(i, words) for i in words], key = len)
>>
['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']
Définir un pivot et vérifier pour potential_words
si elles commencent par votre mot pivot et n'apparaissent pas dans votre nouvelle liste de mots. Si trouvé, il suffit de les trier par longueur et de prendre le premier élément.
La compréhension de la liste parcourt chaque mot comme un pivot et vous renvoie la chaîne la plus longue.