Алгоритм поиска синонимов
Я думаю, что пример будет намного лучше, чем описание loooong:)
предположим у нас есть массив массивов:
("Server1", "Server_1", "Main Server", "192.168.0.3")
("Server_1", "VIP Server", "Main Server")
("Server_2", "192.168.0.4")
("192.168.0.3", "192.168.0.5")
("Server_2", "Backup")
каждая строка содержит строки, являющиеся синонимами. И в результате обработки этого массива я хочу получить следующее:
("Server1", "Server_1", "Main Server", "192.168.0.3", "VIP Server", "192.168.0.5")
("Server_2", "192.168.0.4", "Backup")
поэтому я думаю, что мне нужен своего рода рекурсивный алгоритм. Язык программирования на самом деле не имеет значения - мне нужна только небольшая помощь с идеей в целом. Я собираюсь использовать php или python.
спасибо ты!
4 ответов
эта проблема может быть сведена к проблеме в теории графов, где вы найдете все группы узлов в графе.
эффективным способом решения этой проблемы является выполнение алгоритма "заливки", который по существу является рекурсивным первым поиском дыхания. Это Википедии описывает алгоритм заливки потока и то, как он применяется для решения задачи поиска связанных областей графа.
чтобы увидеть, как исходный вопрос может быть сделан в вопрос на графиках: сделайте каждую запись (например, "Server1", "Server_1" и т. д.) узел на графике. Соединяйте узлы с ребрами, если и только если они являются синонимами. Матричная структура данных особенно подходит для отслеживания ребер при условии, что у вас достаточно памяти. В противном случае будет работать разреженная структура данных, такая как карта, тем более что количество синонимов, вероятно, будет ограничено.
- Server1-это узел #0
- Server_1-это узел #1
- Server_2 является Узел #2
затем edge[0] [1] = edge[1] [0] = 1, указано, что существует ребро между узлами #0 и #1 ( Что означает, что они являются синонимами ). В то время как edge[0][2] = edge[2][0] = 0, указывая, что Server1 и Server_2 являются не синонимы.
Анализ Сложности
создание этой структуры данных довольно эффективно, потому что одного линейного прохода с поиском сопоставления строк с номерами узлов достаточно для создания ящика он. Если вы сохраните сопоставление строк с номерами узлов в словаре, это будет шаг O(N log n).
выполнение заливки потока O (n), вы посещаете каждый узел на графике только один раз. Итак, алгоритм во всем равен O (N log n).
ввести целочисленную маркировку, которая указывает группы синонимов. На старте один отмечает все слова с разными знаками от 1
до N
.
затем поиск через вашу коллекцию, и если вы найдете два слова с индексами i
и j
являются синонимом, то замечание всех слов с пометкой i
и j
С меньшим количеством обоих. После N
итерации вы получаете все группы синонимов.
это какое-то грязное и не слишком эффективное решение, я считаю можно получить больше производительности с помощью структур union-find.
Edit: это, вероятно, не самый эффективный способ решения вашей проблемы. Если вас интересует максимальная производительность (например, если у вас миллионы значений), вам может быть интересно написать более сложный алгоритм.
PHP, кажется, работает (по крайней мере, с данными из данного примера):
$data = array(
array("Server1", "Server_1", "Main Server", "192.168.0.3"),
array("Server_1", "VIP Server", "Main Server"),
array("Server_2", "192.168.0.4"),
array("192.168.0.3", "192.168.0.5"),
array("Server_2", "Backup"),
);
do {
$foundSynonyms = false;
foreach ( $data as $firstKey => $firstValue ) {
foreach ( $data as $secondKey => $secondValue ) {
if ( $firstKey === $secondKey ) {
continue;
}
if ( array_intersect($firstValue, $secondValue) ) {
$data[$firstKey] = array_unique(array_merge($firstValue, $secondValue));
unset($data[$secondKey]);
$foundSynonyms = true;
break 2; // outer foreach
}
}
}
} while ( $foundSynonyms );
print_r($data);
выход:
Array
(
[0] => Array
(
[0] => Server1
[1] => Server_1
[2] => Main Server
[3] => 192.168.0.3
[4] => VIP Server
[6] => 192.168.0.5
)
[2] => Array
(
[0] => Server_2
[1] => 192.168.0.4
[3] => Backup
)
)
Это даст меньшую сложность, чем пример PHP (Python 3):
a = [set(("Server1", "Server_1", "Main Server", "192.168.0.3")),
set(("Server_1", "VIP Server", "Main Server")),
set(("Server_2", "192.168.0.4")),
set(("192.168.0.3", "192.168.0.5")),
set(("Server_2", "Backup"))]
b = {}
c = set()
for s in a:
full_s = s.copy()
for d in s:
if b.get(d):
full_s.update(b[d])
for d in full_s:
b[d] = full_s
c.add(frozenset(full_s))
for k,v in b.items():
fsv = frozenset(v)
if fsv in c:
print(list(fsv))
c.remove(fsv)