Jump to content


Photo

TSort


  • Please log in to reply
No replies to this topic

#1 wvd_vegt

wvd_vegt

    Master Member

  • Honorable Members
  • PipPipPipPipPip
  • 710 posts
  • Gender:Male
  • Location:the Netherlands

Posted 18 March 2005 - 10:41 PM

Hi,

Here is a simple but effective TSort class that sorts external data just by a OnCompare and OnSwap event both based on indexes and not real data.

Currently it supports shellsort, quicksort and bubblesort (yes i know that it's normally the slowest but if the data is already sorted can be quite fast). The sorting methods where taken & ported from various sources.

Beware that quicksort (i think) might fail in one direction (my documentation says i fixed quicksort but i'm not 100% sure as debugging code is still present), if anybody can point out the problem or can make it fail during testing (or tell me it's ok), please let me know.

If you implement shakersort (bi-directional bubblesort) i'm also very very interested!

If you have the OnCompare event compare multiple colums from two rows of gridview and set the OnSwap to exchange two rows you can easily do very complex sorting (the reason why i wrote this class in the first place).

Only minor problem is that with a normal rowswap not all is exchanged (I think I was using the moverow method) in the older gridview versions (focus rectangle for instance).

Any comments are welcome!

The interface:

CODE
type

 {

 A ComparesEvent should compare two nodes indentified by their indexes.

 The Action value returns should be :

  0 is the e1 and e2 values are identical,

 -1 if the e1 value is smaller than the e2 value and

 +1 if the e1 value is larger than the e2 value.}

 TCompareEvent = procedure(Sender: TObject; e1, e2: Word; var Action: Integer) of object;



 {

 A SwapEvent is called to swap two nodes identified by their indexes.}

 TSwapEvent = procedure(Sender: TObject; e1, e2: Word) of object;



 {

 Sorting is performed on a 0 based starting index of the data

 and Elements records of data.

 }

 TSort = class(TObject)

 private

   FOnCompare: TCompareEvent;

   FOnSwap: TSwapEvent;

   function GetCanSort: Boolean;

 public

   {The constructor.}

   constructor Create(compare: TCompareEvent = nil; swap: TSwapEvent = nil);



   {Performs a QuickSort on a 0 based array containing a number of Elements.}

   procedure DoQuickSort(Sender: TObject; Elements: Word; Ascending: Boolean = True);

   {Performs a BubbleSort on a 0 based array containing a number of Elements.}

   procedure DoBubbleSort(Sender: TObject; Elements: Word; Ascending: Boolean = True);

   {Performs a ShellSort on a 0 based array containing a number of Elements.}

   procedure DoShellSort(Sender: TObject; Elements: Word; Ascending: Boolean = True);

   {Performs a ShakerSort on a 0 based array containing a number of Elements.}

 //procedure DoShakerSort(Sender: TObject; Elements: Word; Ascending: Boolean = True);                    //Not Implemented



   {The destructor.}

   destructor Destroy; override;

 published

   {OnCompare is the The @Link(TCompareEvent) that is used to compare two values to be ordered.}

   property OnCompare: TCompareEvent read FOnCompare write FOnCompare;

   {OnSWap is the the @Link(TCompareEvent) that is used to swap two values.}

   property OnSwap: TSwapEvent read FOnSwap write FOnSwap;

   {Returns True if both Events are assigned and thus sorting is possible.}

   property CanSort: Boolean read GetCanSort;

 end;


The implementation:

CODE
constructor TSort.Create(compare: TCompareEvent; swap: TSwapEvent);

begin

 inherited Create();



 FOnCompare := compare;

 FOnSwap := swap;

end;



{-----------------------------------------------}



function TSort.GetCanSort: Boolean;

begin

 Result := Assigned(FOnCompare) and Assigned(FOnSwap);

end;



{-----------------------------------------------}



procedure TSort.DoQuickSort(Sender: TObject; Elements: Word; Ascending: Boolean = True);



 function Compare(e1, e2: Word): Integer;

 begin

//  OutputDebugString(Pchar(Format('Comparing :%2.2d and :%2.2d', [e1, e2])));



   FOnCompare(Sender, e1, e2, Result);

 end;



 procedure Swap(e1, e2: Word);

 begin

//  OutputDebugString(Pchar(Format('Swapping :%2.2d and :%2.2d', [e1, e2])));



   FOnSwap(Sender, e1, e2);

 end;



 procedure QuickSort(lower, upper: Word);                                      //Fails with Word...

 var

   i, j, pivot     : Integer;

 begin

   if (lower < upper) then                                                     //This test fails when using words and no safeguard down below.

     begin

       i := lower;

       j := upper;

       pivot := j;



     //if (lower < 0) then

       //OutputDebugString(Pchar(Format('QuickSort lower:%2.2d, upper:%2.2d, pivot:%2.2d', [lower, upper, pivot])));



       repeat

         while (i < j) and (Compare(i, pivot) <= 0) do                         //a<b

           Inc(i);                                                             { Parting for left }

         while (i < j) and (Compare(j, pivot) >= 0) do

           Dec(j);                                                             { Parting for right}

         if (i < j) then Swap(i, j);

       until (i >= j);



       Swap(i, upper);



       if (i - lower) > (upper - i) then

         begin

           if (i > 0) then

             QuickSort(lower, i - 1);

           QuickSort(i + 1, upper);

         end

       else

         begin

           QuickSort(i + 1, upper);

           if (i > 0) then

             QuickSort(lower, i - 1);

         end;

     end;

 end;



begin

 if CanSort then

   QuickSort(0, Elements - 1);

end;



{-----------------------------------------------}



procedure TSort.DoBubbleSort(Sender: TObject; Elements: Word; Ascending: Boolean = True);



 function Compare(e1, e2: Word): Integer;

 begin

   FOnCompare(Sender, e1, e2, Result);

 end;



 procedure Swap(e1, e2: Word);

 begin

   FOnSwap(Sender, e1, e2);

 end;



 procedure BubbleSort;

 var

   i, j            : Word;

   swapped         : Boolean;

   m               : Integer;

 begin

   if ascending then

     m := 1

   else

     m := -1;



   if CanSort then

     begin

       i := 0;

       swapped := True;

       while (i < Pred(Elements)) and swapped do

         begin

           j := Pred(Elements);

           while (j > i) do

             begin

               if (m * Compare(j, j - 1) < 0) then

                 begin

                   swapped := True;

                   Swap(j, j - 1);

                 end;

               Dec(j);

             end;

           Inc(i);

         end;

     end;

 end;



begin

 if CanSort then

   BubbleSort;

end;



{-----------------------------------------------}



//procedure TSort.DoShakerSort(Sender: TObject; uNElem: Word; Ascending: Boolean = True);

//begin

//end;



{-----------------------------------------------}



procedure TSort.DoShellSort(Sender: TObject; Elements: Word; Ascending: Boolean = True);



 function Compare(e1, e2: Word): Integer;

 begin

   FOnCompare(Sender, e1 - 1, e2 - 1, Result);

 end;



 procedure Swap(e1, e2: Word);

 begin

   FOnSwap(Sender, e1 - 1, e2 - 1);

 end;



 procedure ShellSort(NumberItems, Distance: Integer);

 var

   X, Y            : Integer;

   m               : Integer;

 begin

   if ascending then

     m := 1

   else

     m := -1;

   for Y := Distance + 1 to NumberItems do

     begin

       X := Y - Distance;

       while X > 0 do

         begin

           if m * Compare(X + Distance, X) = -1 then

             begin

               Swap(X + Distance, X);

               X := X - Distance;

             end

           else

             X := 0;

         end;

     end

 end;



var

 Distance          : Integer;



begin

 if CanSort then

   begin

     Distance := Pred(Elements) div 2;

     while Distance > 0 do

       begin

         ShellSort(Elements, Distance);

         Distance := Distance div 2

       end;

   end

end;



{-----------------------------------------------}



destructor TSort.Destroy;

begin



 inherited;

end;


To sort random numbers in a TMemo, just use the following code:

CODE
procedure TForm1.DoCompare(Sender: TObject; e1, e2: Word;

 var Action: Integer);

begin

 if (StrToInt(Memo1.Lines[e1]) < StrToInt(Memo1.Lines[e2])) then

   Action := -1

 else if (StrToInt(Memo1.Lines[e1]) > StrToInt(Memo1.Lines[e2])) then

   Action := 1

 else

   Action := 0;

end;



procedure TForm1.DoSwap(Sender: TObject; e1, e2: Word);

begin

 Memo1.Lines.Exchange(e1, e2);

end;



procedure TForm1.Button1Click(Sender: TObject);

var

 i, j              : Integer;

 sort              : TSort;

begin

 Memo1.DoubleBuffered := True;



 sort := TSort.Create(DoCompare, DoSwap);

 if not sort.CanSort then Exit;



 Randomize;



 //sort 100 times

 for j := 1 to 100 do

   begin

     Memo1.Clear;



     //create demo data

     Memo1.Lines.BeginUpdate;

     for i := 0 to 100 do

       Memo1.Lines.Add(IntToStr(Random(1000)));

     Memo1.Lines.EndUpdate;



     //sort

     Memo1.Lines.BeginUpdate;

     sort.DoShellSort(Self, Memo1.Lines.Count);

     Memo1.Lines.EndUpdate;



     //test if sorted

     for i := 0 to Pred(Memo1.Lines.Count) - 1 do

       if StrToInt(Memo1.Lines[i]) > StrToInt(Memo1.Lines[i + 1]) then

         begin

           ShowMessage('Sort Error:' + IntToStr(i));

           Break;

         end;

   end;

end;



Small Update
------------
Hi

I saw that i had disabled descending sorting in quicksort all together. Here a modification that seems to work (basically it's negating the compare function).

Just change the first lines of the DoQuickSort routine into :

CODE
procedure TSort.DoQuickSort(Sender: TObject; Elements: Word; Ascending: Boolean = True);



var

 m                 : Integer;



 function Compare(e1, e2: Word): Integer;

 var

   r               : Integer;

 begin

   FOnCompare(Sender, e1, e2, r);

   Result := m * r;

 end;



 procedure Swap(e1, e2: Word);

 begin

   FOnSwap(Sender, e1, e2);

 end;



 procedure QuickSort(lower, upper: Word);                                      //Fails with Word...

 var

   i, j, pivot     : Integer;

 begin

   if ascending then

     m := 1

   else

     m := -1;



   if (lower < upper) then                                                     //This test fails when using words and no safeguard down below.

G.W. van der Vegt




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users