Подтвердить что ты не робот

Поиск "наилучшей" комбинации для набора

У меня есть набор sentences, содержащий предложения с английского языка в виде строк. Я хочу создать подмножество sentences, sentences2, которое содержит предложения, содержащие только 20 уникальных слов. Конечно, существует много таких подмножеств, но я ищу "лучший", а "лучший" - подмножество, где все слова имеют наивысшее возможное представление в sentences2.

В следующем примере будет уточнено, что я подразумеваю под "лучшим":

Если бы я должен был фильтровать sentences для этого набора слов:

(i,you,do,think,yes,dont,can,it,good,cant,but,am,why,where,now,no,know,here,feel,are)

Я бы получил следующее:

sentences2 = set(("where are you now", "here i am", "can you do it", "yes i can", "but can i do it", "no you cant", "do you feel good", "yes i do", "why are you here", "i dont know", "i think i know why", "you dont think", "yes i do", "no you dont", "i dont think you think", "i feel good", "but i am good", "i cant do it now", "yes you can", "but i cant", "where do you think i am"))

и здесь каждое слово представляется как минимум дважды, так как мы можем видеть, используем ли счетчик предложений 2:

c = collections.Counter({'i': 13, 'you': 10, 'do': 6, 'think': 5, 'dont': 4, 'can': 4, 'good': 3, 'but': 3, 'am': 3, 'it': 3, 'cant': 3, 'yes': 3, 'know': 2, 'no': 2, 'here': 2, 'why': 2, 'feel': 2, 'are': 2, 'now': 2, 'where': 2})

Если каждое слово представлено хотя бы дважды, мы можем сказать, что этот набор из 20 слов имеет оценку 2.

score = min(c.values())

Однако следующий набор:

(i,you,he,do,think,yes,dont,can,it,good,cant,but,am,why,where,now,no,here,she,are)

имеет 5 баллов, так как если я использую его для фильтрации sentences, я получаю a sentences2, где каждое слово представляется как минимум пять раз.

Итак, я получаю наивысшую возможную оценку для всех возможных 20 комбинаций слов.

Вот моя попытка решить эту проблему:

sentences = ... # all the sentences in my text
common_words = ... # the hundred most common words in the text
result_size = 20

highest_score = 0
for sample in itertools.combinations(common_words, result_size):

    sentences2 = list(filter(lambda s: set(s).issubset(sample), sentences))
    c = Counter([j for i in sentences2 for j in i])

    if len(c.values()) and min(c.values()) > highest_score:
        # this is the set with the highest score to date
        print(c)
        highest_score = min(c.values())

Однако этот алгоритм будет навсегда рассчитывать, с 5.3598337040381E + 20 комбинациями, если я не ошибаюсь. Можете ли вы предложить, как я могу решить это с помощью гораздо более быстрого алгоритма?

Обратите внимание, что результирующий набор может содержать менее 20 слов, и это совершенно нормально. Например, c.values() в моем алгоритме не должен соответствовать размеру result_size.

Также обратите внимание, что я ожидаю, что слова в результирующем наборе будут найдены в верхней сто слов (common_words содержит 100 значений). Это также по дизайну.

4b9b3361

Ответ 1

Отказ от ответственности:. Вы не указали характеристики данных, поэтому мой ответ предполагает, что он не слишком большой (более 1 000 000 предложений, каждое не более 1000). Кроме того, описание немного сложнее, и я, возможно, не полностью понял проблему.

Решение:
Вместо того, чтобы фокусироваться на разных комбинациях, почему бы вам не создать hashMap (dict в python) для ваших 100 наиболее часто используемых слов, затем перейдите по массиву меток и для каждого слова в каждом предложении, увеличьте его соответствующее значение (если это уже внутри dict).
В конце, просто соберите этот hashMap в соответствии с количеством вхождений (значения) каждого слова (ключа), а затем используйте наиболее частые 20.
Сложность:
Быстрый взгляд на алгоритмы дает:
Перемещение N предложений, пересечение каждого с M словами, увеличение значения hashMap. в конце сортировки массива (слово, вхождения) пары. который является незначительным (размер hashMap постоянный, 100 часто используемых слов) и извлечение первого 20.
Сложность времени: O (N * M)
Сложность пространства: O (1) (нам не нужно хранить предложения, мы просто имеем hashMap)

Пример кода:
Вот быстрый psuedo-код:

