Использование рекурсивной сортировки по основанию в параллельной сортировке по ведру

Я пытаюсь написать быстрый алгоритм для сортировки вектора из большого количества целых чисел, например:

159 14 5 97 6 54

до сих пор моя программа разбивает вектор на небольшие сегменты с помощью MSD, например:

bucket[1]:159 14
bucket[5]:5 54
bucket[6]:6
bucket[9]:97

и теперь мне нужно использовать рекурсивную сортировку по основанию для сортировки ведра в порядке старших цифр:

bucket[1]:14 159
bucket[5]:5 54
bucket[6]:6
bucket[9]:97

Это рекурсивный код системы счисления, который я нашел в Интернете:

// Sort 'size' number of integers starting at 'input' according to the 'digit'th digit
// For the parameter 'digit', 0 denotes the least significant digit and increases as significance does
void radixSort(int* input, int size, int digit){
  if (size == 0)
    return;

  int[10] buckets;    // assuming decimal numbers

  // Sort the array in place while keeping track of bucket starting indices.
  // If bucket[i] is meant to be empty (no numbers with i at the specified digit),
  // then let bucket[i+1] = bucket[i]

  for (int i = 0; i < 10; ++i)
  {
    radixSort(input + buckets[i], buckets[i+1] - buckets[i], digit+1);
  }
}

Я не знаю, как реализовать этот бит в моем коде, я не уверен в том, что bucket [] делает в приведенном выше коде. Может ли кто-нибудь объяснить, какие изменения мне следует внести? Вот многопоточный код, который я использую, который не работает, так как я не использую рекурсивный.

void sort(unsigned int numCores, std::vector<unsigned int> numbersToSort){
// ******************Stage 1****************
// Use multithread to seperate numbers into buckets using the most significant digits
  auto smallbuckets = std::vector<std::shared_ptr<std::vector<std::vector<unsigned int>>>>();
  std::mutex mutex;

  unsigned int workload = numbersToSort.size() / numCores;

  std::function<void(unsigned int, unsigned int, unsigned int)> put_small_buckets;
  put_small_buckets = [this, &smallbuckets, &mutex]
(unsigned int id, unsigned int start, unsigned int end) {

    auto buckets = std::make_shared<std::vector<std::vector<unsigned int>>>(std::vector<std::vector<unsigned int>>());
    for (int j = 0; j < 10; ++j) {
        buckets->push_back(std::vector<unsigned int>());
    }

    for (unsigned int i = start; i < end; ++i) {
        unsigned int a = numbersToSort[i];
        std::string tmp = std::to_string(a);
        char c = tmp.at(0);
        int ia = c - '0';
        (*buckets)[ia].push_back(numbersToSort[i]);
    }
    std::lock_guard<std::mutex> lock(mutex);
    smallbuckets.push_back(buckets);
  };

// create a container of threads
  std::vector<std::shared_ptr<std::thread>> containerOfThreads;

// create threads and add them to the container.
  for (unsigned int i = 0; i < numCores; ++i) {
    // start the thread.
    unsigned int start = workload * i;
    unsigned int end = workload * (i + 1);
    if(i == numCores - 1) end = this->numbersToSort.size() ;
    containerOfThreads.push_back(std::make_shared<std::thread>(put_small_buckets, i, start, end));
  }

// join all the threads back together.
  for (auto t : containerOfThreads) t->join();

  numbersToSort.clear();
// ******************Stage 2****************
// Put small multithreaded buckets back to the bucket of radix(10)

  auto bigbuckets = std::vector<std::shared_ptr<std::vector<unsigned int>>>();
  for (int j = 0; j < 10; ++j) {
    bigbuckets.push_back(std::make_shared<std::vector<unsigned int>>(std::vector<unsigned int>()));
  }

int current_index = 10;

std::function<void()> collect;
collect = [this, &smallbuckets, &current_index, &mutex, &collect, &bigbuckets] () {
    mutex.lock();
    int index = --current_index;
    mutex.unlock();
    if (index < 0) return;
    auto mybucket = bigbuckets[index];
    for (auto i = smallbuckets.begin(); i != smallbuckets.end(); ++i) {
        mybucket->insert(mybucket->end(), (*(*i))[index].begin(), (*(*i))[index].end());
    }
    collect();
  };

// create a container of threads
  containerOfThreads.clear();

// create threads and add them to the container.
  for (unsigned int i = 0; i < numCores; ++i) {
    containerOfThreads.push_back(std::make_shared<std::thread>(collect));
  }

// join all the threads back together.
  for (auto t : containerOfThreads) t->join();

// ******************Stage 3****************
// Sort big buckets

  for (int j = 0; j < 10; ++j) {
    bigbuckets.push_back(std::make_shared<std::vector<unsigned int>>(std::vector<unsigned int>()));
  }
  std::function<void(unsigned int, unsigned int)> sort_big_buckets;
  sort_big_buckets = [this, &bigbuckets, &mutex]
  (unsigned int start, unsigned int end) {
    unsigned int curr = start;
    while(curr < end){

        auto mybucket = bigbuckets[curr];
        std::sort(mybucket->begin(),mybucket->end(), [](const unsigned int& x, const unsigned int& y){
            std::string tmp1 = std::to_string(x);
            std::string tmp2 = std::to_string(y);
            return lexicographical_compare(tmp1.begin(), tmp1.end(), tmp2.begin(), tmp2.end());
            //return aLessB(x,y,0);
        } );
        ++curr;
    }
  };
// create a container of threads
  containerOfThreads.clear();

  workload = 10 / numCores;
// create threads and add them to the container.
  for (unsigned int i = 0; i < numCores; ++i) {
    // start the thread.
    unsigned int start = workload * i;
    unsigned int end = workload * (i + 1);
    if(i == numCores - 1) end = 10 ;
    containerOfThreads.push_back(std::make_shared<std::thread>(sort_big_buckets, start, end));
  }

// join all the threads back together.
  for (auto t : containerOfThreads) t->join();
// put all elements back to numbersToSort
  for (auto i = bigbuckets.begin(); i != bigbuckets.end(); ++i) {
    numbersToSort.insert(numbersToSort.end(), (*i)->begin(), (*i)->end());
  }
}

