diff options
author | Staubfinger <staubfingercodinggames@gmail.com> | 2023-01-08 14:55:54 +0100 |
---|---|---|
committer | Jelle Raaijmakers <jelle@gmta.nl> | 2023-02-03 19:04:15 +0100 |
commit | da1023fcc59a5edc3e47b6f88763e1ded9477564 (patch) | |
tree | 89394422253ac1cf49f46985cf3f638a8b37099d /AK | |
parent | 6b9344e86c8cf053cbef58aa57f04e738543a63d (diff) | |
download | serenity-da1023fcc59a5edc3e47b6f88763e1ded9477564.zip |
AK: Add thresholds to `quickselect_inline` and `Statistics::Median`
I did a bit of Profiling and made the quickselect and median algorithms
use the best of option for the respective input size.
Diffstat (limited to 'AK')
-rw-r--r-- | AK/QuickSelect.h | 13 | ||||
-rw-r--r-- | AK/Statistics.h | 12 |
2 files changed, 21 insertions, 4 deletions
diff --git a/AK/QuickSelect.h b/AK/QuickSelect.h index 81a7973e10..bd11c11ded 100644 --- a/AK/QuickSelect.h +++ b/AK/QuickSelect.h @@ -11,6 +11,9 @@ #include <AK/StdLibExtras.h> namespace AK { + +static constexpr int MEDIAN_OF_MEDIAN_CUTOFF = 4500; + // FIXME: Stole and adapted these two functions from `Userland/Demos/Tubes/Tubes.cpp` we really need something like this in `AK/Random.h` static inline double random_double() { @@ -161,9 +164,13 @@ size_t quickselect_inplace(Collection& collection, size_t k, PivotFn pivot_fn) template<typename Collection> size_t quickselect_inplace(Collection& collection, size_t k) { - // By default, lets use middle_element to match `quicksort` - return quickselect_inplace( - collection, 0, collection.size() - 1, k, [](auto collection, size_t left, size_t right, auto less_than) { return PivotFunctions::middle_element(collection, left, right, less_than); }, [](auto& a, auto& b) { return a < b; }); + if (collection.size() >= MEDIAN_OF_MEDIAN_CUTOFF) + return quickselect_inplace( + collection, 0, collection.size() - 1, k, [](auto collection, size_t left, size_t right, auto less_than) { return PivotFunctions::median_of_medians(collection, left, right, less_than); }, [](auto& a, auto& b) { return a < b; }); + + else + return quickselect_inplace( + collection, 0, collection.size() - 1, k, [](auto collection, size_t left, size_t right, auto less_than) { return PivotFunctions::random_element(collection, left, right, less_than); }, [](auto& a, auto& b) { return a < b; }); } } diff --git a/AK/Statistics.h b/AK/Statistics.h index 93df7bd110..0c193d7ed4 100644 --- a/AK/Statistics.h +++ b/AK/Statistics.h @@ -9,10 +9,14 @@ #include <AK/Concepts.h> #include <AK/Math.h> #include <AK/QuickSelect.h> +#include <AK/QuickSort.h> #include <AK/Vector.h> namespace AK { +static constexpr int ODD_NAIVE_MEDIAN_CUTOFF = 200; +static constexpr int EVEN_NAIVE_MEDIAN_CUTOFF = 350; + template<Arithmetic T = float> class Statistics { public: @@ -75,7 +79,13 @@ public: return 0; // If the number of values is even, the median is the arithmetic mean of the two middle values - if (size() % 2 == 0) { + if (size() <= EVEN_NAIVE_MEDIAN_CUTOFF && size() % 2 == 0) { + quick_sort(m_values); + return (m_values.at(size() / 2) + m_values.at(size() / 2 - 1)) / 2; + } else if (size() <= ODD_NAIVE_MEDIAN_CUTOFF && size() % 2 == 1) { + quick_sort(m_values); + return m_values.at(m_values.size() / 2); + } else if (size() % 2 == 0) { auto index = size() / 2; auto median1 = m_values.at(AK::quickselect_inplace(m_values, index)); auto median2 = m_values.at(AK::quickselect_inplace(m_values, index - 1)); |