ShellSort.pas 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. Program ShellSort;
  2. {Сортировка Шелла, улучшение алгоритма прямого включения, разница в том,
  3. что процесс сортировки бьется на T этапов, в каждом из которых делается
  4. один проход прямого включения, но только на элементы, отстоящие друг от
  5. друга на h[i] эл-тов (i in [1..T] и h[T] = 1)
  6. Также для алгоритма прямой вставки удобно использовать "барьеры". Дабы
  7. при перемещении не сверять индекс эл-та с началом массива, удобно
  8. расширить массив на 1 эл-т (в данном случае на h[1]) и туда писать эл-т,
  9. которому собираемся искать место. Таким образом размер массива будет
  10. [-h[1]..TotalCount]. Вследствие желания создать универсальный алгоритм
  11. с использованием динамических структур (нам заранее не известно T и
  12. соответственно h[1]) будет использоваься смещенная индексация массива
  13. ( A[i] -> A[h[1]+i]).
  14. Используются улучшения Д. Кнута:
  15. h[k-1] := 3*h[k]+1;
  16. T := LOG(N,3)-1
  17. PS. Надеюсь весь этот бред поможет вам разобраться в предложенном алгоритме.}
  18. type
  19. item = word;
  20. const
  21. MaxCount = 65530 div SizeOf(item); {Максимальное число эл-тов массива}
  22. TotalCount = 20000;
  23. type
  24. pArray = ^tArray;
  25. tArray = array [1..MaxCount] of Item;
  26. tIndex = 1..MaxCount;
  27. var
  28. T : tIndex; {Число "разбиений"}
  29. h : pArray;
  30. Fi : tIndex; {Реальный индекс первого эл-та (не барьера)}
  31. A : pArray;
  32. {--------------------------------------------------------------}
  33. Procedure Init; {Устанавливаются улучш. Кнута, иниц. массивы}
  34. var
  35. i : tIndex;
  36. begin
  37. T := round(Ln(TotalCount)/Ln(3)-1); {(С) Кнут}
  38. GetMem(h,T*SizeOf(item)); {Иниц. массив расстояний}
  39. h^[T] := 1;
  40. for i := T-1 DownTo 1 do h^[i] := 3*h^[i+1]+1;
  41. Fi := h^[1]; {Установим глоб. индекс "смещенного" смещения}
  42. GetMem(A,(TotalCount+Fi)*Sizeof(Item)); {Иницализируем осн. массив}
  43. {^^^ Для барьеров}
  44. end;{Init}
  45. {--------------------------------------------------------------}
  46. Procedure Done; {Осв. всю память}
  47. begin
  48. FreeMem(h,T*SizeOf(Item));
  49. FreeMem(A,TotalCount*SizeOf(Item));
  50. end;{Done}
  51. {--------------------------------------------------------------}
  52. Procedure SetArray; {Процедурка заполнения эл-тов массива случ. числами}
  53. var
  54. i : tIndex;
  55. begin
  56. Randomize;
  57. For i := 1 To TotalCount Do A^[Fi+i] := Random(65535);
  58. {^^^ Опять барьеры}
  59. end;{SetArray}
  60. {--------------------------------------------------------------}
  61. {Основная программа}
  62. Var
  63. i,j,k,m,s : tIndex;
  64. Times : pArray;
  65. x : Item;
  66. BEGIN
  67. Init;
  68. SetArray;
  69. GetMem(Times,T*SizeOF(Word));
  70. FillChar(Times^,T*SizeOF(Word),0);
  71. For m := 1 to T do
  72. begin
  73. k := h^[m]; {Расстояние между эл-тами}
  74. s := Fi-k; {"барьер"}
  75. For i := k+1 to TotalCount do
  76. begin
  77. x := A^[Fi+i]; {Эл-т, с которым работаем}
  78. If S=Fi then S := Fi-k;
  79. inc(s);
  80. A^[s] := x; {Установим "барьер"}
  81. j := i-k; {Предыдущий через расстояние}
  82. while A^[Fi+j] > x do
  83. begin
  84. Inc(Times^[m]);
  85. A^[Fi+j+k] := A^[Fi+j]; {Двигаем массив}
  86. dec(j,k);
  87. end;
  88. A^[Fi+j+k] := x;
  89. end;
  90. end;
  91. For i := 1 to TotalCount do
  92. begin
  93. if ((i-1) mod 10) = 0 then WriteLN;
  94. Write(A^[Fi+i]:7);
  95. end;
  96. WriteLN(#10#13'Число перестановок на каждом этапе:');
  97. for i := 1 to T do WriteLn(i,'(',H^[i],') -> ',Times^[i]);
  98. FreeMem(Times,T*SizeOF(Word));
  99. Done;
  100. END.