|
Serwis Edukacyjny Nauczycieli w I-LO w Tarnowie
Materiały głownie dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej
Autor artykułu: mgr Jerzy Wałaszek |
©2026 mgr Jerzy Wałaszek
|
W latach 50-tych ubiegłego wieku informatyk Donald Shell zauważył, iż algorytm sortowania przez wstawianie pracuje bardzo efektywnie w przypadku gdy zbiór jest w dużym stopniu uporządkowany (sprawdź wyniki naszych badań czasów sortowania dla tego algorytmu). Z kolei algorytm ten pracuje nieefektywnie w zbiorach nieuporządkowanych, ponieważ elementy są przesuwane w każdym obiegu o jedną pozycję przy wstawianiu elementu wybranego na listę uporządkowaną.
Pomysł Shella polegał na tym, iż sortowany zbiór dzielimy na podzbiory,
których elementy są odległe od siebie w sortowanym zbiorze o pewien odstęp h. Każdy z tych podzbiorów sortujemy algorytmem przez
wstawianie. Następnie odstęp zmniejszamy. Powoduje to powstanie nowych
podzbiorów
(będzie ich już mniej). Sortowanie powtarzamy i znów
zmniejszamy odstęp, aż osiągnie on wartość 1. Wtedy sortujemy już normalnie za pomocą sortowania przez wstawianie. Jednakże z uwagi na wcześniejsze obiegi sortujące mamy
ułatwione zadanie, ponieważ zbiór został w dużym stopniu uporządkowany. Dzięki
początkowym dużym odstępom elementy były przesuwane w zbiorze bardziej
efektywnie - na duże odległości. W wyniku otrzymujemy najlepszy pod względem
szybkości czasu wykonania algorytm sortujący w klasie
Efektywność algorytmu sortowania metodą Shella zależy w dużym stopniu od ciągu przyjętych odstępów. Pierwotnie Shell proponował pierwszy odstęp równy połowie liczby elementów w sortowanym zbiorze. Kolejne odstępy otrzymujemy dzieląc odstęp przez 2 (dzielenie całkowitoliczbowe).
Przykład:
Posortujemy metodą Shella zbiór ośmiu liczb:
| Podział, h=4 | Sortowanie | Wynik | |
4 2 9 5 6 3 8 1 |
- Zbiór wejściowy | ||
| 1 |
4 6 |
4 6 |
4 6 |
| 2 |
2 3 |
2 3 |
2 3 |
| 3 |
9 8 |
8 9 |
8 9 |
| 4 |
5 1 |
1 5 |
1 5 |
| Zbiór wyjściowy - |
4 2 8 1 6 3 9 5 |
||
Zmniejszamy odstęp h o połowę, więc
| Podział, h=2 | Sortowanie | Wynik | |
4 2 8 1 6 3 9 5 |
- Zbiór wejściowy | ||
| 1 |
4 8 6 9 |
4 6 8 9 |
4 6 8 9 |
| 2 |
2 1 3 5 |
1 2 3 5 |
1 2 3 5 |
| Zbiór wyjściowy - |
4 1 6 2 8 3 9 5 |
||
Zmniejszamy odstęp h o połowę,
| Podział, h=1 | Sortowanie | Wynik | |
4 1 6 2 8 3 9 5 |
- Zbiór wejściowy | ||
| 1 |
4 1 6 2 8 3 9 5
|
1 2 3 4 5 6 8 9 |
1 2 3 4 5 6 8 9 |
| Zbiór wyjściowy - |
1 2 3 4 5 6 8 9
|
||
![]() prof. Donald Knuth |
Kluczowym elementem wpływającym na efektywność sortowania metodą Shella jest właściwy dobór ciągu odstępów. Okazuje się, iż ciąg zaproponowany przez twórcę algorytmu jest jednym z najgorszych, ponieważ w kolejnych podzbiorach uczestniczą wielokrotnie te same elementy (możesz to prosto sprawdzić w podanym powyżej przykładzie).
Dotąd problem optymalnych odstępów w algorytmie sortowania metodą Shella nie został rozwiązany matematycznie, ponieważ w ogólnym przypadku jest niezwykle trudny. Wielu badaczy proponowało na wybór tych odstępów różne ciągi liczbowe otrzymując lepsze lub gorsze rezultaty.
Na przykład profesor Donald Knuth (autor "Sztuki Programowania Komputerów" - "The Art of Computer Programming") zbadał bardzo dokładnie algorytm sortowania metodą Shella i doszedł do wniosku, iż dobry ciąg odstępów dla n elementowego zbioru można wyznaczyć następująco:| Przyjmujemy h1
= 1 obliczamy hs = 3hs-1 + 1 aż do momentu, gdy Ostatecznie h = [hs : 9] |
Przykład:
Obliczmy pierwszy odstęp dla zbioru o
| h1
= 1 h2 = 3h1 + 1 = 3 + 1 = 4 - mniejsze od 200, kontynuujemy h3 = 3h2 + 1 = 12 + 1 = 13 - kontynuujemy h4 = 3h3 + 1 = 39 + 1 = 40 - kontynuujemy h5 = 3h4 + 1 = 120 + 1 = 121 - kontynuujemy h6 = 3h5 + 1 = 363 + 1 = 364 - stop, ponieważ jest większe od 200 h = [h6 : 9] = [364 : 9] = [40 4/9] = 40 (zwróć uwagę, iż jest to zawsze element wcześniejszy o dwie pozycje, czyli h4) |
Kolejne odstępy obliczamy dzieląc całkowitoliczbowo bieżący odstęp przez 3. Taki właśnie sposób wyliczania odstępów przyjmiemy w naszym algorytmie.
| n | - liczba elementów w sortowanym zbiorze, n ∈ N |
| d[ ] | - zbiór n-elementowy, który będzie sortowany. Elementy zbioru mają indeksy od 1 do n. |
| d[ ] | - posortowany zbiór n-elementowy. Elementy zbioru mają indeksy od 1 do n. |
| i | - indeks elementu listy uporządkowanej, i ∈ N |
| j | - zmienna sterująca pętli, j ∈ N |
| h | - odstęp pomiędzy kolejnymi elementami podzbiorów, h ∈ N |
| x | - zawiera wybrany ze zbioru element |
| K01: | h ← 1 |
| K02: | Powtarzaj h ← 3h + 1, aż h ≥ n |
| K03: | h ← h div 9 |
| K04: | Jeśli h = 0, to h ← 1 |
| K05: | Dopóki h > 0: wykonuj kroki K06...K10 |
| K06: | Dla j = n - h,
n -
h - 1, ..., 1: wykonuj kroki K07...K09 |
| K07: | x ← d[j]; i ← j + h |
| K08: | Dopóki (i
≤
n) i (x > d[i]): wykonuj d[i - h] ← d[i]; i ← i + h |
| K09: | d[i - h] ← x |
| K10: | h ← h div 3 |
| K11: | Zakończ |

