PRC8/trunk/include/inc_array_sort.nss

381 lines
15 KiB
Plaintext
Raw Normal View History

//:://////////////////////////////////////////////
//:: 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(){}