word_occur_dict = {#initialized with frequent words as keys, and zero as value for all}
for sentence in sentences:    #for each sentence
    sentence_words = sentence.split(" ")    #construct the word list
    for word in sentence_words:        #for each word
        if word in word_occur_dict:         #if it is a frequent word, increase value
            word_occur_dict[word]++
final_result = sort_dict(word_occur_dict)[:20] #returns list of tuples

Код Python:

import operator

common_words = ["do","think","yes","dont","can","it","good","cant","but","am","why","where","now","no","know","here","feel","are","i","you","he","she"]
common_words_dict = {}
sentences = ["where are you now", "here i am", "can you do it", "yes i can", "but can i do it", "no you cant", "do you feel good", "yes i do", "why are you here", "i dont know", "i think i know why", "you dont think", "yes i do", "no you dont", "i dont think you think", "i feel good", "but i am good", "i cant do it now", "yes you can", "but i cant", "where do you think i am"]

for w in common_words:    #initialize the dict
    common_words_dict[w] = 0    

for sentence in sentences:    #for each sentence
    sentence_words = sentence.split(" ")    #construct the word list
    for word in sentence_words:        #for each word
        if word in common_words_dict:         #if it is a frequent word, increase value
            common_words_dict[word] = common_words_dict[word]+1

sorted_word_dict = sorted(common_words_dict.items(), key=operator.itemgetter(1))

print sorted_word_dict[::-1][:20]

Кстати, "он" и "она" нигде не фигурирует в предложениях, но вы сказали, что следующая комбинация слов имеет оценку 5

(я, ты, он, делай, думай, да, не делай, может, это, хорошо, не могу, но, да, почему, где, сейчас, нет, здесь, она есть)

Я неправильно понял проблему.

Кредит, в котором он должен: fooobar.com/questions/302/...

Ответ 2

ШАГ 1 должен состоять в создании структуры данных, в которой есть только слова в предложениях, которые появляются в common_words. Структура может также иметь количество раз, когда слово появляется, и набор целых чисел, которые ссылаются на предложения, в которых находится слово.

counts[..., {
  word:string,
  count:number,
  ids:Set<number>
}, ...]

Некоторые псевдокоды

countsMap = Map()
For i = 0 To sentences.Size - 1
  sentence = sentences[i]
  For Each word in sentence
    If Not countsMap.Contains(word) Then
      countsMap.Add(word, {word:word, count:0, ids:Set()})
    End If
    value = wordMap.Get(word)
    If Not value.ids.Contains(i) Then
      value.Count++
      value.ids.Add(i)
      countsMap[word] = value
    End If
  Next
Next
counts = countsMap.Values

Идеалистический STEP 2 Если вам повезло, и ваш тип данных counts содержит < 40 записей вы можете выполнить исчерпывающий поиск комбинаций C (n, 20) за разумное время с помощью одного компьютера C (38, 20) ~ = 33 миллиарда. Это потребует повторения комбинаций и пересечения наборов идентификаторов, окончательный размер набора - ваш минимальный балл.

Некоторые псевдокоды

bestScore = 0
bestCombo = null
For Each combo in Combinations(counts, 20)
  score = combo.Reduce((prev, curr) => prev.ids.Intersect(curr.ids)).Size
  If bestScore < score Then
    bestScore = score
    bestCombo = combo
  End If
Next

Реалистичный STEP 2 В большинстве случаев ваш счет будет содержать миллиани более 40 уникальных слов, и в этом случае вам придется довольствоваться наилучшим угаром/приближением. Я бы, наверное, сделал что-то вроде этого, используя приведенный выше код, но вместо Pick 20 используйте Pick 2, сортируйте результаты, спускающиеся по счету, и возьмите 10.

Некоторые псевдокоды

list = []
For Each combo in Combinations(counts, 2)
  score = combo[0].ids.Intersect(combo[1].ids).Size
  list.Add( { score:score, words:[ combo[0].word, combo[1].word ] } )
Next
// sort descending by score
list.Sort((a, b) => b.score - a.score)
// grab the 20 best words
result = Set()
i = 0
While result.Size < 20
  result.Add(list[i].words[0])
  result.Add(list[i].words[1])
  i = i + 1
End While

Вы получите окончательный результат больше 1? Статистически это зависит от количества уникальных слов и предложений, но, вероятно, нет.

РЕДАКТИРОВАТЬ Замечание по внедрению и исправление. Пересечение наборов идентификаторов предложений, в которых появляются слова, даст вам минимальный балл минус 1 (нулевой индекс). Например, "Собака" находится в предложениях 1 и 2; "Кошка" находится в предложениях 2 и 3 "," Лягушка "находится в предложении 4, пересечение [1,2]/\ [2,3]/\ [4] = [], но минимальная оценка - 1 результат. Размер() + 1. Точно так же "Собака" и "Кошка" [1,2]/\ [2,3] = [2] имеют заданный размер 1, но минимальный балл равен 2.

Ответ 3

Могу ли я предложить вам попробовать подход ниже. У меня нет математического доказательства того, что он создает абсолютно лучший "счет" или даже какую-то "меру добра",  но я верю, что это может помочь вам, если, конечно, дело не требует, чтобы вы действительно доказывали и находили абсолютно лучшее.

Я не говорю на python, но стратегия и последующая реализация достаточно просты, чтобы даже не требовать объяснения псевдокода. Я буду использовать довольно много слов, хотя не потому, что это очень сложно, но и быть ясным (я надеюсь).

Вам понадобится метод диагонализации матрицы или, по крайней мере, для нахождения собственного вектора, соответствующего самому большому собственному значению. Не каждый язык предоставляет для этого метод. В С++ вы можете использовать библиотеку Eigen. В С#, который я использовал для тестирования, я подключил MathNet. На питоне я понятия не имею. Требования к объекту диагонализации/собственного вектора ограничены: матрица всегда вещественна, симметрична, все элементы положительны. Однако, хотя диагональные элементы по крайней мере столь же велики, как и любой другой в их строке/столбце, матрица, конечно, не является "диагонально-доминирующей".

Задача, которую вы представляете, может быть сформулирована абстрактно для целых чисел, а не слов, что делает ее более удобной (для меня...) формулировать и реализовывать  (но в остальном это совершенно неуместно). На самом деле нам нужны только ваши "common_words", следовательно, 100 целых чисел [0..99], и вы устанавливаете один раз карту между словами и целыми числами просто путем помещения слов в массив или список и используя их индекс.

В качестве побочного примечания: стратегия, скорее всего, гораздо более адекватна для вашего языкового приложения, чем для полностью обобщенной проблемы с целыми числами, где вы можете создать "вредоносный" трудный ввод. Причина в том, что мы будем по существу использовать парные корреляции, если они есть, между элементами (слова в одном и том же предложении), тогда как доминирующие сильные корреляции для троек, четверки,... могут сделать его менее эффективным (но мы я сделаю что-то для троек). Очевидно, что на хорошо сформированном языке вы ожидаете корреляции: "я" часто появляется вместе с "я",  и в то время как "есть" могут в целом быть более частыми, чем "есть" , как только вы найдете "он", вы, скорее всего, также получите "есть" , чем "имеете" в том же предложении. И так далее.

Несколько определений в порядке.

NCommon - это количество элементов в ваших общих словах (i.c. 100). Common_words "являются" целыми числами [0..NCommon-1].

NMaxSet - это ваш предел проблемы (i.c. 20) максимальное количество слов, которые вы готовы использовать, наконец.

F - это фильтр: набор "слов" , который вы используете, чтобы определить, какие предложения включены в какую-либо коллекцию. В конце концов вы должны адаптировать свой фильтр таким образом, чтобы F.Count не превышал NMaxSet.

M (N) - квадратная матрица NxN; индексы строк и столбцов находятся в [0..N-1]

S (F) - совокупность предложений (от ввода задачи), которые удовлетворяют фильтру F. "Предложение" здесь всегда представляет собой набор целых чисел в диапазоне [0..NCommon-1], см. Выше. Дублирующие слова в том же предложении не имеют значения (описание проблемы), и такие дублирования удаляются из наших целых предложений.

Здесь мы идем.

я. Приготовление.

Инициализировать матрицу MCommon = M (NCommon). Создайте фильтр FCommon, содержащий все общие_описания.

Отфильтровать ввод, удалив повторяющиеся слова в предложениях. Это создает набор предложений S0 = S (FCommon), который является вашим "реальным" вводом: все, что не имеет значения, было удалено.

На ходу, принимая предложения в S0, заполните матрицу MCommon: {for (j в предложении): for (k в предложении): M (j, k) + = 1}. Матрица симметрична, поэтому вы можете просто заполнить верхний треугольник и зеркало в конце в нижнем треугольнике.

После завершения сканирования M [j, k] представляет собой число k-вхождений в "предложениях", которые содержат j (и наоборот: матрица симметрична). M [k, k] - общий счетчик k во всех предложениях в S вместе. M является (парной) корреляционной матрицей, рассказывая вам, насколько вероятно, что комбинация {j, k} встречается в базовых предложениях S.

(Всегда, имея дело с такими матрицами: если в конце есть пустые столбцы (и, следовательно, строки): удалите эти столбцы и строки, одновременно удаляя соответствующую запись в Фильтре, которая лежит в основе матрицы, поскольку, очевидно, она не играет роли. Впредь мы предположим, что это было сделано (если не указано иное), т.е. Ни один столбец/строка в матрице пуст.)

II. Вычислить результат (основной подход, мы уточним ниже):

Вычислить собственный вектор E MCommon, который соответствует наивысшему собственному значению: E - массив (вектор) с коэффициентами NCommon.

Установите NTarget = NSetMax

Определите, какие наибольшие коэффициенты NTarget в E. Мы не заинтересованы в их значениях, а в их индексах. Объедините эти индексы: они определяют новый фильтр F (NTarget).

Запустите S0 через новый фильтр для создания STarget. Подсчитайте все "слова" -вхождения, найдите их минимум: это ваше "заданное качество". Вы можете сделать это, например, путем вычисления соответствующего MTarget и сканирования диагональных значений MTarget [k, k]. Возможно, это связано с ненужными дополнительными усилиями, поскольку вы также вычисляете недиагональные элементы, но мы увидим, что MTarget может быть удобен в последующих уточнениях.

III. Уточнения.

A) Нам нужно проверить, удалив один или несколько элементов из фильтра F (NsetMax), уменьшив его до некоторого NTarget, меньшего, чем NSetMax, мы получим лучшее значение оценки. Это требует некоторой осторожности: очень хорошо, что удаление элементов TWO (или 3,...) улучшит оценку, но удаление любого из них ухудшит его.