Algorytm sortowania metodą Shella jest ulepszonym algorytmem sortowania przez wstawianie. Aby się o tym przekonać, wystarczy spojrzeć na schemat blokowy. Kolorem szarym zaznaczyliśmy na nim bloki, które dokładnie odpowiadają algorytmowi sortowania przez wstawianie. Jedyną modyfikacją jest wprowadzenie odstępu h zamiast liczby 1.
Na początku algorytmu wyznaczamy wartość początkowego odstępu h. Wykorzystujemy tu sugestie prof. Donalda Knutha.
Po wyznaczeniu h rozpoczynamy pętlę warunkową nr 1. Pętla ta jest wykonywana dotąd, aż odstęp h przyjmie wartość 0. Wtedy kończymy algorytm, zbiór będzie posortowany.
Wewnątrz pętli nr 1 umieszczony jest opisany wcześniej algorytm sortowania przez wstawianie, który dokonuje sortowania elementów poszczególnych podzbiorów wyznaczonych przez odstęp h. Po zakończeniu sortowania podzbiorów odstęp h jest zmniejszany i następuje powrót na początek pętli warunkowej nr 1.
| Uwaga techniczna: Każdy obieg pętli nr 2 sortuje przemiennie jeden element z kolejnych podzbiorów. Najpierw będą to elementy przedostatnie w kolejnych podzbiorach wyznaczonych odstępem h, później wcześniejsze i wcześniejsze. Takie podejście znacząco upraszcza algorytm sortowania. |
C++// Sortowanie metodą Shella
//-------------------------
// (C)2012 mgr Jerzy Wałaszek
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
#include <cmath>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 20; // Liczebność zbioru.
// Program główny
//---------------
int main()
{
int d[N],h,i,j,x;
cout << " Sortowanie metoda Shella\n"
"--------------------------\n"
" (C)2005 Jerzy Walaszek\n\n"
"Przed sortowaniem:\n\n";
// Najpierw wypełniamy tablicę d[]
// liczbami pseudolosowymi, a następnie
// wyświetlamy jej zawartość
srand((unsigned)time(NULL));
for(i = 0; i < N; i++)
d[i] = rand() % 100;
for(i = 0; i < N; i++)
cout << setw(4) << d[i];
cout << endl;
// Wyznaczamy wartość początkowego przesunięcia
for(h = 1; h < N; h = 3 * h + 1);
h /= 9;
if(!h) h++; // istotne dla małych N,
// dla większych można pominąć!
// Sortujemy
while(h)
{
for(j = N - h - 1; j >= 0; j--)
{
x = d[j];
i = j + h;
while((i < N) && (x > d[i]))
{
d[i - h] = d[i];
i += h;
}
d[i - h] = x;
}
h /= 3;
}
// Wyświetlamy wynik sortowania
cout << "Po sortowaniu:\n\n";
for(i = 0; i < N; i++)
cout << setw(4) << d[i];
cout << endl << endl;
system("pause");
return 0;
}
|
Pascal// Sortowanie Metodą Shella
//-------------------------
// (C)2012 mgr Jerzy Wałaszek
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
program Shell_Sort;
const N = 20; // Liczebność zbioru.
var
d : array[1..N] of integer;
// Program główny
//---------------
var
h,i,j,x : integer;
begin
writeln(' Sortowanie metoda Shella ');
writeln('--------------------------');
writeln(' (C)2005 Jerzy Walaszek ');
writeln;
// Najpierw wypełniamy tablicę d[]
// liczbami pseudolosowymi, a następnie
// wyświetlamy jej zawartość
randomize;
for i := 1 to N do d[i] := random(100);
writeln('Przed sortowaniem:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
writeln;
// Wyznaczamy wartość początkowego przesunięcia
h := 1;
repeat
h := 3 * h + 1;
until h >= N;
h := (h div 9);
if h = 0 then h := 1; // istotne dla małych N,
// dla większych można
// pominąć!
// Sortujemy
while h > 0 do
begin
for j := N - h downto 1 do
begin
x := d[j];
i := j + h;
while (i <= N) and (x > d[i]) do
begin
d[i - h] := d[i];
inc(i, h);
end;
d[i - h] := x;
end;
h := h div 3;
end;
// Wyświetlamy wynik sortowania
writeln('Po sortowaniu:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
writeln;
writeln('Nacisnij Enter...');
readln;
end.
|
Basic' Sortowanie Metodą Shella
'-------------------------
' (C)2012 mgr Jerzy Wałaszek
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
CONST N = 20 ' Liczebność zbioru.
DIM AS INTEGER d(1 TO N),h,i,j,x
PRINT " Sortowanie metoda Shella "
PRINT "--------------------------"
PRINT " (C)2005 Jerzy Walaszek "
PRINT
' Najpierw wypełniamy tablicę d()
' liczbami pseudolosowymi, a następnie
' wyświetlamy jej zawartość
RANDOMIZE TIMER
FOR i = 1 TO N
d(i) = INT(RND * 100)
NEXT
PRINT "Przed sortowaniem:"
PRINT
FOR i = 1 TO N
PRINT USING "####"; d(i);
NEXT
PRINT
PRINT
' Wyznaczamy wartość
' początkowego przesunięcia
h = 1
DO
h = 3 * h + 1
LOOP UNTIL h >= N
h = INT(h / 9)
IF h = 0 THEN h = 1 ' istotne dla małych N,
' dla większych można
' pominąć!
' Sortujemy
WHILE h > 0
FOR j = N - h TO 1 STEP -1
x = d(j)
i = j + h
WHILE (i <= N) AND (x > d(i))
d(i - h) = d(i)
i = i + h
WEND
d(i - h) = x
NEXT
h = INT(h / 3)
WEND
' Wyświetlamy wynik sortowania
PRINT "Po sortowaniu:"
PRINT
FOR i = 1 TO N
PRINT USING "####"; d(i);
NEXT
PRINT
PRINT
PRINT "Nacisnij Enter..."
SLEEP
END
|
Python
(dodatek)# Sortowanie Metodą Shella
#-------------------------
# (C)2012 mgr Jerzy Wałaszek
# I Liceum Ogólnokształcące
# im. K. Brodzińskiego
# w Tarnowie
import random
n = 20 # Liczebność zbioru.
d = [random.randrange(100) for i in range(n)]
print(" Sortowanie metodą Shella ")
print("--------------------------")
print(" (C)2026 Jerzy Walaszek ")
print()
# wyświetlamy d[]
print("Przed sortowaniem:")
print()
for i in range(n):
print("%4d" % (d[i]),end="")
print()
print()
# Wyznaczamy wartość
# początkowego przesunięcia
h = 1
while h < n:
h = 3 * h + 1
h //= 9
if h == 0: h = 1 # istotne dla małych N,
# dla większych można
# pominąć!
# Sortujemy
while h:
for j in reversed(range(n - h)):
x = d[j]
i = j + h
while (i < n) and (x > d[i]):
d[i - h] = d[i]
i += h
d[i - h] = x
h //= 3
# Wyświetlamy wynik sortowania
print("Po sortowaniu:")
print()
for i in range(n):
print("%4d" % (d[i]),end="")
print()
print()
input("Naciśnij Enter...")
|
| Wynik: |
Sortowanie metodą Shella -------------------------- (C)2026 Jerzy Walaszek Przed sortowaniem: 18 78 65 32 19 20 0 80 80 27 84 90 95 9 29 23 74 5 39 4 Po sortowaniu: 0 4 5 9 18 19 20 23 27 29 32 39 65 74 78 80 80 84 90 95 Naciśnij Enter... |
JavaScript<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmshellsort">
<h3 style="text-align: center">Sortowanie Metodą Shella</h3>
<p style="TEXT-ALIGN: center">
(C)2012 mgr Jerzy Wałaszek - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Metodą Shella
//--------------------------------------------------------
// (C)2012 mgr Jerzy Wałaszek
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 20; // Liczebność zbioru.
function main()
{
var d = new Array(N);
var i,j,ip,ik,x,t;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
for(i = 0; i < N; i++) d[i] = Math.floor(Math.random() * 100);
t = "Przed sortowaniem:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
t += "<BR><BR>";
// Wyznaczamy początkowe przesunięcie
for(h = 1; h < N; h = 3 * h + 1);
h = Math.floor(h / 9);
if(!h) h++; // istotne dla małych N, dla większych można pominąć!
// Sortujemy
while(h)
{
for(j = N - h - 1; j >= 0; j--)
{
x = d[j];
i = j + h;
while((i < N) && (x > d[i]))
{
d[i - h] = d[i];
i += h;
}
d[i - h] = x;
}
h = Math.floor(h / 3);
}
// Wyświetlamy wynik sortowania
t += "Po sortowaniu:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html> |
| Cechy Algorytmu Sortowania Metodą Shella | |
| klasa złożoności obliczeniowej optymistyczna | O(n1,14) |
| klasa złożoności obliczeniowej typowa | |
| klasa złożoności obliczeniowej pesymistyczna | |
| Sortowanie w miejscu | TAK |
| Stabilność | NIE |
![]() |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2026 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.