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 4

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 принимает два слова в качестве входных данных и делает следующее:

  1. zip's a & b вместе, здесь оба рассматриваются как итеративные с каждым символом, являющимся один "элемент" в итерации. Например, если a является 'fan' и b является 'ban', то zip('fan', 'ban') производит этот список пар [('f', 'b'), ('a', 'a'), ('n', 'n')]

  2. Затем он использует функцию map, чтобы применить лямбда-функцию (причудливое название для анонимной функции) к каждому элементу в списке, полученном на первом шаге. Функция просто берет пару входных элементов (т. е. 'f' & 'b') и возвращает True , если они совпадают и False в противном случае. Для нашего примера это приведет к [False, True, True] как первая пара символов не совпадает, но обе остальные пары совпадают.

  3. Наконец, функция запускает функцию sum в списке, полученном на Шаге 2. Так получилось, что True вычисляется в 1 в Python и False в 0, и поэтому сумма нашего списка равна 2. Затем мы просто возвращаем, больше ли это число или равно 2.

Цикл for word in words просто сравнивает каждое входное слово со всеми другими словами и сохраняет те, где isChildOf оценивает в True заботясь о том, чтобы не добавлять само слово.

Я надеюсь, что это ясно!