Datastrukturer & Algoritmer, Dat C
RETTELSE
side 13 9/10-2002
Hvorfor beskftige sig med sortering?
Vsentligt til effektiv implementation af metoder p
samlinger af objekter
o
Sgning, jvf.
binr sgning
o
Sammenligning
o
...
Prsentation af store mnger output
o
sgesresultater
efter prioritet
o
postsystem,
efter data, alfabetisk afsender, subject, lngde, ...
Et af de ldste og bedst undersgte omrder i
datalogien
Et lrestykke i algoritmekonstruktion & -analyse
( lidt om formel til vurdering af tidsforbrug ved del-og-hersk; opflg fra sidste gang)
Insertionsort, sortering ved indsttelse
o
m.
kompleksitetsanalyse
Shellsort interessant optimering af Insertionsort
Mergesort eksempel p del-og-hersk m. stort
lagerforbrug
Quicksort del-og-hersk
o
sorteringsalgoritmen
o
eksempel p
meget elegant algoritme
Generel
formel til vurdering af tidsforbrug
public static Lsning ls(Problem p)
{ if(simpel(p))
return lsningen-p-det-simple-problem;
split p op i p1, ..., pn;
L1 = ls p1;
L2 = ls p2;
. . .
Ln = ls pn;
return kombiner(L1, ;2, ..., Ln); }
Parametre:
A,
antallet af delproblemer (n
ovenfor)
B,
ml for relativ strrelse af delproblem (f.eks. B=2 for halvering)
k,
bestemt ved overhead Theta(Nk) (= prisen for at kombinere)
T(N)
= { O(N logB
A) hvis
A > Bk
{
O(Nk log N) for
A= Bk
{
O(Nk) for
A < Bk
Anvende den p binr sgning ....
private static int binarySearch(Comparable[]a,Comparable
x, int low, int high)
{if( low > high ) return -1
int mid = ( low + high ) / 2;
if(a[mid].compareTo(x)< 0) return
binarySearch(a,x,mid+1,high);
else if(a[mid].compareTo(x)>0) return
binarySearch(a,x,low,mid-1);
else return mid;}
A=1,
B=2, Theta(Nk) = 1; dvs. k=0.
Bk
= 20 = 1 = A, dvs. midterste tilflde:
T(N)
= O(Nk log N) = O(N0 log N) = O(1 x log N) = O(log N)
A=2,
B=2, Theta(Nk) = N; dvs. k=1.
Bk
= 21 = 2 = A, dvs. midterste tilflde:
T(N) = O(Nk log N) = O(N1 log N) = O(N log N)
Insertionsort; sortering ved indsttelse
Simpel, effektiv for sm datast, god introduktion til
emnet
Princippet: Som du sorterer en hndfuld spillekort
Billede midtvejs:
sorteret del usorteret
del
3 5 23 34 66 17 15 45 8
12
^
3 5<-->23 34 66
17 15 45 8 12
^
|
|---------------------|
3 5 17 23 34 66
15 45 8 12
Nste skridt:
3 5 17 23 34 66
15 45 8 12
^
3 5<-->17 23 34 66 15 45 8 12
^
|
|--------------------|
3 5 15 17 23 34 66 45 8 12
At skrive det som generisk algoritme i Java:
anvender
p objekter som er underklasse af Comparable
datastrukturen
er arrays, dvs.
o man kan ikke mve sig til
plads,
o elementerne m flyttes
enkeltvis
o der er ikke globalt overblik
(som kortspilleren tror at have)
Hvordan var det nu med de
der Comparable??
package java.lang;
public interface Comparable{
int CompareTo(Object other); }
reprsenterer
intuitivt
x.CompareTo(y) < 0 x
< y
x.CompareTo(y) = 0 x
= y
x.CompareTo(y) > 0 x
> y
Nu koder vi lige ud af landevejen:
public static void insertionSort( Comparable [ ]
a )
{
for( int p = 1; p <
a.length; p++ )
{
Comparable tmp = a[ p ];
int j = p;
for( ; j > 0 && tmp.compareTo( a[ j - 1 ] ) < 0; j-- )
a[ j ] = a[ j - 1 ];
a[ j ] = tmp;
}
Teknikken:
Flytning af elementer og sammenligninger for at finde
ny plads slet sammen, s tom plads til nyt element ruller nedefter.
Tidsforbrug: vrste
= gennemsnit = O(n2)
bedste = O(n) (nr sorteret i forvejen)
Nu
kan vi sortere lystigt derudaf:
class RumskibFraMars implements Comparable{
խ[ ;
int CompareTo(Object other); { &\#\ } }
marsFlde = new RumskibFraMars [];
.....
insertionSort(marsFlde);
minListeAfTalCeller = new Integer [];
..... minListeAfTalCeller[17] = 534;
insertionSort(listeAfTalCeller)
Men....
Java er noget h!
minListeAfTal = new int [];
.....
insertionSort(minListeAfTal);
TYPEFEJL INGEN METODE insertionSort( int [] )
For effektiv reprsentation og sortering af arrays af tal er vi ndt til at skrive en ny:
public static void insertionSort( int [ ] a )
{for( int p = 1; p < a.length; p++ )
{ Comparable tmp = a[ p
]; int j = p;
for( ;
j > 0 && tmp < a[ j - 1 ] ) < 0; j-- ) a[ j ] = a[ j - 1 ];
a[ j ] = tmp; }
og
ogs
public static void insertionSort( byte [ ] a )
{...};
public static void insertionSort( short [ ] a )
{...};
public static void insertionSort( long [ ] a )
{...};
.... og for hver af de 4 vrige primitive typer
Shellsort: Optimering af insertionSort vha. insertionSort
Tidsforbrug
for insertionSort
vrste
= gennemsnit = O(n2)
bedste = O(n) (nr sorteret i forvejen)
Generelt:
O(n) for nsten sorterede arrays.
Tricket
i Shellsort:
Sorterer du delarrays bestende af hvert hte element, bliver arrayet lidt tttere p nsten-sorteret (h et eller anden tal).
Eksempel
h = 4
97 87 43 55 48 19 72 77 65 44 23 88 15 7
46
Betragt
som 4 sammenvvede delarrays:
97 87 43 55 48 19 72 77 65 44 23 88 15 7 46
Sortr
hver af disse indbyrdes insertionSort (hvor +1 erstattes af +4 og -1 af
-4):
15 7 23 55 48 19 43 77 65 44 46 88 97 87 72
Sortering
med h = 3 m forventes at g lidt hurtigere end O(n2)
15 7 23 55 48 19 43 77 65 44 46 88 97 87 72
Observation:
h=1, s er vi tilbage ved insertionSort
Shellsort:
udfre en rkke insertionSort-eringer med her
ht
> ht-1 > ht-2 > ... > h2 > h1
= 1
Varianter
over Shellsort = forskellige sekvenser af her:
Forskellige
strategier:
worst
case gennemsnit
/2 O(n2) O(n3/2)
= O(n1,5)
/2 og
+1 hvis lige O(n3/2)
= O(n1.5) ??O(n5/4)
= O(n1.25)
/2.2 ??O(n7/6)
= O(n1.17)
Iflge
vor bog :)
Shellsort
stammer fra 1959, men tidskompleksitet stadig ml for ny forskning:
http://www.informatik.uni-trier.de/~ley/db/indices/t-form.html
Search
Title
------------------------------------
Keyword: shellsort Submit Reset
------------------------------------------------------------------------
DBLP: [Home | Search: Author, Title | Conferences | Journals]
Resultat:
Bronislava
Brejov: Analyzing variants of Shellsort. Information Processing Letters 79(5):
223-227 (2001)
Marcin
Ciura: Best Increments for the Average Case of Shellsort. FCT 2001: 106-117
Tao
Jiang, Ming Li, Paul M. B. Vitnyi: A lower bound on the average-case
complexity of shellsort. JACM 47(5): 905-911 (2000)
Svante
Janson, Donald E. Knuth: Shellsort with three increments. Random Structures and
Algorithms 10(1-2): 125-142 (1997)
Renren
Liu: An Improved Shellsort Algorithm. TCS 188(1-2): 241-247 (1997)
og
mange-mange flere
Sgning
p Shell<mellemrum>sort:
Robert
T. Smythe, J. Wellner: Stochastic Analysis of Shell Sort. Algorithmica 31(3):
442-457 (2001)
Mergesort
del-og-hersk med O(n log n)
baseret
p feltning af allerede sorterede sekvenser.
En
billede fra dengang mor var dreng:
Store
datamngder opbevaredes p bnd!
Altid
sorterede!
Tre
bndstationer:
bnd
1: status fra igr
bnd
2: transaktioner
fra idag sorteret!
bnd
3: her skrives
status i dag ved lukketid
Eksempel:
Bnd
1: 11 22 33 44 55
66
Bnd
2: 8 27 47
Bnd
3:
Bnd
1: 11 22 33 44 55
66
Bnd 2: 27 47
Bnd
3: 8
Bnd
1: 22 33 44 55 66
Bnd
2: 27 47
Bnd
3: 8 11
Bnd
1: 33 44 55 66
Bnd
2: 27 47
Bnd
3: 8 11 22
Bnd
1: 33 44 55 66
Bnd
2: 47
Bnd
3: 8 11 22 27
....
Bnd
3: 8 11 22 33 44 47 55
66
Fletning af to arrays over i et tredje; som fr men med tllere
private static void merge( Comparable [ ] a,
Comparable [ ] tmpArray,
int leftPos, int rightPos, int rightEnd )
// [] a indeholder to sorterede sekvenser
// 1. fra position leftPos til
rightPos-1
// 2. fra position rightPos til
rightEnd
// De to sekvenser flettes over i tmpArray
// og kopieres tilbage i a
{....};
Mergesort lige ud ad landevejen:
public static void mergeSort( Comparable [ ] a
)
{Comparable [ ] tmpArray = new Comparable[
a.length ];
mergeSort( a, tmpArray, 0,
a.length - 1 ); }
private static void mergeSort( Comparable [ ]
a, Comparable [ ] tmpArray,
int left, int right )
{ if( left < right ) {
int
center = ( left + right ) / 2;
mergeSort( a, tmpArray, left, center );
mergeSort( a, tmpArray, center + 1, right );
merge(
a, tmpArray, left, center + 1, right ); }}
Tidsforbrug:
Vrst
= Bedst = Gennemsnit = O(n log n)
Problemer:
Krver et ekstra array + kopiering frem og tilbage
En
anden, Quicksort, = O(n log n) ca. 3 gange s hurtig (?)
At sortere en sekvens af elementer S:
1.
Hvis
lngden af S er 0 eller 1, s er vi frdige, ellers
2.
Vlg
et element v i S kaldet omdrejningspunkt (pivot)
3.
Flyt
rundt p elementerne i S, s S kommer til at se ud som:
x1, x2, ..., xk, v, y1, y2,
... ym
hvor alle xerne v og yerne
v
4.
Sortr
x1, x2, ..., xk p deres pladser i S
5.
Sortr
y1, y2, ... ym p deres pladser i S
For
at vurdere tidsforbrug. Skridt 3 er kritisk: vi pstr O(n)
Princip:
Kr en tller ind fra hjre eller
venstre, s snart fejl opdages, byt om;
Blive ved med det til tllerne mdes;
I Java (lidt enklere end i bogen):
private static void partition(Comparable [ ] a,
int low, int pivotIndex, int high )
{
Comparable pivot = a[pivotIndex];
int i = low; int j = high;
while(true)
{while( a[ i ].compareTo(
pivot ) < 0 ) i++;
while( pivot.compareTo(
a[ j ] ) < 0) j--;
if( i > j ) break;
swapReferences( a, i, j
);i++;j--; } }
Optlling:
i og
j flyttes tilsammen a.length
gange, og op til n swaps
(gentaget)
At sortere en sekvens af elementer S:
1.
Hvis
lngden af S er 0 eller 1, s er vi frdige, ellers
2.
Vlg
et element v i S kaldet omdrejningspunkt (pivot)
3.
Flyt
rundt p elementerne i S, s S kommer til at se ud som:
x1, x2, ..., xk, v, y1, y2,
... ym
hvor alle xerne v og yerne
v
4.
Sortr
x1, x2, ..., xk p deres pladser i S
5.
Sortr
y1, y2, ... ym p deres pladser i S
Vi
ved nu skridt
3 er O(lngde S).
Definition: Medianen for z1, z2, ..., zn er et zi,
s ca. halvdelen af zer zi og ca. halvdelen af zer
zi
Obs: I de tilflde, hvor vi p
magisk vis er heldige at ramme (ca.) medianen som omdrejningspunkt, har vi O(n
log n):
et balanceret binrt tr
som kaldetr med dybde n og halvering
af arbejde hver gang.
(eller
check generel formel, argument som ved maximum contiguous subsequence sum)
Obs: Vrste tilflde O(n2) er hvis vi altid er uheldige og rammer det strste element som omdrevningspunkt:
et skvt tr af dybde n og
n-1, n-2, ..., 1, 0 arbejde p hvert niveau
Det kan bevises (vi gider ikke):
gennemsnit
O(n log n)
Tommelfingerregel:
Hvis
omdrejningspunktet vlges ca. midt gr det ikke galt med i forvejen nsten
sorterede data.
Obs:
Vi kunne i princippet godt beregne medianen hvergang, men tung beregning og
ikke sikkert det giver noget...
Tilpasset efter
N.Wirth (1976)
Opsplitning som
srskilt metode:
public static int partition(Comparable [ ] a, int low, int pivotIndex, int high )
//return new position for pivot element
{ Comparable pivot = a[pivotIndex];
int i = low; int j = high;
while(true)
{while( a[ i ].compareTo(
pivot ) < 0 ) i++;
while(
pivot.compareTo( a[ j ] ) < 0)
j--;
if( i > j )
break;
swapReferences( a,
i, j );i++;j--;}
return i; }
NBNB: FEJL I
OVENSTENDE; SE
http://www.dat.ruc.dk/~henning/DatC_DsAlg/QSort.java
FOR RETTE VERSION!!!
Som i bogen, st
nedre grnse p hvor vi gr over til en anden metode; forenkle ved at vlge
midterste element som omdrejningspunkt:
private static final int CUTOFF = 10;
private static void quicksort( Comparable [ ] a, int
low, int high )
{ if( low + CUTOFF > high ) insertionSort( a, low,
high );
else
{ int middle = ( low + high ) / 2;
int newPosForOldMiddleElt =
partition(a, low, middle, high);
quicksort( a, low,
newPosForOldMiddleElt - 1 );
quicksort( a,
newPosForOldMiddleElt + 1, high ); }}
Bogens version
lidt svrere at overskue:
ikke lagt partition ud i
srskilt metode
som omdrejningspunkt vlges
median af a[low], a[mid], a[high]
optimeringer:
o
beregning
af omdrejningspunkt kombineres med sortering af a[low], a[mid], a[high].
o
man
undgr allokering af variabel svarende til pivot i partition
private static void quicksort( Comparable [ ]
a, int low, int high )
{ if( low + CUTOFF > high ) insertionSort(
a, low, high );
else
{ // Sort low, middle, high
int middle = ( low + high ) /
2;
if(a[middle].compareTo(a[low])<0)
swapReferences(a,low,middle);
if(a[high].compareTo(a[low])<0) swapReferences(a,low,high);
if(a[high].compareTo(a[middle])<0) swapReferences(a,middle,high);
// Place pivot at position
high - 1
swapReferences( a, middle, high - 1 );
Comparable pivot = a[ high - 1
];
// Begin partitioning
int i, j;
for( i = low, j = high -
1; ; )
{ while( a[ ++i ].compareTo(
pivot ) < 0 ) ;
while(
pivot.compareTo( a[ --j ] ) < 0 );
if( i >=
j ) break;
swapReferences( a, i, j );}
// Restore pivot
swapReferences( a, i, high - 1 );
quicksort( a, low, i - 1 ); // Sort small elements
quicksort( a, i + 1, high ); // Sort large elements
}
}
Afsluttende
velse: QuickSelect
At
finde det kte strste element i array a
1.
Sortr
2.
Find
svaret i a[k-1] (-1 fordi
vi starter med nul)
Benyt
quicksort, men drop det ene rekursive kald:
private static final int CUTOFF = 10;
private static void quicksortselect( Comparable
[]a, int low, int high, int k)
{ if( low + CUTOFF > high ) insertionSort( a, low,
high );
else
{ int middle = ( low + high ) / 2;
int newPosForOldMiddleElt =
partition(a, low, middle, high);
if(k<i-1)
quicksortselect( a, low, newPosForOldMiddleElt - 1 );
if(k>i-1)
quicksortselect( a, newPosForOldMiddleElt + 1, high ); }}
Dvs. gennemsnitsopfrsel falder fra O(N log N) til O(log N)
check evt. generel formel, argument nu som binr sgning og
o og ikke som ved maximum contiguous subsequence sum
Dvs. parametren A falder fra 2 til 1
S L U
T