person SuperMurloc    schedule 25.10.2016    source источник
comment
Не связано с вашим вопросом, но почему у вас есть вектор (я предполагаю) общих указателей на std::thread? Вы передаете этот вектор? Будете ли вы делать с обсуждениями больше, чем просто присоединяться к ним? Почему не просто вектор из std::thread объектов? Затем вы можете использовать emplace_back для создания потоков.   -  person Some programmer dude    schedule 25.10.2016
comment
Что касается вашей проблемы, что такое bigbuckets? Что содержит bigbuckets? Не могли бы вы создать минимальный, полный и проверяемый пример и показать нам?   -  person Some programmer dude    schedule 25.10.2016
comment
@Someprogrammerdude Я не передаю вектор, и я использую его, потому что он используется в одном из моих примеров из учебника. Я попробую упомянутый вами метод, спасибо за совет! Также я отредактировал пример с тем, что я сделал, пытаясь приблизиться к сортировке.   -  person SuperMurloc    schedule 25.10.2016
comment
Вы действительно хотите отсортировать целые числа в лексикографическом порядке их строкового представления? Между тем, ваш код не работает, потому что вы используете to_string для каждого сравнения, сегментации и множества других распределений временной памяти. Если вам действительно нужен такой порядок, вам лучше заранее преобразовать все целые числа в строки и сделать 11 ведер на каждую позицию цифры, помещая числа, которые недостаточно длинные, в дополнительную корзину.   -  person Alexander Anikin    schedule 25.10.2016
comment
@AlexanderAnikin Да, я хочу, чтобы они были в порядке старших цифр, что в значительной степени является лексикографическим порядком. Можете ли вы объяснить, что вы подразумеваете под недостаточными числами?   -  person SuperMurloc    schedule 25.10.2016
comment
Сравнение полных строк излишне для поразрядной сортировки. Вам нужно проверять только 1 цифру на сегмент на глубину каждой цифры. Но если строка 1 переходит в сегмент уровня 1, вы не должны обращаться к ее второй (индекс 1) цифре. Вместо этого вам следует проверить длину строки и отложить числа короче текущего уровня. Такие числа образуют еще одну корзину, но в дальнейшей сортировке она не нуждается.   -  person Alexander Anikin    schedule 25.10.2016


Ответы (1)


Я не знаю, как реализовать этот бит в моем коде, я не уверен в том, что bucket [] делает в приведенном выше коде. Может ли кто-нибудь объяснить, какие изменения мне следует внести?

Если честно, buckets [] не нужен. Идея состоит в том, чтобы сохранить здесь индексы начала сегментов, но поскольку более поздние сегменты обрабатываются в том же порядке один за другим, можно использовать пару дополнительных переменных вместо этого массива.

Как я уже сказал, вам следует преобразовывать числа в строки и сортировать строки. Таким образом, вы сможете проверять 1 символ для каждого сегмента, не выполняя все операции create-string-> compare-> destroy-string. В конце концов, вам придется преобразовать строки обратно в числа.

Запрашиваемая часть кода может выглядеть так:

void radixSort(std::vector<std::string>::iterator begin, std::vector<std::string>::iterator end, int digit){
    if (begin == end)
        return;

    // first skip short numbers
    e = begin;
    for (auto p = begin; p != end; ++p)
        if (p->size() <= digit)
        {
            if (p != e)
                std::swap(*p, *e);
            q++;
        }
    if (e == end)
        return;

    for (char d = '0'; d <= '9'; ++d)
    {
        auto s = e;
        for (auto p = e; p != end; ++p)
            if (p->at(digit) == d)
            {
                if (p != e)
                    std::swap(*p, *e);
                e++;
            }
        radixSort(s, e, digit+1);
    }
}

Чтобы отсортировать вектор строки, вы можете сделать что-то вроде этого:

radixSort(v.begin(), v.end(), 0);
person Alexander Anikin    schedule 25.10.2016