Уменьшить перестановку
-
20-08-2019 - |
Вопрос
Мне нужен алгоритм, который может сопоставить серии перестановок с одним числом, а также уменьшить последующие числа.
Таким образом, серия — это последовательный набор чисел в перестановке, отсортированный и упорядоченный.В списке 1;2;3;5;6;4 есть два прогона: 1;2;3 и 5;6.Я хочу заменить их одним числом, минимум, поэтому, если после удаления серий у нас есть перестановка из 4 элементов, в перестановке используются числа 1...4.В приведенном выше примере нам нужно сократить два прогона.первый прогон будет равен 1, 4 преобразуется в 2, а [5;6] преобразуется в 3, чтобы сохранить второй критерий.Если я отсортирую уменьшенную перестановку, затем разверну элементы внутри отображения и отсортирую исходную перестановку, я получу тот же результат.В результирующей перестановке не должно быть никаких пробегов.
Например (я выделил прогоны):
(1;2;3;4;5;6) => 1 //Mappings: 1->1;2;3;4;5;6
(2;3;4);1;(5;6) => 2 1 3 // Mappings: 2->2;3;4, and 3->5;6
(3;4);(1;2);(5;6) => 2 1 3 // Mappings: 2->3;4, and 1->1;2 and 3->5;6
пока прохожу по списку и составляю список списков, группируя прогоны.На самом деле вторая часть — самая сложная часть в создании чистого решения.Я подумал о наивном подходе, любопытно, есть ли у кого-нибудь какой-нибудь хитрый трюк, который может сделать это лучше, чем мой, я примерно в O (2n + n log n),
- замена прогонов головным элементом прогона и вставка этих данных в хеш-таблицу (для возможности восстановления)
- сортировка для создания карты недостающих цифр с отсортированным индексом.[1;6;5;4] выведет [(1,1);(4,2);(5,3);(6,4)]
- замена списка на шаге 1 картой, созданной на шаге 2, и обновление хеш-таблицы для перевода.
еще раз пробежимся по примеру:
step 1: replace runs with the head element of the run and inserting data into a hash table [1;3;4;2;5;6;] -> [1;3;2;5] step 2: sort array to create map [1235], so we know that, in the previous array, 1 maps to 1, 2 to 2, 3 to 3, 4 to 5. step 3: do above translation on array from step one. [1;3;2;4]
Если я отсортирую эту перестановку и реконструирую:[1;2;3;4], 3->3;4 и 4->5;6 я получаю, 1;2;3;4;5;6.Также отсортировано.
Я использую списки, поэтому предпочтительнее использовать функциональный подход.Код не требуется.Все идеи, конечно, приветствуются.
РЕДАКТИРОВАТЬ:
мвеерден:
Мне неясно, каковы точные условия отображения.Почему именно нельзя просто создать перестановку [1,2,...,N] для перестановки с N пробегами?Кажется, вы предпочитаете сопоставлять прогон с числом из этого прогона, но (поскольку это не всегда возможно), вы, кажется, допускаете некоторую свободу.–
Я не предпочитаю сопоставлять прогон числу внутри этого прогона (см. выше!), но мне нужно сохранить заказ.Перестановка [1;2;3...N] является пробега и, таким образом, может быть сокращен еще больше.Именно поэтому оно недействительно.Порядок будет иметь значение в дальнейших операциях в другом алгоритме, но в конце отдельные элементы можно расширить, чтобы сохранить исходную перестановку.
Решение
Обозначение:
- счет начинается с 1
l.i
это элементi
спискаl
l+m
это объединение списковl
иm
- прогон — это максимальный подсписок, который является списком
[n,n+1,n+2,...,m]
для некоторыхn
иm
сn <= m
Итак, нам дана перестановка p
из списка [1,2,...,N]
.Мы разделяем p
в бегах r_1,r_2,...,r_M
такой, что p = r_1+r_2+...+r_M
.Затем мы ищем перестановку q
из [1,2,...,M]
такой, что r_(q.1)+r_(q.2)+...+r_(q.M) = [1,2,...,N]
.
Например, если p = [1,3,4,2,5,6]
, у нас есть N=6
, M=4
, r_1 = [1]
, r_2 = [3,4]
, r_3 = [2]
и r_4 = [5,6]
.В этом случае нам понадобится q
быть [1,3,2,4]
как r_1+r_3+r_2+r_4 = [1]+[2]+[3,4]+[5,6] = [1,2,3,4,5,6]
.
Обратите внимание, что q
не может содержать серии длиной более одной на определение;если бы это было так, то есть i < M
с q.i + 1 = q.(i+1)
и, следовательно, подсписок r_(q.i)+r_(q.(i+1)) = r_(q.i)+r_(q.i + 1)
из [1,2,...,N]
, но r_(q.i)+r_(q.i + 1)
также является подсписком p
что противоречит этому r_(q.i)
и r_(q.i + 1)
являются пробежками.
Теперь, чтобы найти q
учитывая p
, мы предполагаем наличие структуры данных отображения с O(1)
вставки и поиск чисел и списков с O(1)
добавляет и прямой обход.Затем делаем следующее.
Сначала мы создаем список
runheads = [r_1.1,r_2.1,...,r_M.1]
.Это можно сделать тривиально, пройдяp
сохраняя при этом текущий пробег.На этом этапе мы также запоминаем максимальное число, с которым можно получитьN
в конце и построим отображениеheads
с элементамиrunheads
как ключи.Этот шаг явноO(n)
.Обратите внимание, что не имеет значения, какие значенияheads
есть, поэтому мы можем просто использовать runr_i
как значение ключаr_i.1
.Далее мы итерируем от
1
до (и включая)N
поддержание ценностиk
с начальной стоимостью1
.Для каждого значенияi
мы проверяем, еслиi
вheads
.Если это так, мы добавляемi -> k
к отображениюf
и увеличитьk
.Этот шаг также явноO(n)
.Чтобы иметь возможность вернуться изq
кp
мы также можем сохранить дополнительное отображениеrev
сk -> i
или дажеk -> runheads(i)
без каких-либо дополнительных затрат.Наконец, мы применяем отображение
f
к элементамrunheads
получитьq
.СноваO(n)
.
Для иллюстрации примера рассмотрим случай, когда p = [1,3,4,2,5,6]
.
На первом этапе мы получаем
runheads = [1,3,2,5]
,N=6
иheads = { 1 -> [1], 3 -> [3,4], 2 -> [2], 5 -> [5,6] }
.Для второго шага у нас есть четыре случая, для которых нам нужно что-то сделать:
1
,2
,3
и5
.Для этих случаев у нас есть значения дляk
которые1
,2
,3
и4
, соответственно.Это значит, чтоf
будет{ 1 -> 1, 2 -> 2, 3 -> 3, 5 -> 4 }
.(Иrev
было бы{ 1 -> 1, 2 -> 2, 3 -> 3, 4 -> 5 }
или{ 1 -> [1], 2 -> [2], 3 -> [3,4], 4 -> [5,6] }
, в зависимости от того, что вы решили сохранить.)Получить
q
мы рассчитываемmap(f,runheads)
который[f(1),f(3),f(2),f(5)]
или, что то же самое,[1,3,2,4]
.
Итак, если я не ошибся и если структуры данных удовлетворяют вышеуказанным требованиям, то это решение должно быть O(n)
.Обратите внимание, что на практике может оказаться более полезным использовать свои собственные (O(n*log(n))
) решение.Если у вас большой p
но всего за пару прогонов, сортировка runheads
и использование этого для построения сопоставлений может быть более эффективным.Я думаю, что p
чтобы это произошло, должно быть довольно большим.
Другие советы
Отредактировано для пояснения
шаг 1:Запустите алгоритм, но вместо создания только одной хеш-таблицы создайте хеш-таблицу (D1), индексированную по заголовку набора, с которым она сопоставляется (например, для [3,4] это будет 3) и список (L1). с самим набором
[3;4;6;8;1;2]:
D1 L1
3 -> [3,4] 1 -> [3,4]
6 -> [6] 2 -> [6]
8 -> [8] 3 -> [8]
1 -> [1,2] 4 -> [1,2]
Шаг 2:Если вы посмотрите на коллекции, которые у нас сейчас есть, вы увидите, что для данного набора у нас есть индекс, в котором мы его нашли (в L1), и значение Head.Правильным значением карты будет минимальное целое число между ними, которое еще не занято.Например, для [3,4] значение должно быть между 1 и 3, а поскольку 1 уже занято, соответствующее значение равно 2.Учтите, что, поскольку D1 индексируется по значению Head, всегда будут приниматься более низкие значения, если соответствующий набор существует.В этом примере [1,2] отображается в 1, так что этот ключ уже «занят».Итак, в псевдокоде:
for (int Current = 1; Current < L1.Length; Current++)
{
GetHead(L1[Current]);
Index = Current;
While Head > Index
{
if D1.Empty(Index)
{
D1.Add(Index,D2[Current])
D1.DeleteIfNotEmpty(Head);
}
else
Index++;
}
}
Например
- мы берем первое значение в L1 -> [3,4]...
- получить голову = 3
- Начиная с 1, мы ищем D1[1], который уже занят, поэтому увеличиваем до 2.
- Мы ищем пустой D1[2], поэтому присваиваем D1[2] = [3,4] и удаляем D[3]
Это дает не O(n), а что-то вроде O(n+log(n)) (n для первого шага, log(n) для второго)
Для примера выше, который вас поймет:
1 -> [1,2]
2 -> [3,4]
3 -> [6]
4 -> [8]
Теперь, если у вас есть [3;4;8;6;1;2], это приведет к
1 -> [1,2]
2 -> [3,4]
3 -> [8]
4 -> [6]
поскольку он использует абсолютный порядок в исходном массиве, я не знаю, все ли в порядке, или вы захотите, чтобы 6 было в индексе 3, а 8 - в индексе 4, в этом случае вам, вероятно, придется предварительно заказать L1 на основе головы, которая увеличит вашу сложность на Log (n)
Если вам нужно сделать предзаказ, у вас будет 0(n+log^2(n)), что не так уж плохо (может быть, и меньше, если предположить, что QuickSort имеет O(Log n), порядок L1 будет O(log(log n))