Какой алгоритм может сделать стабильное двоичное разбиение на месте только с O(N) ходами?
Я пытаюсь понять эту статью: стабильное минимальное разбиение пространства в линейном времени.
Кажется, что важной частью претензии является то, что
Алгоритм B стабильно сортирует битовый массив размером n за O (nlog 2 n) времени и постоянного дополнительного пространства, но делает только O(n) ходов.
Тем не менее, статья не описывает алгоритм, а ссылается только на другую статью, к которой у меня нет доступа. Я могу найти несколько способов сделать сортировку в пределах временного интервала, но у меня возникают проблемы с поиском того, который гарантирует O(N) перемещений, не требуя также больше, чем постоянное пространство.
Что это за алгоритм B? Другими словами, учитывая
boolean Predicate(Item* a); //returns result of testing *a for some condition
есть ли функция B(Item* a, size_t N);
который стабильно сортирует использование Предиката в качестве ключа сортировки с меньшим, чем nlog 2 n обращений к Предикату, и выполняет только O(N) записи в a?
3 ответа
Я испытываю желание сказать, что это невозможно. Каждый раз, когда вы вычисляете O(n log n) количество информации, но у вас нет (1) негде спрятать ее (постоянное пространство) и (2) негде немедленно ее использовать (O(n) перемещается), происходит нечто странное на, возможно, в связи с интенсивным использованием стека (который не может быть включен в анализ пространства, хотя это должно быть).
Это может быть возможно, если вы храните временную информацию во многих битах одного целого числа или чего-то подобного. (Таким образом, O(1) на практике, но O(log n) в теории.)
Radix-сортировка не вполне может это сделать, потому что вам нужно вызвать предикат для создания цифр, и если вы не запомните транзитивность сравнения, вы будете называть это O(n^2) раз. (Но для того, чтобы запоминать, я думаю, что O(log n) амортизированного пространства на элемент, я думаю.)
QED - доказательство отсутствия фантазии:)
Вот что у меня так далеко. Версия цикла сортировки, которая использует битовый массив для хранения результатов тестов раздела и вычисляет места назначения на лету. Он выполняет стабильный двоичный раздел с N сравнениями,
int getDest(int i, BitArray p, int nz)
{ bool b=BitArrayGet(p,i);
int below = BitArrayCount1sBelow(p,i); //1s below
return (b)?(nz+below):i-below;
}
int BinaryCycleSort(Item* a, int n, BitArray p)
{
int i, numZeros = n-BitArrayCount1sBelow(p,n);
BitArray final = BitArrayNew(n);
for (i=0;i<numZeros;i++)
if (!BitArrayGet(final,i))
{ int dest= GetDest(i,p,numZeros);
while (dest!=i)
{ SwapItem(a+i,a+dest);
BitArraySet(final,dest);
dest = getDest(dest,p,numZeros);
}
BitArraySet(final,dest);
}
return numZeros;
}
int BinaryPartition(Item* a, int n, Predicate pPred)
{
int i;
BitArray p = BitArrayNew(n);
for (i=0;i<n;i++)
if (pPred(a+i)) BitArraySet(p,i);
return BinaryCycleSort(a,n,p);
}
используя эти помощники:
typedef uint32_t BitStore;
typedef BitStore* BitArray;
BitArray BitArrayNew(int N); //returns array of N bits, all cleared
void BitArraySet(BitArray ba, int i); //sets ba[i] to 1
bool BitArrayGet(BitArray ba, int i); //returns ba[i]
int BitArrayCount1sBelow(BitArray ba, int i) //counts 1s in ba[0..i)
Очевидно, это не постоянное пространство. Но я думаю, что это может быть использовано как строительный блок для достижения конечной цели. Весь массив можно разбить на блоки N/B, используя битовый массив фиксированного размера из B битов. Есть ли способ повторно использовать те же биты при выполнении стабильного слияния?