какой алгоритм может сделать стабильный двоичный раздел на месте всего за O (N) ходов?

Я пытаюсь понять этот документ: Стабильное минимальное разделение пространства за линейное время.

Кажется, что критическая часть утверждения состоит в том, что

Алгоритм B стабильно сортирует массив битов размером n за время O(nlog2n) и постоянное дополнительное пространство, но делает только O(n) ходов.

Однако в документе не описывается алгоритм, а только ссылка на другой документ, к которому у меня нет доступа. Я могу найти несколько способов выполнить сортировку в пределах временных границ, но мне трудно найти тот, который гарантирует O(N) ходов, не требуя при этом больше, чем постоянное пространство.

Что это за алгоритм Б? Другими словами, учитывая

boolean Predicate(Item* a);  //returns result of testing *a for some condition

существует ли функция B(Item* a, size_t N);, которая стабильно сортирует a с использованием Predicate в качестве ключа сортировки с менее чем nlog2n вызовами Predicate и выполняет только O (N) пишет a?


person AShelly    schedule 28.03.2011    source источник
comment
Из Списка методов доказательства, Доказательство по фантомной ссылке: ничего даже отдаленно напоминающий приведенную теорему, появляется в приведенной ссылке.   -  person corsiKa    schedule 29.03.2011
comment
Забавная ссылка, но не актуальная. Ссылка на стабильную сортировку на месте и минимальное перемещение данных J.I. МАНРО, В. РАМАН и др. Поиск в Google показывает, что эта пара также опубликовала несколько статей по связанным темам, включая быструю стабильную сортировку на месте с O(n) перемещениями данных. Алгоритмика 16, 151–160. Так что я думаю, что это реальная техника (по крайней мере, в теории). Но я все еще не могу найти версию, которая показывает, что это действительно практично.   -  person AShelly    schedule 29.03.2011
comment
Ах, черт возьми. Перечитывая ваш вопрос, вы правы, это не применимо. Однако интересно (и грустно) отметить, как много из них появляется в статьях и лекциях. Если бы у меня был этот список, когда я еще учился в универе, было бы гораздо интереснее.   -  person corsiKa    schedule 29.03.2011
comment
Возможно, стоит задать вопрос на cstheory.stackexchange.com.   -  person Steve    schedule 30.03.2011


Ответы (3)


Мне хочется сказать, что это невозможно. Каждый раз, когда вы вычисляете O(n log n) количество информации, но вам (1) негде ее спрятать (постоянное пространство) и (2) негде ее немедленно использовать (O(n) ходов), происходит что-то странное. включено, что может привести к интенсивному использованию стека (что может быть не включено в анализ пространства, хотя должно быть включено).

Это может быть возможно, если вы храните временную информацию во многих битах только одного целого числа или что-то в этом роде. (Таким образом, O(1) на практике, но O(log n) в теории.)

Сортировка по основанию не совсем подходит, потому что вам придется вызывать предикат для создания цифр, и если вы не запомните транзитивность сравнения, вы будете называть это O (n ^ 2) раз. (Но я думаю, что для запоминания требуется O(log n) амортизированного пространства на элемент.)

QED - Доказательство отсутствием воображения :)

person Spencer Tipping    schedule 29.03.2011
comment
Я уверен, что это потребует некоторой упаковки битов, основываясь на том, что я могу почерпнуть из статей по этому вопросу, которые я читал. Я просто пока не могу понять как. - person AShelly; 30.03.2011

Вот что у меня есть до сих пор. Версия циклической сортировки, в которой используется массив битов для хранения результатов тестов секционирования и расчета пунктов назначения на лету. Он выполняет стабильный двоичный раздел с N сравнений, ‹ N обменов и ровно 2N бит выделенного хранилища.

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 с использованием массива BitArray фиксированного размера из B бит. Есть ли способ повторно использовать те же самые биты при выполнении стабильного слияния?

person AShelly    schedule 30.03.2011

Разве это не RadixSort?

О(кН)

person fabrizioM    schedule 28.03.2011
comment
Я бы хотел, чтобы вы реализовали это с постоянными накладными расходами. Кроме того, в ОП четко упоминается использование предиката, а не целочисленного отображения. - person ltjax; 29.03.2011
comment
Сортировка по логическому предикату, по сути, является 1-битной сортировкой по основанию. Я знаю, как сделать это на месте или стабильно, но не то и другое одновременно. - person AShelly; 29.03.2011
comment
Не могли бы вы вычислить сумму префикса по всем элементам, это дает вам стабильный индекс каждого элемента после сортировки за линейное время (с линейной дополнительной памятью). тогда вам нужен какой-то умный способ замены элементов, поэтому вам нужно поменять местами каждую пару только один раз. Я думаю, это сложная часть, но, может быть, это возможно в n log n? - person zerm; 30.03.2011
comment
@zerm, на самом деле, когда у вас есть индексы, реализовать обмен O (N) легко. Посмотрите на мой ответ - вы можете реализовать getDest(i,..) как return prefixIndex[i];. Проблема, которую я вижу, заключается в хранении сумм префиксов в постоянном пространстве. Даже при идеальной битовой упаковке место для хранения 1 миллиона сумм префиксов составляет ~2,4 миллиона байт. - person AShelly; 30.03.2011
comment
@AShelly, да, я думаю, в этом проблема. Я думал о том, чтобы не хранить полный префикс, однако это приведет к чему-то вроде пузырьковой сортировки. Однако я не совсем уверен - может быть, сложность, скажем, пузырьковой сортировки можно уменьшить, когда присутствуют только двоичные данные... м-м-м... - person zerm; 31.03.2011
comment
@AShelly: я могу сделать его на месте и стабильным, но за счет времени выполнения NlgN. На самом деле, довольно красиво. Если цель состоит в том, чтобы, например. переместите все прописные буквы на передний план, а строчные — на задний, начните с сортировки пар букв. Затем, чтобы превратить две отсортированные группы из N отсортированных букв в группу из 2N отсортированных букв, обратите внимание, что если первая группа заканчивается p строчными буквами, а вторая группа начинается с q прописных букв, нужно только повернуть правильную (p+q) буквы q пробелов вправо (или p пробелов влево). Мило, а? - person supercat; 16.07.2011