Возьмите первый (и довольно разумный) выстрел по этой проблеме.

При создании STarget вы также заполняете новую матрицу MTarget (вы видите, она удобна...), как и раньше с MCommon. Размер NTarget. Получите его наибольший собственный собственный вектор. Определите МАЛЕНЬКИЙ коэффициент. Удалите из фильтра соответствующий индекс.

Фильтр снова (в качестве входных данных, конечно, теперь можно использовать коллекцию STarget) и вычислить счет. Если это лучше: отметьте/запомните. Во всех случаях (улучшение или нет) продолжаются одинаково, уменьшая фильтр один за другим до тех пор, пока у вас ничего не останется.

B) Можно ожидать, что причина, как было кратко объяснено, для "осторожного" подхода в дальнейшем уменьшении фильтра ниже NSetMax - по одному за раз - также может в некоторой степени примениться к первому шагу, на котором мы уменьшаем F (NCommon) до F (NTarget = NMaxSet) за один большой удар.

Чтобы разместить это, мы можем пойти от NCommon до NMaxSet пошагово. Чтобы не налагать слишком много вычислительных накладных расходов, мы не будем выполните шаги размером 1, но каждый раз, например, сокращение на 10% или что-то подобное.

Итак, в разделе II выше, не устанавливайте NTarget немедленно в NSetMax, но (например) установите NTarget = 90. Построить соответствующий фильтр, применить фильтр к S0, дать S1, произвести также матрицу M1, получить свой собственный вектор (наибольшее собственное значение). Повторите: установите NTarget = 80, 70, 60 и так далее. На более поздних этапах вы можете стать более осторожными, опустив от 40 до 35-30 до 28... На каждом шаге вы строите и используете результаты предыдущего, чтобы оптимально использовать уменьшение размера и вычислительных усилий.

