BFS, желая найти самый длинный путь между узлами, сокращает findchildren-метод
Я открыл другую тему именно с этой темой, но я думаю, что написал слишком много кода, и я действительно не знал, где была моя проблема, теперь я думаю, что у меня есть идея получше, но все еще нуждаюсь в помощи. То, что мы имеем,-это текстовый файл с 3 буквенными словами, только 3 буквенными словами. У меня также есть слово (узел) и класс очереди. Мой findchildren-метод должен найти, для одного единственного слова, всех детей к этому слову, допустим, я ввожу "веер", тогда я должен получить что-то вроде ["Кан", "человек"....прием]. В настоящее время код выглядит следующим образом:
def findchildren(mangd,parent):
children=set()
lparent=list(parent)
mangd.remove(parent)
for word in mangd:
letters=list(word)
count=0
i=0
for a in letters:
if a==lparent[i]:
count+=1
i+=1
else:
i+=1
if count==2:
if word not in children:
children.add(word)
if i>2:
break
return children
Приведенный выше код для findchildren в настоящее время работает нормально, но, когда я использую его для других моих методов (для реализации BFS-поиска), все займет слишком много времени, поэтому я хотел бы собрать всех детей в словарь, содержащий списки с детьми. Такое чувство, что это задание мне сейчас не по зубам, но возможно ли это сделать? Я попытался создать что-то вроде это:
def findchildren2(mangd):
children=[]
for word in mangd:
lparent=list(word)
mangd.remove(word)
letters=list(word)
count=0
i=0
for a in letters:
if a==lparent[i]:
count+=1
i+=1
else:
i+=1
if count==2:
if word not in children:
children.append(word)
if i>2:
break
return children
Я полагаю, что моя последняя попытка-это просто мусор, я получаю errormessage "Set changed size using iteration".
def findchildren3(mangd,parent):
children=defaultdict(list)
lparent=list(parent)
mangd.remove(parent)
for word in mangd:
letters=list(word)
count=0
i=0
for a in letters:
if a==lparent[i]:
count+=1
i+=1
else:
i+=1
if count==2:
children[0].append(word)
if i>2:
break
return children
2 ответа:
Есть более эффективные способы сделать это (ниже O (n^2) так что не очень), но вот простой алгоритм, чтобы вы начали:
import itertools from collections import defaultdict words = ['abc', 'def', 'adf', 'adc', 'acf', 'dec'] bigrams = {k: {''.join(x) for x in itertools.permutations(k, 2)} for k in words} result = defaultdict(list) for k, v in bigrams.iteritems(): for word in words: if k == word: continue if len(bigrams[k] & bigrams[word]): result[k].append(word) print result
Производит:
defaultdict(<type 'list'>, {'abc': ['adc', 'acf'], 'acf': ['abc', 'adf', 'adc'], 'adf': ['def', 'adc', 'acf'], 'adc': ['abc', 'adf', 'acf', 'dec'], 'dec': ['def', 'adc'], 'def': ['adf', 'dec']})
Вот более эффективная версия с некоторыми комментариями:
import itertools from collections import defaultdict words = ['abc', 'def', 'adf', 'adc', 'acf', 'dec'] # Build a map of {word: {bigrams}} i.e. {'abc': {'ab', 'ba', 'bc', 'cb', 'ac', 'ca'}} bigramMap = {k: {''.join(x) for x in itertools.permutations(k, 2)} for k in words} # 'Invert' the map so it is {bigram: {words}} i.e. {'ab': {'abc', 'bad'}, 'bc': {...}} wordMap = defaultdict(set) for word, bigramSet in bigramMap.iteritems(): for bigram in bigramSet: wordMap[bigram].add(word) # Create a final map of {word: {words}} i.e. {'abc': {'abc', 'bad'}, 'bad': {'abc', 'bad'}} result = defaultdict(set) for k, v in wordMap.iteritems(): for word in v: result[word] |= v ^ {word} # Display all 'childen' of each word from the original list for word in words: print "The 'children' of word {} are {}".format(word, result[word])
Производит:
The 'children' of word abc are set(['acf', 'adc']) The 'children' of word def are set(['adf', 'dec']) The 'children' of word adf are set(['adc', 'def', 'acf']) The 'children' of word adc are set(['adf', 'abc', 'dec', 'acf']) The 'children' of word acf are set(['adf', 'abc', 'adc']) The 'children' of word dec are set(['adc', 'def'])
Решение(которое, к сожалению, является O (n^2)) для обновленного требования в Python 3 (запустите его здесь):
Из коллекции import defaultdict
words = ['fan', 'ban', 'fbn', 'ana', 'and', 'ann'] def isChildOf(a, b): return sum(map(lambda xy: xy[0] == xy[1], zip(a, b))) >= 2 result = defaultdict(set) for word in words: result[word] = {x for x in words if isChildOf(word, x) and x != word} # Display all 'childen' of each word from the original list for word in words: print("The children of word {0} are {1}".format(word, result[word]))
Производит:
Алгоритм здесь очень прост и не очень эффективен, но позвольте мне попытаться его разбить.The 'children' of word fan are set(['ban', 'fbn']) The 'children' of word ban are set(['fan']) The 'children' of word fbn are set(['fan']) The 'children' of word ana are set(['and', 'ann']) The 'children' of word and are set(['ann', 'ana']) The 'children' of word ann are set(['and', 'ana'])
Функция
isChildOf
принимает два слова в качестве входных данных и делает следующее:
zip
'sa
&b
вместе, здесь оба рассматриваются как итеративные с каждым символом, являющимся один "элемент" в итерации. Например, еслиa
является'fan'
иb
является'ban'
, тоzip('fan', 'ban')
производит этот список пар[('f', 'b'), ('a', 'a'), ('n', 'n')]
Затем он использует функцию
map
, чтобы применить лямбда-функцию (причудливое название для анонимной функции) к каждому элементу в списке, полученном на первом шаге. Функция просто берет пару входных элементов (т. е.'f'
&'b'
) и возвращаетTrue
, если они совпадают иFalse
в противном случае. Для нашего примера это приведет к[False, True, True]
как первая пара символов не совпадает, но обе остальные пары совпадают.Наконец, функция запускает функцию
sum
в списке, полученном на Шаге 2. Так получилось, чтоTrue
вычисляется в1
в Python иFalse
в0
, и поэтому сумма нашего списка равна2
. Затем мы просто возвращаем, больше ли это число или равно2
.Цикл
for word in words
просто сравнивает каждое входное слово со всеми другими словами и сохраняет те, гдеisChildOf
оценивает вTrue
заботясь о том, чтобы не добавлять само слово.Я надеюсь, что это ясно!