#java #коллекции #big_data
Нужное решить такую задачу: Разбить множество уникальных строк на непересекающиеся группы по следующему критерию: Если две строчки имеют совпадения непустых значений в одной или более колонках, они принадлежат одной группе. Например, строчки 111;123;222 200;123;100 300;;100 все принадлежат одной группе, так как первые две строчки имеют одинаковое значение 123 во второй колонке, а две последние одинаковое значение 100 в третьей колонке Также есть ограничение на время работы программы (30 секунд). Также могу добавить количество строк - около миллиона. Вот мой код: private static Set> findLineGroups(List lines) { Set > resultSet = new TreeSet<>((Comparator >) (trSet1, trSet2) -> { int diff = trSet2.size() - trSet1.size(); if (diff != 0) return diff; Iterator iterator1 = trSet1.iterator(); Iterator iterator2 = trSet2.iterator(); while (iterator1.hasNext()) { diff = iterator1.next() - iterator2.next(); if (diff != 0) return diff; } return 0; }); Map termLineGroupsPairs = new HashMap<>(); List > lineNumGroups = new ArrayList<>(); for (int lineNum = 0; lineNum < lines.size(); lineNum++) { String line = lines.get(lineNum); String[] lineElements = line.replaceAll("\"", "").replaceAll(" ", "").split(";"); Set termSet = new HashSet<>(Arrays.asList(lineElements)); termSet.remove(""); Integer groupNum = null; TreeSet tempSet = new TreeSet<>(termLineGroupsPairs.keySet()); tempSet.retainAll(termSet); //оставляем только общие элементы if (!tempSet.isEmpty()) { String term = tempSet.first(); groupNum = termLineGroupsPairs.get(term); lineNumGroups.get(groupNum).add(lineNum); } if (groupNum == null) { TreeSet group = new TreeSet<>(); group.add(lineNum); lineNumGroups.add(group); groupNum = lineNumGroups.size() - 1; } for (String term : termSet) { termLineGroupsPairs.put(term, groupNum); } if (lineNumGroups.size() % 1000 == 0) System.out.println(lineNumGroups.size()); } resultSet.addAll(lineNumGroups); return resultSet; } И все мои решения работают слишком долго (а пробовал по-разному решать эту задачу). Правда, если строк меньше тысячи, то работает быстро (укладываюсь в указанное ограничение), и практически с любым моим алгоритмом. Прошу подсказать, как можно решить эту задачку (или что изменить в моём решении, чтобы работало быстро).
Ответы
Ответ 1
К какому решению "почти в лоб" я пришел: храним результат в виде списка списков: [номер_группы -> [строки_группы]] используем вспомогательный список хэш-таблиц: [позиция_слова -> { слово -> номер_группы }] и вспомогательную хэш-таблицу для хранения какая группа в какую была влита каждую строку разбиваем на слова каждое слово строки ищем в соответствующей (позиции слова в строке) хэш-таблице если слово есть, запоминаем номер группы (значение из хэш-таблицы), в которой оно найдено если слова нет, то добавляем его в список новых слов если строка (а точнее её слова) найдена в группах, то берём первую из "живых" (объяснение этого позже) групп, иначе создаём новую группу добавляем новые слова в соответствующие хэш-таблицы с номером найденной/созданной группы объединяем найденные группы в одну, выбранную ранее. Так как группы хранятся в виде списка строк, то просто объединяем списки строк в один у выбранной группы, а более ненужные группы отмечаем как "мёртвые" (присваиваем null, дабы не перемещать элементы внутри списка) добавляем строку в список строк группы Кода выходит ещё больше, чем слов: //вспомогательный класс для добавления новых слов private static class NewWord { public String value; public int position; public NewWord(String value, int position) { this.value = value; this.position = position; } } private static List> findGroups(List
lines) { List
Комментариев нет:
Отправить комментарий