Все время, когда вы хотите контролировать, имеют ли наибольшие коэффициенты NSetMax (конечное значение в этой части алгоритма) всегда одни и те же индексы или нет. Это даст некоторую информацию о том, является ли конечный результат надежным: стабильность или нет ожидаемого финального фильтра против алгоритмического пути к нему.

C) Рассмотрим ситуацию, в которой мы привели к NSetMax, и исследуем, следует ли ее еще больше сократить в одноразовом подходе. (То же самое может применяться и на заключительных этапах (B), если при приближении к NSetMax сверху вы начинаете "один раз в то время", как только приближаетесь к NSetMax.)

Предположим, что на этой фазе алгоритма в вашей (первой или более поздней) коллекции STarget,  некоторые ПАРЫ "слов" , так что удаление такой конкретной пары из фильтра улучшит ситуацию,  в то время как ни один из их индивидуальных коэффициентов в собственном векторе не является наименьшим. Я не уверен, насколько это возможно или даже возможно, но посмотрим, как мы можем справиться с этим. Если (ваши) тесты показывают, что это не имеет значения, вы можете в финальной реализации удалить эту функцию из алгоритма.

Предположим, что у нас есть NTarget, связанный фильтр FTarget и матричный MTarget. (Порядок элементов ( "слов" ) в фильтре всегда равен (конечно) порядку строк и столбцов в матрице.)

