Я уже некоторое время сталкиваюсь с этой проблемой и широко гуглюсь, но никогда не находил удовлетворительного, чистого решения. Проблема кажется достаточно простой, и решение всегда кажется заманчиво близким, но пока оно ускользает от меня.
Проблема
Рассмотрим список элементов, проиндексированных с 0, которые хранятся удаленно. Размер списка неизвестен (или не важен), и единственный способ переупорядочить элементы в списке - переместить их один за другим из исходного индекса в целевой индекс с помощью команд вида:
moveitem(from_index, to_index)
, который для списка lst концептуально эквивалентен Python:
lst.insert(to_index, lst.pop(from_index))
Задача состоит в том, чтобы написать функцию
moveitems(from_indices, to_index)
который, учитывая:
- from_indices - список индексов элементов, а
- to_index - целевой индекс
генерирует последовательность moveitem (from_index, to_index) инструкций, которые перемещают элементы с указанными индексами на целевую позицию в списке, сохраняя порядок, в котором индексы перечислены в from_indices .
Если удаленный список равен lst , то суммарный эффект вызова moveitems (from_indices, to_index) должен быть эквивалентным следующему Pythonвыражение ( expr1 ):
[x for x in lst[:to_index] if x not in from_indices] +
from_indices +
[x for x in lst[to_index:] if x not in from_indices]
или датьВ следующем определении разности списков:
diff = lambda l1, l2: [x for x in l1 if x not in l2]
желаемый результат равен ( expr2 ):
diff(lst[:to_index], from_indices) +
from_indices +
diff(lst[to_index:], from_indices)
Таким образом, если удаленный список содержал элементы:
[0,1,2,3,4,5,6,7,8,9,10,11,12, ... ]
вызов к элементам движения ([8, 2, 7, 4, 0], 6) должен преобразовать его в:
[1, 3, 5, 8, 2, 7, 4, 0, 6, 9, 10, 11, 12, ... ]
со следующим- или эквивалентная - последовательность инструкций перемещения:
moveitem(0,5)
moveitem(3,4)
moveitem(7,4)
moveitem(1,3)
moveitem(8,3)
Обратите внимание, что в приведенном выше примере элементы перемещаются в обратном порядке, они отображаются в списке индексов ( from_indices ). Это приемлемо, если в преобразованном списке сохраняется порядок, в котором элементы перечислены в from_indices .
Проблема заключается в том, как вычислить последовательность инструкций перемещения, отслеживаясписок индексов по мере их изменения при последовательном извлечении и вставке элементов.
Сортировочное решение
В настоящее время используемое мной решение выглядит следующим образом (в Python):
def moveitems(from_indices, to_index):
lst_len = max(from_indices + [to_index])+1
lst = list(range(lst_len))
last = to_index
for item in from_indices[::-1]:
source = lst.index(item)
dest = lst.index(last)
if dest > source: dest -= 1
lst.insert(dest, lst.pop(source))
last = item
moveitem(source, dest)
Он начинается с составления списка индексов lst = [0,1,2,3,4, ...] . Мы не имеем представления о длине удаленного списка, но он должен быть не меньше максимального индекса, указанного в списке from_indices , и индекса to_index .
Затем мы перебираем элементы, которые нужно переместить ( from_indices ) - каждый раз item - это элемент, который нужно переместить, а last - это ранее перемещенныйпредмет. Идея состоит в том, чтобы вытолкнуть элемент из любого места в списке и вставить его рядом с последним перемещенным элементом. Поскольку вставка списка в Python вставляет элементы ДО элемента, который в данный момент находится в точке вставки, мы перебираем from_indices в обратном порядке. Мы ищем item и last в списке lst , чтобы получить их позиции source и dest , а затемизвлеките элемент из позиции source и вставьте его перед ранее перемещенным элементом в позиции dest . Небольшая коррекция необходима, когда адресат выше источника, чтобы компенсировать смещение адресата вниз на 1, когда source выталкивается.
Это решение работает - в том смысле, что оно производитсписок, эквивалентный списку, созданному ( expr1 ) и ( expr2 ) выше, но имеет 3 основных проблемы:
- Это ужасно;
- это безобразно;и
- нецелесообразно для больших индексов.
Поскольку алгоритм строит временный список индексов до тех пор, пока максимальный интересующий индекс не будет, даже перемещение нескольких элементов в длинном списке приведет к значительным потерям пространства и времени. Просто рассмотрите что-то вроде:
moveitems([56, 12, 19], 1000000)
Задача
Я ищу алгоритм, который вычисляет необходимую последовательность инструкций moveitem напрямую, правильно отслеживая индексыпоскольку они изменяются, когда элементы перемещаются без необходимости манипулирования и многократного поиска (возможно, очень большого) временного списка индексов. Бонусные баллы, если алгоритм перемещает элементы в том же порядке, в котором они перечислены в списке from_indices .
Context
Элементы представляют документы на удаленном сервере. Пользователи могут просматривать список, выбирать случайный подсписок документов, а затем перетаскивать их на новую позицию в списке. Затем клиент должен отправить на сервер последовательность инструкций moveitem , чтобы соответствующим образом изменить порядок списка. Документы должны сохранять порядок, в котором они были выбраны (НЕ их первоначальный порядок).