mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Add quickselect (#1654)
* sort: extract partition from quicksort Extract the partition logic from quicksort into a macro. This allows to reuse the partition logic for, e.g., the quickselect algorithm. * sort: implement quickselect implement Hoare's selection algorithm (quickselect) on the basis of the already implemented quicksort. Quickselect allows to find the kth smallest element in a unordered list with an average time complexity of O(N) (worst case: O(N^2)). * add quicksort benchmark Create a top-level benchmarks folder. Add the benchmark implementation for the quicksort algorithm. Benchmarks can then be run in the same way as unit tests from the root folder with: c3c compile-benchmarks benchmarks/stdlib/sort
This commit is contained in:
@@ -13,6 +13,21 @@ macro quicksort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @built
|
||||
qs::qsort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len - 1, cmp, context);
|
||||
}
|
||||
|
||||
<*
|
||||
Select the (k+1)th smallest element in an unordered list using Hoare's
|
||||
selection algorithm (Quickselect). k should be between 0 and len-1. The data
|
||||
list will be partially sorted.
|
||||
|
||||
@require @is_sortable(list) "The list must be indexable and support .len or .len()"
|
||||
@require @is_valid_cmp_fn(cmp, list, context) "expected a comparison function which compares values"
|
||||
@require @is_valid_context(cmp, context) "Expected a valid context"
|
||||
*>
|
||||
macro quickselect(list, isz k, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
|
||||
{
|
||||
usz len = sort::@len_from_list(list);
|
||||
return qs::qselect(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len - 1, k, cmp, context);
|
||||
}
|
||||
|
||||
module std::sort::qs(<Type, CmpFn, Context>);
|
||||
|
||||
def ElementType = $typeof(Type{}[0]);
|
||||
@@ -29,10 +44,6 @@ def Stack = StackElementItem[64] @private;
|
||||
|
||||
fn void qsort(Type list, isz low, isz high, CmpFn cmp, Context context)
|
||||
{
|
||||
var $has_cmp = @is_valid_macro_slot(cmp);
|
||||
var $has_context = @is_valid_macro_slot(context);
|
||||
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type));
|
||||
|
||||
if (low >= 0 && high >= 0 && low < high)
|
||||
{
|
||||
Stack stack;
|
||||
@@ -48,34 +59,7 @@ fn void qsort(Type list, isz low, isz high, CmpFn cmp, Context context)
|
||||
|
||||
if (l < h)
|
||||
{
|
||||
ElementType pivot = list[l];
|
||||
while (l < h)
|
||||
{
|
||||
$switch
|
||||
$case $cmp_by_value && $has_context:
|
||||
while (cmp(list[h], pivot, context) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(list[l], pivot, context) <= 0 && l < h) l++;
|
||||
$case $cmp_by_value:
|
||||
while (cmp(list[h], pivot) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(list[l], pivot) <= 0 && l < h) l++;
|
||||
$case $has_cmp && $has_context:
|
||||
while (cmp(&list[h], &pivot, context) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(&list[l], &pivot, context) <= 0 && l < h) l++;
|
||||
$case $has_cmp:
|
||||
while (cmp(&list[h], &pivot) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(&list[l], &pivot) <= 0 && l < h) l++;
|
||||
$default:
|
||||
while (greater_eq(list[h], pivot) && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (less_eq(list[l], pivot) && l < h) l++;
|
||||
$endswitch
|
||||
if (l < h) list[h--] = list[l];
|
||||
}
|
||||
list[l] = pivot;
|
||||
l = @partition(list, l, h, cmp, context);
|
||||
stack[i + 1].low = l + 1;
|
||||
stack[i + 1].high = stack[i].high;
|
||||
stack[i++].high = l;
|
||||
@@ -91,3 +75,71 @@ fn void qsort(Type list, isz low, isz high, CmpFn cmp, Context context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@require low <= k "kth smalles element is smaller than lower bounds"
|
||||
@require k <= high "kth smalles element is larger than upper bounds"
|
||||
*>
|
||||
fn ElementType! qselect(Type list, isz low, isz high, isz k, CmpFn cmp, Context context)
|
||||
{
|
||||
if (low >= 0 && high >= 0 && low < high)
|
||||
{
|
||||
isz l = low;
|
||||
isz h = high;
|
||||
isz pivot;
|
||||
|
||||
usz max_retries = 64;
|
||||
while (l <= h && max_retries--)
|
||||
{
|
||||
pivot = @partition(list, l, h, cmp, context);
|
||||
if (k == pivot) return list[k];
|
||||
if (k < pivot)
|
||||
{
|
||||
h = pivot - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
l = pivot + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
macro @partition(Type list, isz l, isz h, CmpFn cmp, Context context)
|
||||
{
|
||||
var $has_cmp = @is_valid_macro_slot(cmp);
|
||||
var $has_context = @is_valid_macro_slot(context);
|
||||
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type));
|
||||
|
||||
ElementType pivot = list[l];
|
||||
while (l < h)
|
||||
{
|
||||
$switch
|
||||
$case $cmp_by_value && $has_context:
|
||||
while (cmp(list[h], pivot, context) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(list[l], pivot, context) <= 0 && l < h) l++;
|
||||
$case $cmp_by_value:
|
||||
while (cmp(list[h], pivot) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(list[l], pivot) <= 0 && l < h) l++;
|
||||
$case $has_cmp && $has_context:
|
||||
while (cmp(&list[h], &pivot, context) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(&list[l], &pivot, context) <= 0 && l < h) l++;
|
||||
$case $has_cmp:
|
||||
while (cmp(&list[h], &pivot) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(&list[l], &pivot) <= 0 && l < h) l++;
|
||||
$default:
|
||||
while (greater_eq(list[h], pivot) && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (less_eq(list[l], pivot) && l < h) l++;
|
||||
$endswitch
|
||||
if (l < h) list[h--] = list[l];
|
||||
}
|
||||
list[l] = pivot;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user