avp210159

rotater.c rotate string using reverse

Jul 11th, 2016
2,916
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 8.50 KB | None | 0 0
  1. // avp 2016 rotate string using reverse (based on http://codelab.ru/task/cycle_shift/)
  2.  
  3. #ifdef TESTCNT
  4. int Revcnt, Swpcnt;
  5. #endif
  6.  
  7. /*
  8. Алгоритм #3: переворотами
  9.  
  10. Задача кажется сложной, пока вас не осенит озарение («ага!»):
  11. итак, нужно преобразовать массив ab в bа.
  12.  
  13. Предположим, что у нас есть функция reverse, переставляющая элементы некоторой
  14. части массива в противоположном порядке.
  15. В исходном состоянии массив имеет вид ab. Вызвав эту функцию для первой части,
  16. получим аr b (прим. редактора:аr - это модифицированная часть a,
  17. к которой применили фукнцию перестановки reverse).
  18. Затем вызовем ее для второй части: получим аr br. Затем вызовем функцию для
  19. всего массива, что даст (аr br)r, а это в точности соответствует bа.
  20.  
  21. Посмотрим, как будет такая функция действовать на массив "abcdefgh",
  22. который нужно сдвинуть влево на три элемента:
  23.  
  24.  псевдокод: Сдвиг через функцию перестановки reverse
  25.  
  26.  
  27.     reverse(0, i-1)   // cba|defgh
  28.     reverse(i, n-1)   // cba|hgfed
  29.     reverse(0, n-1)   // defgh|abc
  30.  
  31.  
  32. Код, использующий функцию переворота, оказывается эффективным и
  33. малотребовательным к памяти, и настолько короток и прост,
  34. что при его реализации сложно ошибиться.
  35.  
  36. Б. Керниган и П. Дж. Плоджер пользовались именно этим методом для перемещения
  37. строк в текстовом редакторе в своей книге (В. Kernighan, P. J. Plauger,
  38. Software Tools in Pascal, 1981). Керниган пишет, что эта функция заработала
  39. правильно с первого же запуска, тогда как их предыдущая версия, использовавшая
  40. связный список, содержала несколько ошибок. Этот же код используется в
  41. некоторых текстовых редакторах, включая тот, в котором я впервые набрал
  42. настоящую главу. Кен Томпсон (Ken Thompson) написал этот редактор с функцией
  43. reverse в 1971 году, и он утверждает, что она уже тогда была легендарной.
  44.  
  45. */
  46.  
  47. static void
  48. reverse (char *s, int n)
  49. {
  50.   char *t = s + n - 1, c;
  51.  
  52.   while (s < t) {
  53.     //    swap(s++, t--);
  54.     c = *s;
  55.     *s++ = *t;
  56.     *t-- = c;
  57. #ifdef TESTCNT
  58.     Revcnt++;
  59. #endif
  60.   }
  61. }
  62.  
  63. char *
  64. rotate (char *s, int len, int dist)
  65. {
  66.   if (len > 1 && (dist %= len)) {
  67.     if (dist < 0)
  68.       dist += len; // ROR == len - dist
  69.     reverse(s, dist);
  70.     reverse(s + dist, len - dist);
  71.     reverse(s, len);
  72.   }
  73.   return s;
  74. }
  75.  
  76. char *
  77. rotate_l (char *s, int len, int dist) {
  78.   return rotate(s, len, dist);
  79. }
  80.  
  81. // -(dist % len) for dist == INT_MIN
  82. char *
  83. rotate_r (char *s, int len, int dist) {
  84.   return len > 1 ? rotate(s, len, -(dist % len)) : s;
  85. }
  86.  
  87.  
  88. /* -----------------------
  89. Алгоритм #2:
  90.  
  91. Можно предложить алгоритм, который возникает из рассмотрения задачи с другой
  92. точки зрения.
  93.  
  94. Циклический сдвиг массива х сводится фактически к замене ab на bа,
  95. где а — первые i элементов х, a b — оставшиеся элементы.
  96.  
  97. Предположим, что а короче b. Разобьем b на bleft и bright, где bright
  98. содержит i элементов (столько же, сколько и а). Поменяем местами а и bright,
  99. получим bright bleft а. При этом а окажется в конце массива — там,
  100. где и полагается.
  101.  
  102. Поэтому можно сосредоточиться на перестановке bright и bleft.
  103. Эта задача сводится к начальной, поэтому алгоритм можно вызывать рекурсивно.
  104. Программа, реализующая этот алгоритм, будет достаточно красивой,
  105. но она требует аккуратного написания кода, а оценить ее эффективность непросто:
  106.  
  107. */
  108.  
  109. static inline void
  110. swap (char a[], int f, int t, int l)
  111. {
  112.   int tt;
  113.  
  114.   for (l += f; f < l; f++, t++) {
  115.     tt = a[f];
  116.     a[f] = a[t];
  117.     a[t] = tt;
  118. #ifdef TESTCNT
  119.     Swpcnt++;
  120. #endif
  121.   }
  122. }
  123.  
  124. // not more swaps, but not faster (than reverse based) rotate left
  125. void
  126. rotatex (char a[], int n, int dist)
  127. {
  128.   if (n < 2 || (dist %= n) == 0)
  129.     return;
  130.   if (dist < 0)
  131.     dist += n; // ROR == len - dist
  132.  
  133.   int i, j, p;
  134.   i = p = dist;
  135.   j = n - p;
  136.   while (i != j) {
  137.     if (i > j) {
  138.       swap(a, p - i, p, j);
  139.       i -= j;
  140.     } else {
  141.       swap(a, p - i, p + j - i, i);
  142.       j -= i;
  143.     }
  144.   }
  145.   swap(a, p - i, p, i);
  146. }
  147.  
  148.  
  149. // macroses for any array
  150. #define REVERSE(a, size) ({int _i = 0, _j = (size) - 1;                 \
  151.       while (_i < _j) {                                                 \
  152.         __typeof__(*a) c = (a)[_i];                                     \
  153.         (a)[_i] = (a)[_j];                                              \
  154.         (a)[_j] = c;                                                    \
  155.         _i++; _j--;                                                     \
  156.       }})
  157.  
  158. #define ROTA_L(arr, size, dist) ({int _s = (size), _d = (dist);         \
  159.       __typeof__(&((arr)[0])) _a = (arr);                               \
  160.                                                                         \
  161.       if (_s > 1 && (_d %= _s)) {                                       \
  162.         if (_d < 0) _d += _s;                                           \
  163.         REVERSE(_a, _d); REVERSE(_a + _d, _s - _d);                     \
  164.         REVERSE(_a, _s);                                                \
  165.       }})
  166.  
  167. #define ROTA_R(arr, size, dist) ({int __s = (size), __d = (dist);       \
  168.       if (__s > 1) ROTA_L(arr, __s, -(__d % __s)); })
  169.  
  170. #include <stdio.h>
  171. #include <string.h>
  172. #ifdef TESTCNT
  173. #include <stdlib.h>
  174. #include <time.h>
  175. #endif
  176.      
  177. int
  178. main (int ac, char *av[])
  179. {
  180.   char s[1000], dir[100];
  181.   int dist;
  182.  
  183. #ifdef TESTCNT
  184.   puts("Enter ^D to time test");
  185. #endif
  186.   while (scanf("%s %s %d", dir, s, &dist) != EOF) {
  187.     char t[1000], p[1000], *pt = t;
  188.     strcpy(t, s); strcpy(p, s);
  189.  
  190.     if (*dir == 'l') {
  191.       ROTA_L(pt, strlen(t), dist);
  192. #ifdef TESTCNT
  193.       Revcnt = Swpcnt = 0;
  194. #endif
  195.       rotate_l(s, strlen(s), dist);
  196.       rotatex(p, strlen(s), dist);
  197.     } else {
  198.       ROTA_R(pt, strlen(t), dist);
  199. #ifdef TESTCNT
  200.       Revcnt = Swpcnt = 0;
  201. #endif
  202.       rotate_r(s, strlen(s), dist);
  203.       rotatex(p, strlen(s), -dist);
  204.     }
  205.     printf("%s: [%s]    %s: [%s] "
  206. #ifdef TESTCNT
  207.        " (%d) "
  208. #endif
  209.        "  rotatex: [%s]"
  210. #ifdef TESTCNT
  211.        " (%d)"
  212. #endif
  213.        "\n",
  214.        *dir == 'l' ? "ROTL" : "ROTR", t,
  215.        *dir == 'l' ? "rotl" : "rotr", s,
  216. #ifdef TESTCNT
  217.        Revcnt,
  218. #endif
  219.        p
  220. #ifdef TESTCNT
  221.        , Swpcnt
  222. #endif
  223.        );
  224.   }
  225.  
  226. #ifdef TESTCNT
  227.  
  228.   Swpcnt = Revcnt = 0;
  229.   int i, j, n;
  230.   long long sum = 0;
  231.  
  232.   srand(0);
  233.   for (j = 2; j < 1000; j++)
  234.     for (i = 0; i < 1000; i++)
  235.       if (rand() % j)
  236.     sum += j;
  237.  
  238.   puts("start measure");
  239.   clock_t t1 = clock(), t1x, t2x;
  240.  
  241.   srand(0);
  242.   for (j = 2; j < 1000; j++)
  243.     for (i = 0; i < 1000; i++)
  244.       rotate(s, j, rand() % j);
  245.   t1x = clock();
  246.  
  247.   srand(0);
  248.   for (j = 2; j < 1000; j++)
  249.     for (i = 0; i < 1000; i++)
  250.       rotatex(s, j, rand() % j);
  251.   t2x = clock();
  252.  
  253.   printf("total %lld bytes rotate time:\n"
  254.      "reverse %f sec (%d byte swaps)\n"
  255.      "blockswap %f sec (%d byte swaps)\n",
  256.      sum,
  257.      (double)(t1x - t1)/CLOCKS_PER_SEC, Revcnt,
  258.      (double)(t2x - t1x)/CLOCKS_PER_SEC, Swpcnt);
  259. #endif
  260.  
  261.  
  262.   return puts("End") == EOF;
  263. }
Advertisement
Add Comment
Please, Sign In to add comment