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

Каков стандартный алгоритм для синхронизации двух списков связанных объектов?

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

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

Жесткие характеристики всех экземпляров проблемы:

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

Дополнительные характеристики в некоторых случаях проблемы:

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

Итак, как бы я обычно справлялся с этим? Какое имя алгоритма я должен Google для?

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

Здесь примерный подход:

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

Обновление 1 Спасибо за все ваши ответы! Мне нужно время, чтобы посмотреть ссылки.
[...] (текст переместился в основную часть вопроса)

Обновление 2 Перестроил средний абзац в (надеюсь) более легко анализируемые списки пули и включил детали, добавленные позже в первом обновлении.

4b9b3361

Ответ 1

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

Большинство ссылок на google на технические рефераты.

Ответ 2

2 типичных решения: 1. Скопируйте основной список в список синхронизации. 2. Проведите сравнение O (N * N) между всеми парами элементов.

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

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

Ответ 3

Часто наилучшим решением таких проблем является не решение их напрямую.

ЕСЛИ вы действительно не можете использовать сортированный бинарный контейнер для поиска в своей части кода (например, набор или даже отсортированный вектор), затем...

Вы очень привязаны к памяти? Если нет, тогда я просто создаю словарь (например, std:: set), содержащий содержимое одного из списков, а затем просто перебираю по второму, который я хочу синхронизировать с первым.

Таким образом, вы создаете nlogn для создания словаря (или nX для хеш-словаря, в зависимости от того, что будет более эффективным) + операции mlogn, чтобы переходить во второй список и синхронизировать его (или просто MY) - трудно выполнить если вам действительно нужно использовать списки в первую очередь - также хорошо, что вы делаете это только один раз, когда и если вам это нужно, и это лучше, чем сохранение списков, отсортированных все время, что было бы задачей 2 для обоих из них.

Ответ 4

В С++ STL алгоритм называется set_union. Кроме того, реализация алгоритма, вероятно, будет намного проще, если вы сделаете объединение в 3-й список.

Ответ 5

Похоже, что этот парень по имени Майкл Хейк имеет хорошее, O (n) решение. Проверьте, что сообщение в блоге для объяснения и некоторый код.

По сути, решение отслеживает как основной, так и подчиненный списки за один проход, отслеживая индексы в каждом. Затем управляются две структуры данных: список вставок, которые будут воспроизведены в списке подчиненных, и список удалений.

Это выглядит просто, а также имеет доказательство минимализма, которое Хейек последовал в последующем сообщении. Фрагмент кода в этом сообщении также более компактен:

def sync_ordered_list(a, b):
x = 0; y = 0; i = []; d = []
while (x < len(a)) or (y < len(b)):
    if y >= len(b): d.append(x); x += 1
    elif x >= len(a): i.append((y, b[y])); y += 1
    elif a[x] < b[y]: d.append(x); x += 1
    elif a[x] > b[y]: i.append((y, b[y])); y += 1
    else: x += 1; y += 1
return (i,d)

Снова, кредит Майклу Хейку.

Ответ 6

У меня была такая проблема в одном проекте в прошлом.

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

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

Каждый клиент имеет возможность обновить локальное хранилище данных до последней версии.

Ответ 7

Вот версия кода Майкла Хейка на Javascript.

    var b= [1,3,8,12,16,19,22,24,26]; // new situation
    var a = [1,2,8,9,19,22,23,26]; // previous situation
    var result = sync_ordered_lists(a,b);
console.log(result);
    function sync_ordered_lists(a,b){
// by Michael Heyeck see http://www.mlsite.net/blog/?p=2250
// a is the subject list
// b is the target list
// x is the "current position" in the subject list
// y is the "current position" in the target list
// i is the list of inserts
// d is the list of deletes
        var x = 0; 
        var y = 0; 
        var i = []; 
        var d = []; 
        var acc = {}; // object containing inserts and deletes arrays
        while (x < a.length || y < b.length) {
            if (y >= b.length){
                d.push(x); 
                x++;
            } else if (x >= a.length){ 
                i.push([y, b[y]]); 
                y++;
            } else if (a[x] < b[y]){ 
                d.push(x); 
                x++;
            } else if (a[x] > b[y]){ 
                i.push([y, b[y]]); 
                y++;
            } else { 
                x++; y++;
            }
        }
        acc.inserts = i;
        acc.deletes = d;
        return acc;
    }

Ответ 8

Очень грубая сила и чистый технический подход:

Наследовать из класса List (извините, не знаю, что такое ваш язык). Переопределите методы добавления/удаления в классе дочерних списков. Используйте свой класс вместо базового. Теперь вы можете отслеживать изменения с помощью собственных методов и синхронизировать списки в режиме онлайн.