Из MTarget мы можем непосредственно вывести некоторую информацию о том, что произойдет, если мы должны удалить j-й элемент из фильтра. Конечно, j-я строка и j-й столбец в матрице становятся пустыми (все ноль). Более интересно, что M [j, k] показывает, сколько раз элемент k встречается во всех предложениях, которые содержат элемент j вместе. Поэтому, устраняя все j (удалив его из фильтра), мы заранее знаем, что в полученной новой матрице MReducted,  элемент MReduced [k, k] будет точно уменьшаться из этого значения M [j, k]. (Кстати, это дает альтернативный способ определения того, какой элемент удалить (если вы решите удалить только один):  минимальный {j: M [j, j]} - это оценка связанного множества S, а из M мы можем вычислить, как изменились бы все диагональные элементы при удалении определенного элемента j, что позволило бы предварительно вычислить полученный результат)

Здесь, однако, мы хотели бы знать, как повлияет оценка на удаление некоторой PAIR элементов, j и k. Теперь нам нужно определить, как M [p, p] влияет на все p, которые не являются j или k. Это, вы не можете напрямую вычислить из M:  удаление j влияет на строку k, и, когда мы знаем, как она изменяется [k.k], и любой другой [p, p], мы не знаем, как это изменится [k, p],  который необходим для вычисления того, как ALSO ('впоследствии') удаление k изменится [p, p]. Обратите внимание, кстати, что для окончательного результата должно быть несущественно, удаляете ли вы сначала j, а затем k или наоборот, или оба одновременно.

Для этого нам нужна какая-то "производная". К счастью, мы можем вычислить и обработать это без слишком больших вычислительных усилий (учитывая, что NTarget уже довольно мал, около 20).

Рассмотрим "восстановительный фильтр" G (F; j), связанный с текущим фильтром F, который определяется просто как обработка F, но игнорируя в нем элемент j. С G мы так же, как и всегда, вычисляем "редукционную матрицу" N (G), где для удобства обсуждения (и реализации) мы сохраняем один и тот же размер (не удаляя пустой столбец/строку). Конечно, в такой N-матрице j-я строка и j-й столбец пусты. Его диагональные элементы будут иметь значения, как описано выше (мы могли бы вычислить их непосредственно из M). Однако теперь у нас также есть все недиагональные элементы, которые возникают при удалении j.

Из N (G {F; j}) мы теперь можем определить, что произойдет, если мы удалим ALSO элемент k, см. выше вышесказанное о том, как получить ожидаемый результат от текущей матрицы. Таким образом, это позволяет вычислять счет при удалении пары {j, k}.

Если наборы параметров равны 20, мы должны вычислить 20 N (G (F; j)) матриц, относительно небольшое усилие, я полагаю (ваши предложения-коллекции также к настоящему времени стали намного меньше, чем первоначально). Затем, имея все N, вычислите для каждой из 20 * 19/2 уникальных пар результирующие пар-удаленные оценки, и вы в состоянии выберите PAIR (а не отдельный элемент) для удаления из фильтра. Вы можете сравнить это, на лету, с сокращением на "один раз в то время" и сделать правильный выбор, как систематически уменьшить фильтр в поисках лучшего результата. Есть много способов обойти это сравнение. Относительно простым было бы: рассчитать сокращение от первой пары, а затем одного (всего 3 элемента).  Сравните с первым удалением одного, а затем пары. Выберите лучший из этих двух, но сделайте из этого выбора только первый шаг (один или пару) и повторите процедуру.

