381 lines
15 KiB
Plaintext
381 lines
15 KiB
Plaintext
//:://////////////////////////////////////////////
|
|
//:: Array sorting functions
|
|
//:: inc_array_sort
|
|
//:://////////////////////////////////////////////
|
|
/** @file
|
|
A bunch of sorting functions for different
|
|
data types.
|
|
|
|
TMI may occur if attempting to sort too
|
|
large arrays.
|
|
For the quicksorts, 100 elements should always
|
|
be safe. TMI becomes almost certain past 150 elements.
|
|
The counting sort can handle 200 elements, with
|
|
250 being near upper limit.
|
|
It is not recommended that one directly use the
|
|
insertion sort, but 50 elements are probably safe.
|
|
|
|
Array implementation is assumed to follow
|
|
certain constraints:
|
|
- Array elements begin at index 0
|
|
- The value returned by the function to get
|
|
array size returns the number of elements
|
|
in the array.
|
|
- The index of the last element in the array
|
|
is the number of elements - 1.
|
|
- Array reads change no stored data
|
|
- Array writes are allowed to write over
|
|
existing entries.
|
|
|
|
@author Ornedan
|
|
@data Created 2006.05.27
|
|
*/
|
|
//:://////////////////////////////////////////////
|
|
//:://////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Constants */
|
|
//////////////////////////////////////////////////
|
|
|
|
/// At a certain point when the sort range is small enough, the overhead of quicksort exceeds
|
|
/// the higher order of efficiency in comparison to insertion sort, which has minimal overhead,
|
|
/// but still decent performance. At that point, switch to insertion sort.
|
|
const int QUICKSORT_TO_INSERTIONSORT_TRESHOLD = 6; // Anything betweem 4 - 10 seems to work. 6 was best in testing with randomly created arrays
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Function prototypes */
|
|
//////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Implements a quicksort for integers. The array given for sorting should only contain
|
|
* integer elements. Sane results not guaranteed otherwise.
|
|
*
|
|
* @param oStore The object the array to sort is stored on
|
|
* @param sArrayName The name of the array to sort
|
|
*
|
|
* @param nLower The lower sort bound. Set by the function itself to 0 for recursive calls if
|
|
* left to default value (-1).
|
|
* @param nUpper The upper sort bound. Set by the function itself to array size - 1 for recursive
|
|
* calls if left to default value (-1).
|
|
*/
|
|
void QuickSortInt(object oStore, string sArrayName, int nLower = -1, int nUpper = -1);
|
|
|
|
/**
|
|
* Implements a quicksort for floating point numbers. The array given for sorting should only contain
|
|
* float elements. Sane results not guaranteed otherwise.
|
|
*
|
|
* @param oStore The object the array to sort is stored on
|
|
* @param sArrayName The name of the array to sort
|
|
*
|
|
* @param nLower The lower sort bound. Set by the function itself to 0 for recursive calls if
|
|
* left to default value (-1).
|
|
* @param nUpper The upper sort bound. Set by the function itself to array size - 1 for recursive
|
|
* calls if left to default value (-1).
|
|
*/
|
|
void QuickSortFloat(object oStore, string sArrayName, int nLower = -1, int nUpper = -1);
|
|
|
|
/**
|
|
* Implements an insertion sort for integers. The array given for sorting should only contain
|
|
* integer elements. Sane results not guaranteed otherwise.
|
|
*
|
|
* @param oStore The object the array to sort is stored on
|
|
* @param sArrayName The name of the array to sort
|
|
*
|
|
* @param nLower The lower sort bound. Set by the function itself to 0 for recursive calls if
|
|
* left to default value (-1).
|
|
* @param nUpper The upper sort bound. Set by the function itself to array size - 1 for recursive
|
|
* calls if left to default value (-1).
|
|
*/
|
|
void InsertionSortInt(object oStore, string sArrayName, int nLower = -1, int nUpper = -1);
|
|
|
|
/**
|
|
* Implements an insertion sort for floating point numbers. The array given for sorting should only contain
|
|
* float elements. Sane results not guaranteed otherwise.
|
|
*
|
|
* @param oStore The object the array to sort is stored on
|
|
* @param sArrayName The name of the array to sort
|
|
*
|
|
* @param nLower The lower sort bound. Set by the function itself to 0 for recursive calls if
|
|
* left to default value (-1).
|
|
* @param nUpper The upper sort bound. Set by the function itself to array size - 1 for recursive
|
|
* calls if left to default value (-1).
|
|
*/
|
|
void InsertionSortFloat(object oStore, string sArrayName, int nLower = -1, int nUpper = -1);
|
|
|
|
/**
|
|
* Implements counting sort for integers. The array given for sorting should only contain
|
|
* integer elements. Sane results not guaranteed otherwise.
|
|
*
|
|
* @param oStore The object the array to sort is stored on
|
|
* @param sArrayName The name of the array to sort
|
|
*/
|
|
void CountingSortInt(object oStore, string sArrayName);
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Includes */
|
|
//////////////////////////////////////////////////
|
|
|
|
#include "inc_array"
|
|
#include "inc_debug"
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Internal functions */
|
|
//////////////////////////////////////////////////
|
|
|
|
/** Internal function.
|
|
* Shuffles elements around until they are a bit more in order.
|
|
*
|
|
* @param oStore The object the array to sort is stored on
|
|
* @param sArrayName The name of the array to sort
|
|
* @param nLower The lower sort bound of the range to sort.
|
|
* @param nUpper The upper sort bound of the range to sort.
|
|
* @return The array index around which the array has been "sorted".
|
|
*/
|
|
int _inc_array_sort_PartitionInt(object oStore, string sArrayName, int nLower, int nUpper)
|
|
{
|
|
int nDivider, nSwap;
|
|
|
|
// Attempt to determine the median of the values in the array positions nLower, nMid and nUpper
|
|
int nLowerVal = array_get_int(oStore, sArrayName, nLower),
|
|
nMidVal = array_get_int(oStore, sArrayName, (nLower + nUpper) / 2),
|
|
nUpperVal = array_get_int(oStore, sArrayName, nUpper);
|
|
if(nLowerVal < nMidVal)
|
|
{
|
|
if(nLowerVal < nUpperVal) nDivider = (nMidVal < nUpperVal) ? nMidVal : nUpperVal;
|
|
else nDivider = nLowerVal;
|
|
}
|
|
else if(nLowerVal < nUpperVal) nDivider = nLowerVal;
|
|
else nDivider = (nMidVal > nUpperVal) ? nMidVal : nUpperVal;
|
|
|
|
// Loop to sort the array around the median value
|
|
nLower -= 1; nUpper += 1; // Hack - The loops below need to be pre-increment, so we need to move the index variables to make the first elements examined be the ones at the original values
|
|
while(TRUE)
|
|
{
|
|
while(array_get_int(oStore, sArrayName, ++nLower) < nDivider); // Seek an element in the lower range of the array that is lesser than the divider
|
|
while(array_get_int(oStore, sArrayName, --nUpper) > nDivider); // Seek an element in the upper range of the array that is greater than the divider
|
|
// If the indexes haven't passed each other yet, swap the elements
|
|
if(nLower < nUpper)
|
|
{
|
|
nSwap = array_get_int(oStore, sArrayName, nLower);
|
|
array_set_int(oStore, sArrayName, nLower, array_get_int(oStore, sArrayName, nUpper));
|
|
array_set_int(oStore, sArrayName, nUpper, nSwap);
|
|
}
|
|
// Otherwise, the array is now arranged so that all elements at positions nUpper and higher are greater than or equal to the
|
|
// elements lower in the array
|
|
else
|
|
return nUpper;
|
|
}
|
|
|
|
// Never going to reach here, but compiler can't figure that out :P
|
|
Assert(FALSE, "FALSE", "Execution reached code that shouldn't be reachable", "inc_array_sort", "_inc_arrays_sort_PartitionInt");
|
|
return -1;
|
|
}
|
|
|
|
/** Internal function.
|
|
* Shuffles elements around until they are a bit more in order.
|
|
*
|
|
* @param oStore The object the array to sort is stored on
|
|
* @param sArrayName The name of the array to sort
|
|
* @param nLower The lower sort bound of the range to sort.
|
|
* @param nUpper The upper sort bound of the range to sort.
|
|
* @return The array index around which the array has been "sorted".
|
|
*/
|
|
int _inc_array_sort_PartitionFloat(object oStore, string sArrayName, int nLower, int nUpper)
|
|
{
|
|
float fDivider, fSwap;
|
|
|
|
// Attempt to determine the median of the values in the array positions nLower, nMid and nUpper
|
|
float fLowerVal = array_get_float(oStore, sArrayName, nLower),
|
|
fMidVal = array_get_float(oStore, sArrayName, (nLower + nUpper) / 2),
|
|
fUpperVal = array_get_float(oStore, sArrayName, nUpper);
|
|
if(fLowerVal < fMidVal)
|
|
{
|
|
if(fLowerVal < fUpperVal) fDivider = (fMidVal < fUpperVal) ? fMidVal : fUpperVal;
|
|
else fDivider = fLowerVal;
|
|
}
|
|
else if(fLowerVal < fUpperVal) fDivider = fLowerVal;
|
|
else fDivider = (fMidVal > fUpperVal) ? fMidVal : fUpperVal;
|
|
|
|
// Loop to sort the array around the median value
|
|
nLower -= 1; nUpper += 1; // Hack - The loops below need to be pre-increment, so we need to move the index variables to make the first elements examined be the ones at the original values
|
|
while(TRUE)
|
|
{
|
|
while(array_get_float(oStore, sArrayName, ++nLower) < fDivider); // Seek an element in the lower range of the array that is lesser than the divider
|
|
while(array_get_float(oStore, sArrayName, --nUpper) > fDivider); // Seek an element in the upper range of the array that is greater than the divider
|
|
// If the indexes haven't passed each other yet, swap the elements
|
|
if(nLower < nUpper)
|
|
{
|
|
fSwap = array_get_float(oStore, sArrayName, nLower);
|
|
array_set_float(oStore, sArrayName, nLower, array_get_float(oStore, sArrayName, nUpper));
|
|
array_set_float(oStore, sArrayName, nUpper, fSwap);
|
|
}
|
|
// Otherwise, the array is now arranged so that all elements at positions nUpper and higher are greater than or equal to the
|
|
// elements lower in the array
|
|
else
|
|
return nUpper;
|
|
}
|
|
|
|
// Never going to reach here, but compiler can't figure that out :P
|
|
Assert(FALSE, "FALSE", "Execution reached code that shouldn't be reachable", "inc_array_sort", "_inc_array_sort_PartitionFloat");
|
|
return -1;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Function definitions */
|
|
//////////////////////////////////////////////////
|
|
|
|
void QuickSortInt(object oStore, string sArrayName, int nLower = -1, int nUpper = -1)
|
|
{
|
|
// If range limits are not given, initialise them to defaults
|
|
if(nLower == -1) nLower = 0;
|
|
if(nUpper == -1) nUpper = array_get_size(oStore, sArrayName) - 1;
|
|
|
|
// See if we have reached the point when quicksort becomes less efficient than insertion sort.
|
|
if((nUpper - nLower) <= QUICKSORT_TO_INSERTIONSORT_TRESHOLD)
|
|
InsertionSortInt(oStore, sArrayName, nLower, nUpper);
|
|
else
|
|
{
|
|
// Move entries into a slightly more sorted order by arranging them around a median value
|
|
// nDivider is the position of the beginning of the range where all the elements are
|
|
// greater or equal to the median used
|
|
int nDivider = _inc_array_sort_PartitionInt(oStore, sArrayName, nLower, nUpper);
|
|
|
|
// Recurse into the halves of the array generated by the above sorting
|
|
QuickSortInt(oStore, sArrayName, nLower, nDivider);
|
|
QuickSortInt(oStore, sArrayName, nDivider + 1, nUpper);
|
|
}
|
|
}
|
|
|
|
void QuickSortFloat(object oStore, string sArrayName, int nLower = -1, int nUpper = -1)
|
|
{
|
|
// If range limits are not given, initialise them to defaults
|
|
if(nLower == -1) nLower = 0;
|
|
if(nUpper == -1) nUpper = array_get_size(oStore, sArrayName) - 1;
|
|
|
|
// See if we have reached the point when quicksort becomes less efficient than insertion sort.
|
|
if((nUpper - nLower) <= QUICKSORT_TO_INSERTIONSORT_TRESHOLD)
|
|
InsertionSortFloat(oStore, sArrayName, nLower, nUpper);
|
|
else
|
|
{
|
|
// Move entries into a slightly more sorted order by arranging them around a median value
|
|
// nDivider is the position of the beginning of the range where all the elements are
|
|
// greater or equal to the median used
|
|
int nDivider = _inc_array_sort_PartitionFloat(oStore, sArrayName, nLower, nUpper);
|
|
|
|
// Recurse into the halves of the array generated by the above sorting
|
|
QuickSortFloat(oStore, sArrayName, nLower, nDivider);
|
|
QuickSortFloat(oStore, sArrayName, nDivider + 1, nUpper);
|
|
}
|
|
}
|
|
|
|
void InsertionSortInt(object oStore, string sArrayName, int nLower = -1, int nUpper = -1)
|
|
{
|
|
// If range limits are not given, initialise them to defaults
|
|
if(nLower == -1) nLower = 0;
|
|
if(nUpper == -1) nUpper = array_get_size(oStore, sArrayName) - 1;
|
|
|
|
// Some variables
|
|
int i, nSwap;
|
|
|
|
// Run the insertion sort loop
|
|
for(nLower += 1; nLower <= nUpper; nLower++)
|
|
{
|
|
// Store current entry in temporary variable
|
|
nSwap = array_get_int(oStore, sArrayName, nLower);
|
|
|
|
// Move preceding elements forward by one until we encounter index 0 or an element <= nSwap,
|
|
// whereupon we insert the swapped out element
|
|
i = nLower;
|
|
while(i > 0 && array_get_int(oStore, sArrayName, i - 1) > nSwap)
|
|
{
|
|
array_set_int(oStore, sArrayName, i,
|
|
array_get_int(oStore, sArrayName, i - 1)
|
|
);
|
|
i--;
|
|
}
|
|
|
|
// Insert the swapped out element at the position where all elements with index less than it's are lesser than or equal to it
|
|
array_set_int(oStore, sArrayName, i, nSwap);
|
|
}
|
|
}
|
|
|
|
void InsertionSortFloat(object oStore, string sArrayName, int nLower = -1, int nUpper = -1)
|
|
{
|
|
// If range limits are not given, initialise them to defaults
|
|
if(nLower == -1) nLower = 0;
|
|
if(nUpper == -1) nUpper = array_get_size(oStore, sArrayName) - 1;
|
|
|
|
// Some variables
|
|
int i;
|
|
float fSwap;
|
|
|
|
// Run the insertion sort loop
|
|
for(nLower += 1; nLower <= nUpper; nLower++)
|
|
{
|
|
// Store current entry in temporary variable
|
|
fSwap = array_get_float(oStore, sArrayName, nLower);
|
|
|
|
// Move preceding elements forward by one until we encounter index 0 or an element <= nSwap,
|
|
// whereupon we insert the swapped out element
|
|
i = nLower;
|
|
while(i > 0 && array_get_float(oStore, sArrayName, i - 1) > fSwap)
|
|
{
|
|
array_set_float(oStore, sArrayName, i,
|
|
array_get_float(oStore, sArrayName, i - 1)
|
|
);
|
|
i--;
|
|
}
|
|
|
|
// Insert the swapped out element at the position where all elements with index less than it's are lesser than or equal to it
|
|
array_set_float(oStore, sArrayName, i, fSwap);
|
|
}
|
|
}
|
|
|
|
void CountingSortInt(object oStore, string sArrayName)
|
|
{
|
|
int nMin = 0, nMax = 0,
|
|
nCount = 0,
|
|
i, size = array_get_size(oStore, sArrayName),
|
|
nTemp;
|
|
|
|
/* Find the least and greatest elements of the array */
|
|
for(i = 0; i < size; i++)
|
|
{
|
|
nTemp = array_get_int(oStore, sArrayName, i);
|
|
if(nTemp < nMin) nMin = nTemp;
|
|
if(nTemp > nMax) nMax = nTemp;
|
|
}
|
|
|
|
// Create temporary array
|
|
string sTempArray = "_CSort";
|
|
while(array_exists(oStore, sTempArray)) sTempArray += "_";
|
|
array_create(oStore, sTempArray);
|
|
|
|
// Get the amount of each number in the array
|
|
for(i = 0; i < size; i++)
|
|
{
|
|
nTemp = array_get_int(oStore, sArrayName, i) - nMin;
|
|
array_set_int(oStore, sTempArray, nTemp, array_get_int(oStore, sTempArray, nTemp) + 1);
|
|
}
|
|
|
|
// Set the values in the sortable array
|
|
size = array_get_size(oStore, sTempArray);
|
|
for(i = 0; i < size; i++)
|
|
{
|
|
while(array_get_int(oStore, sTempArray, i) > 0)
|
|
{
|
|
array_set_int(oStore, sArrayName, nCount++, i + nMin);
|
|
array_set_int(oStore, sTempArray, i, array_get_int(oStore, sTempArray, i) - 1);
|
|
}
|
|
}
|
|
|
|
// Delete the temporary array
|
|
array_delete(oStore, sTempArray);
|
|
}
|
|
|
|
|
|
// Test main
|
|
//void main(){}
|