У нас есть ряд предметов, которые конечный пользователь сможет организовать в желаемом порядке. Набор элементов неупорядочен, но каждый элемент содержит ключ сортировки, который можно изменить.
Мы ищем алгоритм, который позволил бы генерировать новый ключ сортировки для элемента, который добавляется или перемещается в качестве первого элемента, последнего элемента или между любыми двумя элементами. Мы надеемся только изменить ключ сортировки перемещаемого элемента.
В качестве примера алгоритма каждый ключ сортировки должен иметь число с плавающей запятой, а при размещении элемента между двумя элементами установите ключ сортировки равным их среднему значению. Размещение элемента первым или последним будет иметь крайнее значение + - 1.
Проблема здесь в том, что точность с плавающей точкой может привести к сбою сортировки. Использование двух целых чисел для представления дробного числа может также привести к тому, что числа станут настолько большими, что их невозможно будет точно представить в обычных числовых типах (например, при передаче в формате JSON). Мы бы не хотели использовать BigInts.
Существует ли подходящий для этого алгоритм, который бы работал, например, с использованием строк, на которые эти недостатки не влияли бы?
Мы не стремимся поддерживать огромное количество ходов, но описанный выше алгоритм может дать сбой для числа с плавающей запятой двойной точности примерно через 50 ходов.
источник
A, B, C
-A, AA, B, C
-A, AA, AB, B, C
-A, AA, AAA, AAB, AAC, AB, AC, B, C
. Конечно, вы, вероятно, захотите разместить буквы больше, чтобы строки не росли так быстро, но это можно сделать.Ответы:
Как резюме всех комментариев и ответов:
TL; DR - Использование чисел с плавающей запятой двойной точности с первоначально предложенным алгоритмом должно быть достаточно для большинства практических (по крайней мере, вручную упорядоченных) нужд. Ведение отдельного упорядоченного списка элементов следует также рассмотреть. Другие ключевые решения несколько громоздки.
Двумя проблемными операциями являются вставка элементов в начале / конце снова и снова и многократная вставка или перемещение элементов в одну и ту же точку (например, с помощью трех элементов, многократно перемещающих третий элемент между первыми двумя, или повторное добавление новых элементов в качестве второй. элемент).
С теоретической точки зрения (т. Е. Допускается бесконечное переупорядочение), единственное решение, о котором я могу подумать, это использование двух целых чисел неограниченного размера в виде дробного числа a / b. Это допускает бесконечную точность для средних вставок, но числа могут становиться все более большими.
Строки могут поддерживать большое количество обновлений (хотя у меня все еще возникают проблемы с выяснением алгоритма для обеих операций), но не бесконечно, потому что вы не можете добавлять бесконечно много в первой позиции (по крайней мере, используя обычную сортировку строк). сравнение).
Для целых чисел потребуется выбрать начальный интервал для ключей сортировки, который ограничивает количество промежуточных вставок, которые вы можете выполнить. Если вы изначально разнесете ключи сортировки на 1024, вы можете выполнить только 10 промежуточных вставок в худшем случае до того, как у вас появятся соседние числа. Выбор большего начального интервала ограничивает количество первых / последних вставок, которые вы можете выполнить. Используя 64-битное целое число, вы ограничиваетесь ~ 63 операциями в любом случае, которые вам нужно разделить между промежуточными вставками и первыми / последними вставками априори.
Использование значений с плавающей точкой устраняет необходимость выбора пробелов априори. Алгоритм прост:
Использование плавающего типа с двойной точностью позволяет 52 промежуточных вставки в наихудшем случае и практически бесконечные (около 1e15) первые / последние вставки. На практике при перемещении элементов по всему алгоритму он должен самокорректироваться, так как каждый раз, когда вы перемещаете элемент первым или последним, он расширяет диапазон, который можно использовать.
Преимущество поплавков двойной точности также заключается в том, что они поддерживаются всеми платформами и легко сохраняются и транспортируются практически всеми транспортными форматами и библиотеками. Это было то, что мы в конечном итоге использовали.
источник
Я написал решение в TypeScript на основе резюме @ Sampo. Код можно найти ниже.
Пара идей, полученных по пути.
Только вставка в середину между двумя существующими ключами сортировки должна генерировать новый ключ сортировки, замена (то есть перестановка) не вызывает расщепления (то есть новые средние точки). Если вы перемещаете два элемента и касаетесь только одного из них, вы теряете информацию о том, какие два элемента изменили положение в списке. Даже если это было требование для начала, обратите внимание, что это хорошая идея
Каждое 1074-е деление средней точки нам нужно нормализовать диапазон с плавающей точкой. Мы обнаруживаем это, просто проверяя, удовлетворяет ли новая средняя точка инварианту
Масштабирование не имеет значения, поскольку ключи сортировки нормализованы, нормализация по-прежнему происходит при каждом
1074
разделении средней точки. Ситуация не улучшится, если мы начнем распределять цифры более широко.Нормализация сортировки ключей невероятно редка. Вы амортизируете эту стоимость до такой степени, что нормализация не будет заметна. Тем не менее, я был бы осторожен с этим подходом, если у вас есть более 1000 элементов.
источник
Там, сделал это, возможно, придется сделать это снова. Используйте строку в качестве ключа сортировки, тогда вы всегда можете найти ключ, который находится между двумя заданными ключами. Если строки становятся слишком длинными на ваш вкус, вы должны изменить несколько или все ключи сортировки.
источник
Используйте целые числа и установите ключ сортировки для начального списка равным 500 * номеру элемента. При вставке между предметами вы можете использовать среднее. Это позволит много вставок для начала
источник