したがって、これは私が作成しようとしている機能の一部です。
コードが複雑になりたくありません。
単語のリストがあります。
words = ['giraffe', 'elephant', 'ant', 'tiger', 'racoon', 'cat', 'hedgehog', 'mouse']
Wordチェーンシーケンスのアイデアは、次のWordが最後のWordが終了した文字で始まることです。
(編集:各Wordを複数回使用することはできません。それ以外の制約はありません。)
出力で最長のWordチェーンシーケンスが得られるようにします。この場合は次のとおりです。
['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']
私はそれをどうやってやるかわからない、私はこれを試す別の試みをした。それらの中の一つ...
このコードは、リストから特定のWordで始まる場合、Wordチェーンを正しく検出します。 words [0](つまり「キリン」):
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)
出力:
['giraffe', 'elephant', 'tiger', 'racoon']
しかし、私は可能な限り長い単語の連鎖を見つけたい(上記で説明)。
私の方法:ですから、リストから各Wordを開始点として使用し、各Word [0]、WordのWordチェーンを見つけて、記述してループする上記の作業コードを使用しようとしました[1]、Word [2]など。その後、ifステートメントを使用して最長のWordチェーンを検索し、その長さを以前の最長チェーンと比較しようとしましたが、適切に実行できず、実際にはわかりませんこれはどこに向かっています。
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)
これは私のn回目の試みです。これは空のリストを出力します。これまでにWord_chainリストを適切にクリアできず、繰り返し単語を繰り返してしまうさまざまな試みがありました。
助けていただければ幸いです。うまくいけば、私はこれをあまりにも面倒または混乱させないでください...ありがとう!
再帰を使用して、適切な初期文字を含む可能性のあるすべての文字が実行リストに追加されたときに出現するすべての「ブランチ」を探索できます。
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)
出力:
['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']
このソリューションは、幅優先検索と同様に機能します。関数get_resuls
は、現在の値が以前に呼び出されていない限り、リスト全体を繰り返し処理するためです。関数によって認識された値は_seen
リストに追加され、最終的に再帰呼び出しのストリームを停止します。
このソリューションでは、重複した結果も無視されます。
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)
出力:
['ant', 'tiger', 'racoon', 'ning', 'giraffe', 'elephant']
他の人が述べたように、問題は 有向非巡回グラフの最長パス を見つけることです。
Pythonに関連するグラフについては、 networkx が友達です。
グラフを初期化し、ノードを追加し、エッジを追加して、 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))
以下を出力します:
['hedgehog', 'giraffe', 'elephant', 'tiger', 'racoon']
注:このアルゴリズムは、グラフにサイクル(ループ)がない場合にのみ機能します。これは、無限の長さのパスがあるため、['ab', 'ba']
で失敗することを意味します:['ab', 'ba', 'ab', 'ba', 'ab', 'ba', ...]
ブルートフォースソリューションの精神では、words
リストのすべての順列をチェックして、最適な連続開始シーケンスを選択できます。
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']
すべての順列を検討しているため、最大のWordチェーンから始まる順列が必要であることがわかります。
もちろん、これはO(n n!)時間の複雑さ:D
この関数は、ジェネレーターと呼ばれるイテレーターのタイプを作成します(参照: 「yield」キーワードは何をしますか? )。同じジェネレーターのインスタンスを再帰的に作成して、可能なすべてのテールシーケンスを探索します。
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']
または、より効率的に最長のチェーンに直接到達するには:
print(max(chains(words), key=len)
最後に、入力で繰り返される単語を許可する代替バージョンがあります(つまり、WordをN回含めると、チェーンで最大N回使用できます)。
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
この質問に対するツリーベースのアプローチがありますが、これはより高速かもしれません。私はまだコードの実装に取り組んでいますが、ここに私がやることがあります:
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.
与えられた単語のリストが大きい場合、これがより良い解決策になることを願っています。これを実際のコード実装で更新します。
再帰的なブルートフォースアプローチが有効です。
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']
すべての再帰呼び出しで、これは残りのプールからすべての適格なWordでチェーンを継続しようとします。次に、そのような最長の継続を選択します。
再帰的アプローチを使用した別の答え:
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)
出力:
['dog', 'giraffe', 'elephant', 'tiger', 'racoon']
うまくいけば、再帰なしでそれを行うより直感的な方法。リストを反復処理し、Pythonのソートおよびリストの理解に任せてください:
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']
ピボットを設定し、ピボットWordで始まり、新しい単語リストに表示されない場合は、potential_words
を確認します。見つかった場合は、長さで並べ替えて最初の要素を取得します。
リスト内包表記は、すべてのWordをピボットとして通過し、最長のチェーンを返します。