Пузырьковая сортировка в списке — слишком много времени тратится на вычисления

#c #c 11 #bubble-sort #stdlist

#c #c 11 #пузырьковая сортировка #стандартный список

Вопрос:

Я создал код, отвечающий за выполнение пузырьковой сортировки в списке. Кажется, что это работает, но для выполнения требуется много времени. Я был бы рад, если бы кто-нибудь сказал мне, что в этом не так, чтобы я мог избежать повторения той же ошибки в будущем

Подумал, что это может быть как-то связано с auto , но переписывание кода ничего не дало.

 void Sorting::bubblesort(std::list<int>::iterator start, std::list<int>::iterator stop)
{
    int k = 0;
    int temp;
    std::list<int>::iterator j_1 ;
    for (auto i = start; i != stop; i  )
    {
        for (auto j = std::next(start, 1); j != std::prev(stop, k); j  )
        {
            j_1= std::prev(j, 1);
            if (*j_1 > *j)
            {
                temp = *j_1;
                *j_1 = *j;
                *j = temp;
            }
        }
        k  ;
    }
}
  

Протестировано на 1000 элементах — 9032 с (измерено с помощью std:: chrono)

Комментарии:

1. Было бы лучше поместить всю программу целиком, чтобы у людей был шанс воспроизвести относительную медлительность.

2. std::prev(stop, k) имеет линейную сложность. Итак, если я не ошибаюсь в своем интуитивном анализе, временная сложность вашего алгоритма равна O(n*n*n)

3. Кажется, что это работает, но для выполнения требуется много времени — Конечно, это занимает много времени — это пузырьковая сортировка.

4. @PaulMcKenzie 1000 элементов — это ничто, 9 секунд — это вечность, это не (только) потому, что пузырьковая сортировка, а из-за способа ее реализации

5. как говорит @eerorika, вы тратите свое время на std::prev(stop, k) , около 99% на ит в -O3 , смотрите Мой ответ, чтобы иметь альтернативу

Ответ №1:

делать

 void bubblesort(std::list<int>::iterator start, std::list<int>::iterator stop)
{
    std::list<int>::iterator k = stop;
    int temp;
    std::list<int>::iterator j_1 ;
    for (auto i = start; i != stop; i  )
    {
        for (auto j = std::next(start, 1); j != k; j  )
        {
            j_1= std::prev(j, 1);
            if (*j_1 > *j)
            {
                temp = *j_1;
                *j_1 = *j;
                *j = temp;
            }
        }
        k--;
    }
}
  

чтобы не выполнять непрерывные перерасчеты std::prev(stop, k) все время, ваша программа делает почти только это

Конечно, список также не является лучшей коллекцией для хранения int и их сортировки


Полный пример :

 #include <list>
#include <iostream>
#include <chrono>
#include <ctime>

void bubblesort(std::list<int>::iterator start, std::list<int>::iterator stop)
{
#ifdef YOU
    int k = 0;
#else
    std::list<int>::iterator k = stop;
#endif
    int temp;
    std::list<int>::iterator j_1 ;
    for (auto i = start; i != stop; i  )
    {
        for (auto j = std::next(start, 1);
#ifdef YOU
             j != std::prev(stop, k);
#else
             j != k;
#endif
             j  )
        {
            j_1= std::prev(j, 1);
            if (*j_1 > *j)
            {
                temp = *j_1;
                *j_1 = *j;
                *j = temp;
            }
        }
#ifdef YOU
        k  ;
#else
        k--;
#endif
    }
}

int main()
{
  std::list<int> l;

  for (int i = 0; i != 1000;   i)
    l.push_front(i);

#ifdef DEBUG
  for (auto i : l)
    std::cout << i << ' ';
  std::cout << std::endl;
#endif


  std::chrono::time_point<std::chrono::system_clock> start, end;  

  start = std::chrono::system_clock::now();
  bubblesort(l.begin(), l.end());
  end = std::chrono::system_clock::now();
  std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count() / 1000.0
    << " sec" << std::endl;

#ifdef DEBUG
  for (auto i : l)
    std::cout << i << ' ';
  std::cout << std::endl;
#endif
}
  

Компиляция и выполнение :

 pi@raspberrypi:/tmp $ g    b.cc
pi@raspberrypi:/tmp $ ./a.out
0.183 sec
pi@raspberrypi:/tmp $ g   -DYOU b.cc
pi@raspberrypi:/tmp $ ./a.out
3.98 sec
pi@raspberrypi:/tmp $ 
pi@raspberrypi:/tmp $ g   -O3 b.cc
pi@raspberrypi:/tmp $ ./a.out
0.004 sec
pi@raspberrypi:/tmp $ g   -O3 -DYOU b.cc
pi@raspberrypi:/tmp $ ./a.out
0.413 sec
  

Обратите также внимание на преимущество компиляции в O3 …

Комментарии:

1. Спасибо, но теперь мне немного любопытно, как именно в вашем коде работают #ifdef в этом случае и флаги, которые вы добавили в команду компиляции?

2. опция -DYOU определяет ВАШ идентификатор препроцессора ( -DYOU=123 также установите его равным 123, но мне не нужно заданное значение), что позволяет мне выбирать, скомпилировать ваш код или мой, не имея двух программ / файлов. #ifdef XX является сокращением для #if defined(XX) и #if работает как if , но во время компиляции, а не во время выполнения

Ответ №2:

Это скорее дополнение к ответу Бруно, лучше попытаться отделить реализацию sort от реальных типов, например

 template <class InIt>
void bubblesort(InIt start, InIt stop)
{
    InIt k = stop, j_1;
    for (auto i = start; i != stop;   i)
    {
        for (auto j = std::next(start, 1); j != k;   j)
        {
            j_1 = std::prev(j, 1);
            if (*j_1 > *j)
            {
                /*auto temp = *j;
                *j = *j_1;
                *j_1 = temp;*/
                std::swap(*j,*j_1); 
                // - no real performance difference on GCC with -O2.
            }
        }
        --k;
    }
}