niromru

Untitled

Jun 19th, 2022
801
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 16.61 KB | None | 0 0
  1. #pragma once
  2. #include <stdlib.h>
  3.  
  4. #include "common.h"
  5.  
  6. typedef struct {    // последовательность
  7.     int* beg;   // указатель на начало последовательности
  8.     int len;    // длина последовательности
  9. }segment;
  10.  
  11. int get_min_size(int n) // вычисляем минимальный размер подмассива, на который будет разделён массив
  12. {
  13.     int r = 0;  // принимает значение 1, если среди сдвинутых битов была хотя-бы одна единица, 0 в обратном случае
  14.     while (n >= 64) {
  15.         n >>= 1;    // сдвигаем биты числа вправо на 1 позицию
  16.         r |= n & 1; // если n - нечётно (среди сдвинутых битов была единица)
  17.     }
  18.     return n + r;   /* размер подмассива от 32 до 64 включ. (определен как оптимальный автором алгоритма),
  19.     при делении числа элементов массива на это число получается число-степень двойки */
  20. }
  21.  
  22. void reverse(int* a, int n) // перезаписываем массив в обратном порядке
  23. {
  24.     int i, j, tmp;  // итераторы цикла for, вспомогательный элемент
  25.     for (i = 0, j = n - 1; i < j; i++, j--)
  26.         if (a[i] != a[j])   // нет смысла переставлять равные элементы
  27.         {
  28.             // меняем элементы массива местами друг с другом
  29.             tmp = a[i];
  30.             a[i] = a[j];
  31.             a[j] = tmp;
  32.         }
  33. }
  34.  
  35. void insertion_sort(int* a, int n, int i)   // сортировка вставками. Так как на вход подаются почти упорядоченные последовательности малых размеров, она эффективна.
  36. {
  37.     int j, x;   // итератор цикла for для перебора элементов массива, x - рассматриваемый элемент
  38.     for (; i < n; ++i)  // цикл по элементам массива
  39.     {
  40.         x = a[i];   // задаем рассматриваемый элемент массива
  41.         for (j = i; j && a[j - 1] > x; --j) // перебираем элементы массива в обратном порядке пока они меньше текущего и не выбрались за границы массива
  42.             a[j] = a[j - 1];    // сдвигаем элементы
  43.         a[j] = x;   // как только встретился элемент, больший рассматриваемого, вставляем перед ним рассматриваемый элемент
  44.     }
  45. }
  46.  
  47. void merge_copy_less(segment* seg)  // слияние последовательностей
  48. {
  49.     int* a = seg[0].beg;    // указатель на начала 1-ой последовательности
  50.     int split = seg[0].len; // длина 1-ой последовательности (индекс начала 2-ой последовательности в массиве)
  51.     int n = split + seg[1].len; // индекс конца 2-ой последовательности
  52.     int pos1 = 0;   // индекс перебираемого элемента в 1-ой последовательности
  53.     int pos2 = split;   // индекс перебираемого элемента во 2-ой последовательности
  54.     int pos3 = 0;   // индекс перебираемого элемента в буфере
  55.     int* temp;  // указатель на начало буфера (динамический массив)
  56.  
  57.     if (++secondaryComparesCount && seg[0].len < seg[1].len)    // если 1-ая последовательность меньше 2-ой
  58.     {
  59.         temp = (int*)malloc(sizeof(int) * split);   // выделяем память под необходимое кол-во элементов в буфере (длина - количество элементов в 1-ой последовательности)
  60.  
  61.         // подсчёт используемой памяти для динамических структур хранения
  62.         if (usedDynamicMemory < sizeof(int) * split)
  63.             usedDynamicMemory = sizeof(int) * split;
  64.  
  65.         for (pos1 = 0; ++secondaryComparesCount && pos1 < split; pos1++)
  66.             temp[pos1] = a[pos1];   // записываем элементы 1-ой последовательности в буфер
  67.         pos1 = 0;   // обнуляем индекс рассматриваемого элемента 1-ой последовательности
  68.         while (++secondaryComparesCount && ++secondaryComparesCount && pos1 < split && pos2 < n)    // пока не вышли за границы последовательностей
  69.             if (temp[pos1] <= a[pos2] && ++mainComparesCount)   // если элемент из буфера не превосходит элемент элемент из второй последовательности
  70.                 a[pos3++] = temp[pos1++];   // присваиваем первый элемент из буфера первому элементу исходного массива (наименьший из последовательностей), больше эти элементы не рассматриваем
  71.             else
  72.                 a[pos3++] = a[pos2++];  // присваиваем первый элемент из 2-ой последовательности первому элементу исходного массива (наименьший из последовательностей), больше эти элементы не рассматриваем
  73.  
  74.  
  75.         while (++secondaryComparesCount && pos1 < split)
  76.             a[pos3++] = temp[pos1++];   // дописываем, сохраняя порядок, оставшиеся элементы из 1-ой (наименьшей) последовательности в буфер
  77.     }
  78.     else    // если 1-ая последовательность больше или равна 2-ой
  79.     {
  80.         temp = (int*)malloc(sizeof(int) * seg[1].len);  // выделяем память под необходимое кол-во элементов в буфере (длина - количество элементов во 2-ой последовательности)
  81.  
  82.         for (pos1 = 0, pos2 = split; ++secondaryComparesCount && pos2 < n; )
  83.             temp[pos1++] = a[pos2++];   // записываем элементы 2-ой последовательности в буфер
  84.  
  85.         pos1 = split - 1; pos2 = seg[1].len - 1; pos3 = n - 1;  // обнуляем индексы рассматриваемых элементов 1-ой, 2-ой последовательности и буфера
  86.         while (++secondaryComparesCount && ++secondaryComparesCount && pos1 >= 0 && pos2 >= 0)
  87.             if (temp[pos2] > a[pos1] && ++mainComparesCount)
  88.                 a[pos3--] = temp[pos2--];   // присваиваем последний элемент из буфера последнему элементу исходного массива (наибольший из последовательностей), больше эти элементы не рассматриваем
  89.             else
  90.                 a[pos3--] = a[pos1--];  // присваиваем последний элемент из 1-ой последовательности последнему элементу исходного массива (наибольший из последовательностей), больше эти элементы не рассматриваем
  91.  
  92.  
  93.         while (++secondaryComparesCount && pos2 >= 0)
  94.             a[pos3--] = temp[pos2--];   // дописываем, сохраняя порядок, оставшиеся элементы из 2-ой (наименьшей) последовательности в буфер
  95.     }
  96.     free(temp);
  97.     seg[0].len = n; // записываем новую длину получившейся последовательности
  98. }
  99.  
  100. int try_merge(segment* seg, int top)    // функция балансировки размера найденных упорядоченных последовательностей
  101. {
  102.     int x, y, z;    // длины 3-х последних найденных последовательностей
  103.     while (++secondaryComparesCount && top > 0) // пока остались нерассмотренные последовательности
  104.     {
  105.         x = seg[top].len;   // размер последней последовательности
  106.         y = seg[top - 1].len;   // размер предпоследней последовательности
  107.         z = ++secondaryComparesCount && top > 1 ? seg[top - 2].len : 0; // размер 3-ей последней последовательности (если есть)
  108.         if (++secondaryComparesCount && ++secondaryComparesCount && top > 1 &&  z <= x + y) // если есть 3-я последняя последовательность и по размеру она не превосходит суммы размеров предпоследней и последней последовательностей
  109.         {
  110.             if (++secondaryComparesCount && z < x)  // если 3-я последняя последовательность меньше последней
  111.             {
  112.                 merge_copy_less(&seg[top - 2]); // сливаем 3-ю последнюю и предпоследнюю последовательности
  113.                 seg[top - 1] = seg[top];    // перемещаем последнюю последовательность на место предпоследней
  114.             }
  115.             else    // если 3-я последняя последовательность не превосходит последней
  116.             {
  117.                 merge_copy_less(&seg[top - 1]); // сливаем предпоследнюю и последнюю последовательности
  118.             }
  119.             top--;  // так как 2 последовательности слиты, уменьшаем их количество на 1
  120.         }
  121.         else
  122.             if (++secondaryComparesCount && y <= x) // если предпоследняя последовательность не превосходит последней
  123.             {
  124.                 merge_copy_less(&seg[top - 1]); // сливаем предпоследнюю и последнюю последовательности
  125.                 top--;  // так как 2 последовательности слиты, уменьшаем их количество на 1
  126.             }
  127.             else    // если предпоследняя последовательность превосходит последнюю
  128.                 break;  // больше сгруппировать невозможно, завершаем балансировку
  129.     }
  130.     return top; // возвращаем количество упорядоченных последовательностей
  131. }
  132.  
  133. void tim_sort(int* a, int n)
  134. {
  135.     int min_size = get_min_size(n); // вычисляем минимальный размер подмассива
  136.     int size;   // размер найденной упорядоченной последовательности
  137.     int i, j = 0;   // итераторы циклов for
  138.  
  139.     // подсчёт используемой памяти для динамических структур хранения
  140.     int allocMem = ((n - 1) / min_size + 1) * sizeof(segment);
  141.     usedStaticMemory += allocMem;
  142.  
  143.     segment* seg = (segment*)malloc(allocMem);    // выделяем память под хранение подмассивов
  144.     int t = -1; // переменная-итератор для перебора подмассивов
  145.  
  146.     usedStaticMemory = (5 + 3 + 5 + 2 + 3 + 1) * sizeof(int) + 2 * sizeof(int*) + sizeof(segment*); /* 5 переменных типа int из функции tim_sort(),
  147.                                                                                             3 переменных типа int из функции try_merge(),
  148.                                                                                             5 переменных типа int из функции merge_copy_less(),
  149.                                                                                             2 переменных типа int из функции insertion_sort(),
  150.                                                                                             3 переменных типа int из функции reverse(),
  151.                                                                                             1 переменные типа int из функции get_min_size(),
  152.                                                                                             2 переменных типа int* из функции merge_copy_less(),
  153.                                                                                             1 переменная типа segment* из функции tim_sort() */
  154.  
  155.     for (i = 0; ++secondaryComparesCount && i < n; i += size)   // перебираем все подмассивы массива, чтобы разделить на упорядоченные последовательности
  156.     {
  157.         for (j = i + 1; ++secondaryComparesCount && ++mainComparesCount && j < n && a[j - 1] >= a[j]; j++); // пока просматриваемая последовательность упорядочена по неубыванию
  158.         if (j != i + 1 && ++secondaryComparesCount) // если нашли последовательность, упорядоченную по неубыванию
  159.             reverse(a + i, j - i);  // переписываем ее наоборот, часть элементов последовательности упорядочена
  160.         if (++secondaryComparesCount && ++secondaryComparesCount && j != n && j - i < min_size) // если нашли последовательность, упорядоченную по неубыванию и она полностью входит в текущий подмассив
  161.         {
  162.             size = ++secondaryComparesCount && n - i < min_size ? n - i : min_size; // если подмассив не последний в массиве: вычисляем размер найденной последовательности в подмассиве
  163.             insertion_sort(a + i, size, j - i); // сортируем найденную последовательность сортировкой вставками
  164.             j = i + size;   // последовательность обработана, переходим дальше
  165.         }
  166.         for (; ++secondaryComparesCount && ++mainComparesCount && j < n && a[j - 1] <= a[j]; j++);  // пока просматриваемая последовательность упорядочена по невозрастанию
  167.  
  168.         seg[++t].beg = a + i;   // сохраняем её, как объект структурного типа
  169.         size = seg[t].len = j - i;  // вычисляем её размер
  170.         t = try_merge(seg, t);  /* сливаем предпоследнюю последовательность с наименьшей из 3-ей последней и последней
  171.         если они в сумме меньше или равны текущей последовательности или предыдущая последовательность меньше 3-ей последней.
  172.         Пока выполняется условие, выполняем это действие.
  173.  
  174.         Получаем количество в массиве слитых последовательностей.
  175.         Нужно для получения упорядоченных последовательностей, примерно равных по размеру.
  176.         */
  177.     }
  178.  
  179.     while (++secondaryComparesCount && t > 0)   // пока остались неслитые последовательности
  180.     {
  181.         merge_copy_less(&seg[t - 1]);   // поочередно с конца сливаем найденные упорядоченные последовательности
  182.         t--;
  183.     }
  184.     free(seg);
  185. }
Advertisement
Add Comment
Please, Sign In to add comment