Обратите внимание, что с использованием этих "полученных" фильтров G, матриц N и последующих предварительных вычислений, как объяснено,  вы эффективно вносите корреляции между ТРИПЛАМИ пунктов: вы определяете, что происходит для всех {j, k, p}, до частоты p при удалении как j, так и k. Разумеется, эту идею можно расширить, чтобы включить четверки и за ее пределы, но (а) я не верю, что это практически поможет, и (б) чем дальше вы продвигаетесь по этому пути, тем быстрее будут увеличиваться вычислительные усилия, Я даже сомневаюсь в том, что включение в "тройки", как объяснено здесь,  но я не эксперт по языку и, кроме небольшого дополнительного усилия по программированию, не имеет большого недостатка.

D). Основу стратегии полагается на собственный вектор с наибольшим собственным значением, чтобы указать на соответствующие элементы для последующего фильтрации. Конечно, может случиться, что два или более собственных значения "почти" одинаковы, а соответствующие собственные векторы могут указывать на совершенно разные наборы элементов  при анализе их крупнейших компонентов. В этом случае целесообразно "разветкить", то есть: пойти с одним из них, работать до конца, а потом переделайте все со своим конкурентом (-ами) и посмотрите, дают ли они лучший результат. Проблема возникает здесь, если вы сталкиваетесь с множеством веток (в разных точках пути к решению). Это вряд ли произойдет, но реализация должна иметь некоторую стратегию для решения этой проблемы на практике, если это произойдет. Я предлагаю вам сначала оставить любое разветвление, но вы контролируете появление "конкурентоспособных" самых больших собственных значений, чтобы выводить предупреждение пользователю.  В качестве альтернативы вы можете реализовать обработку такого разветвления, но будьте очень строги в отношении того, что программа будет считать "почти равной", или поставит некоторый предел, как к числу ветвей, которые (в целом) подлежат исследованию, тем самым уменьшая вероятность того, что время вычисления закончится.

Мне нужно оставить его здесь (из-за нехватки времени...), надеюсь, что я достаточно подробно объяснил эту идею, а также некоторые детали и уточнения, которые нужно рассмотреть.

У меня не было времени, чтобы организовать для себя соответствующие предложения "реального языка" в качестве входных данных и тестов против этого. Я запрограммировал основные шаги на С#, проверенные на случайные целые числа ", с некоторыми уклонами, чтобы заставить определенные" слова "встречаться чаще, чем другие,  но не беспокоиться о корреляциях между словами в рамках "предложений". Результаты оказались вполне разумными для меня, но у меня не было времени для его всестороннего анализа.

Учитывая отсутствие структуры в "случайных" последовательностях, которые я использовал в качестве входных данных, в то время как реальный язык, как ожидается, проявит значительные парные корреляции,  которые стратегия использует,  У меня есть хорошая надежда, что вам будет полезно посмотреть.

[EDITED] Примечание добавлено: в приведенном выше случае я иногда небрежно говорил о "j", "k" и т.д. Я сожалею о том, что. Иногда "j" ссылается на j-ю запись чего-либо (строка Matrix, индекс в списке фильтров), иногда она ссылается на соответствующее VALUE в (обычно) на фильтр. Например, фильтр может содержать 18 элементов, пронумерованных (индексов) 0..17, но их VALUES всегда ссылаются на исходный список Common_words,  поэтому они могут быть {3, 15, 29, 30, 31, 40,...}. Затем, когда он говорит "удалить j из фильтра", обычно это означает "удалить j-ю запись из фильтра (и эта запись может иметь любое значение из [0..NCommon-1]). При применении фильтра к предложению вы сравниваете значения VALUES в фильтре со значениями в предложении. Я надеюсь, что контекст - в сочетании с честным пониманием линии рассуждений - всегда дает понять, что на самом деле означает.

[EDITED: некоторые результаты испытаний] Я выполнил свою реализацию С#, используя приведенный выше алгоритм (более или менее: большинство, но не все уточнения/подробности), чтобы получить какое-то представление о том, что он будет производить.

Для ввода я взял 4 книги (обычный текст) из проекта Гутенберга. Всего (всего) 27 тыс. Предложений, 380 тыс. Слов (20 тыс. Раз), поэтому довольно небольшой образец.

