Объединение некоторых отсортированных списков с неизвестной последовательностью заказа - PullRequest
8 голосов
/ 10 января 2011

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

Пример ввода:

  1. XS, M, L, XL
  2. S, M, XXL
  3. XXS, XS, S, L

Ожидаемый результат:

  • XXS, XS, S, M,L, XL, XXL

Ожидаемый результат получается путем сопоставления входных последовательностей для получения объединенного результата, который содержит элементы каждой входной последовательности в правильном порядке, например:

    XS   M L XL
       S M      XXL
XXS XS S   L
-------------------
XXS XS S M L XL XXL

Функция должна уведомлять, если есть элементы, которые имеют неоднозначные позиции.Здесь это будет XXL (он может остаться после M, L или XL), и мне нужно указать его положение вручную после XL (потому что здесь я знаю алгоритм сортировки и могу помочь).Я думал об определении пар из каждых двух элементов, каждая пара по порядку, как в исходном списке.Из этого можно построить полный список.

Ответы [ 4 ]

14 голосов
/ 20 июня 2013

Эту проблему можно решить с помощью алгоритма Topological Sort .

Если вы считаете, что каждая из ваших входных последовательностей представляет собой путь через ориентированный граф, топологическая сортировка упорядочит ваш набор узлов слева направо так, что каждый направленный край будет указывать вправо. Diagram of a directed graph after topological sorting

Страница Википедии по Топологическая сортировка включает этот алгоритм, впервые описанный Артуром Каном в 1962 году:

L ← Empty list that will contain the sorted elements
S ← Set of all nodes with no incoming edges
while S is non-empty do
    remove a node n from S
    insert n into L
    for each node m with an edge e from n to m do
        remove edge e from the graph
        if m has no other incoming edges then
            insert m into S
if graph has edges then
    return error (graph has at least one cycle)
else 
    return L (a topologically sorted order)

Этот алгоритм, как написано, на самом деле не дает сбоя, если находит неоднозначные последовательности, но его легко добавить, вставив проверку в начале цикла, например:

...
while S is non-empty do
    if S contains more than 1 item
        return error (inputs are ambiguous)
    remove a node n from S
    ...

Я не знаю, на каком языке вы работаете, но я собрал эту реализацию PHP в качестве доказательства концепции:

function mergeSequences($sequences, $detectAmbiguity = false) {

    // build a list of nodes, with each node recording a list of all incoming edges
    $nodes = array();
    foreach ($sequences as $seq) {
        foreach ($seq as $i => $item) {
            if (!isset($nodes[$item])) $nodes[$item] = array();
            if ($i !== 0) {
                $nodes[$item][] = $seq[$i-1];
            }
        }
    }

    // build a list of all nodes with no incoming edges
    $avail = array();
    foreach ($nodes as $item => $edges) {
        if (count($edges) == 0) {
            $avail[] = $item;
            unset($nodes[$item]);
        }
    }

    $sorted = array();
    $curr = '(start)';
    while (count($avail) > 0) {

        // optional: check for ambiguous sequence
        if ($detectAmbiguity && count($avail) > 1) {
           throw new Exception("Ambiguous sequence: {$curr} can be followed by " . join(' or ', $avail));
        }

        // get the next item and add it to the sorted list
        $curr = array_pop($avail);
        $sorted[] = $curr;

        // remove all edges from the currently selected items to all others
        foreach ($nodes as $item => $edges) {
            $nodes[$item] = array_diff($edges, array($curr));                
            if (count($nodes[$item]) == 0) {
                $avail[] = $item;
                unset($nodes[$item]);
            }
        }

    }

    if (count($nodes) > 0) {
        throw new Exception('Sequences contain conflicting information. Cannot continue after: ' . join(', ', $sorted));
    }

    return $sorted;
}

Вы можете вызвать функцию следующим образом:

$input = array(
    array('XS', 'M', 'L', 'XL'),
    array('S', 'M', 'XXL'),
    array('XXS', 'XS', 'S', 'L'),
);
echo(join(', ', mergeSequences($input)));
echo(join(', ', mergeSequences($input, true)));

Чтобы получить следующий вывод:

XXS, XS, S, M, XXL, L, XL
Uncaught exception 'Exception' with message 'Ambiguous sequence: M can be followed by L or XXL'
6 голосов
/ 20 июня 2013

Вы пытаетесь объединить частично упорядоченные наборы или наборы.Неоднозначные части слияния называются антицепями .Итак, вам нужен алгоритм, который объединяет множества и говорит вам, что такое антицепи.

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

3 голосов
/ 11 января 2011

Вот что я бы сделал:

  1. Предварительная обработка списков: выяснение того, что XXS меньше, чем XS, меньше, чем S, меньше, чем ... XXL - это [проблема удовлетворения ограничений] (http://en.wikipedia.org/wiki/Constraint_satisfaction_problem). Проблема такого типа включает поиск правильного порядка среди всех элементов с учетом ограничений, определенных в исходных списках.
  2. Создание двунаправленного отображения из набора {XXS, ..., XXL} в набор{1, ..., 6}, после того, как вы выполнили шаг 1.
  3. Для каждого списка создайте другой список, используя отображение, определенное в 2.
  4. Используйте измененный [сортировка слиянием] (http://en.wikipedia.org/wiki/Merge_sort) для объединения двух списков. Измените алгоритм объединения так, чтобы он сообщал, если два сравниваемых элемента идентичны (и не учитывает один из объединяемых элементов).
  5. Выполните шаг 4 для каждой пары списковпока не будет одного списка.
  6. Используя отображение, определенное в 2, создайте текстовую версию списка.
0 голосов
/ 27 июня 2013

Для сортировки части, я думаю, Merge Sort достаточно по вашему описанию.Одна вещь, которую нужно изменить, это объединение, мы должны пропустить элементы во входном массиве, если первый элемент входного массива совпадает с результирующим массивом.

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

'S' <'M' <'XXL' </p>

'XS' <'M' <'L' <'XL' </p>

'XXS' <'XS' <'S' <'L' </p>

четко определено.Но алгоритм все еще не знает, является ли «XXL» больше или меньше «XL», «L».
Что ж, поскольку три входных массива отсортированы, должен существовать полный порядок входных элементов.Поэтому я предлагаю попросить вашего поставщика данных упорядочить список всех возможных элементов данных.Звучит глупо, но это простой способ.

Если этот список недоступен, простой способ разобраться - запросить парную сортировку для пользователя, затем проверить, не конфликтует ли это с существующей входной последовательностью, и запомнить ее, когда алгоритм сталкивается с неоднозначной парой.,Я думаю, что сортировка топологии более мощная, чем это приложение.Поскольку мы имеем дело с единичными элементами данных, необходимо завершить общий заказ.В то время как топология сортировки имеет дело с частичным порядком.

...