Список общих слов, определяемых с этого ввода, начинался с "the", "of", "and" и "... (при сортировке по частоте появления в общем вводе).

Алгоритм отфильтровал 14 слов ( "i", "what", "do", "you", "it", "yes",...), чтобы создать "оптимальное" значение "set-quality-value", из 8 (139 предложений были найдены только с этими 14 словами).

Поскольку я был подозрительным в предположении, что нужно использовать всего лишь 100 "общих слов", я имел априори, допустив 500 общих слов, а не 100, и, конечно же, среди слов, которые попали в последний фильтр, 4 ( "да", "знать" ): "да", например, было № 224 в общем списке, если вы должны сортировать их по вхождению во всех вводах, предположительно, базу для "общих слов".

Ответ 4

Я не уверен, возможно ли придумать лучшее решение в менее чем экспоненциальном времени, проблема может не иметь достаточной структуры. Но здесь есть эвристика, чтобы придумать "хорошее" решение.

Я думаю, что способ сделать это - начать с wordset с размером 0 и добавить к нему слова один за другим "умным" способом с максимумом 20. Рассмотрим, что для данного wordset_n оценка для каждого отдельного слова может только увеличиваться или оставаться неизменной при добавлении нового слова. Единственный способ, которым wordset_(n+1) может иметь более низкий балл, чем wordset_n, - это то, что (n + 1) -е слово сбивает минимум. Таким образом, мы ограничиваемся только добавлением слов, которые приносят минимум вверх или сохраняют его одинаково (но см. Описание ниже).

Итак, в первом приближении

  • Сортировка слов в common_words по частоте в sentences.
  • Добавьте наиболее часто встречающиеся слова в wordset, пока оценка не будет отличной от нуля.
  • Искать дерево возможного wordset, созданное только добавлением слов, которые увеличивают или поддерживают оценку предыдущего node (max N = 20). Таким образом, путь вниз по этому дереву будет wordset_n, wordset_(n+1), wordset_ (n + 2) `с каждым node, состоящим из предыдущего node плюс одно новое слово.
  • Выберите лист node с наивысшим результатом.

Во втором приближении: а) вы можете попробовать несколько комбинаций для шага 2. 100 выбрали 3 - 162000, о котором не может быть и речи. б) Кроме того, для шага 3 вам может потребоваться выполнить два шага, а не только один - то есть, только отклонить слово для пятна n + 1 в wordset, если после всех возможностей для слова n + 2 оценка все еще ниже wordset_n. Это может помочь, если есть антикорреляции, т.е. Короткое предложение с одним глаголом вряд ли будет содержать другое. Наконец, c) если этот метод по-прежнему является чрезмерно запрещенным, вы можете еще больше ограничить конструкцию дерева, закрыв ветки, где (n + 1) -й не повышает оценку на заданную величину.

Если распределения слов в sentences относительно независимы друг от друга или имеют только положительные корреляции, этот метод может дать вам что-то довольно близкое к оптимальному.

Ответ 5

Я бы искал форму кластеризации данных, чтобы ускорить поиск в каждом из 20 пробных слов.

Данные, которые имеют значение, являются уникальными словами в предложении.

2 предложения можно считать близкими, если расстояние jaccard мало. Расстояние jaccard равно 1 - (размер (пересечение слов в предложениях))/(размер (объединение слов в предложениях)).

Поскольку расстояние jaccard является метрическим (соответствует неравенству треугольника), можно построить индекс m-tree, который позволяет быстрее находить предложения.

Из этой базы может возникнуть кластеризация (наилучшие результаты будут близки друг к другу).

Наконец, повторив каждое предложение и найдя ближайших соседей K, вы можете найти набор из 20 слов, которые могут быть использованы. Это даст другое значение для K (предложения, которые содержат лучшие 20 слов).

Я бы, вероятно, использовал базу данных для поддержки этих select union и select intersection, позволяющих устанавливать тестирование

Ответ 6

NP-hard путем сокращения от CLIQUE (при условии, что мы заменим 20 параметром). Учитывая график, в котором мы ищем k-clique, присвойте каждой вершине уникальное слово, сделайте предложение из двух слов, соответствующее каждому ребру, и попытайтесь выбрать k, выберем 2 предложения, которые включают каждое слово k - 1 раз.

Нужно подумать о том, существует ли алгоритм с разумной параметризованной сложностью.