MathPHP\Statistics\Average::kthSmallest PHP Method

kthSmallest() public static method

if $a = [1,2,3,4,6,7] kthSmallest($a, 4) = 6 Algorithm: 1) If n is small, just sort and return 2) Otherwise, group into 5-element subsets and mind the median 3) Find the median of the medians 4) Find L and U sets - L is numbers lower than the median of medians - U is numbers higher than the median of medians 5) Recursive step - if k is the median of medians, return that - Otherwise, recusively search in smaller group.
public static kthSmallest ( array $numbers, integer $k ) : number
$numbers array
$k integer zero indexed
return number
    public static function kthSmallest(array $numbers, int $k)
    {
        // If the numbers array is empty, or if k is out of bounds
        // return null. Should it be an exception instead?
        $n = count($numbers);
        if (empty($numbers) || $k >= $n) {
            return null;
        }
        // Reset the array key indexes because we don't know what might be passed in
        $numbers = array_values($numbers);
        // If the array is 5 elements or smaller, use quicksort and return the element of interest.
        if ($n <= 5) {
            sort($numbers);
            return $numbers[$k];
        }
        // Otherwise, we are going to slice $numbers into 5-element slices
        // and find the median of each.
        $num_slices = ceil($n / 5);
        for ($i = 0; $i < $num_slices; $i++) {
            $median_array[] = self::median(array_slice($numbers, 5 * $i, 5));
        }
        // Then we find the median of the medians.
        $median_of_medians = self::median($median_array);
        // Next we walk the array and seperate it into values that are greater than or less than
        // this "median of medians".
        $lower_upper = self::splitAtValue($numbers, $median_of_medians);
        $lower_number = count($lower_upper['lower']);
        $upper_number = count($lower_upper['upper']);
        $equal_number = $lower_upper['equal'];
        // Lastly, we find which group of values our value of interest is in, and find it in the
        // smaller array.
        if ($k < $lower_number) {
            return self::kthSmallest($lower_upper['lower'], $k);
        } elseif ($k < $lower_number + $equal_number) {
            return $median_of_medians;
        } else {
            return self::kthSmallest($lower_upper['upper'], $k - $lower_number - $equal_number);
        }
    }

Usage Example

Example #1
0
 /**
  * Evaluate for x
  * Use the smoothness parameter α to determine the subset of data to consider for
  * local regression. Perform a weighted least squares regression and evaluate x.
  *
  * @param  number $x
  *
  * @return number
  */
 public function evaluate($x)
 {
     $α = $this->α;
     $λ = $this->λ;
     $n = $this->n;
     // The number of points considered in the local regression
     $Δx = Single::abs(Single::subtract($this->xs, $x));
     $αᵗʰΔx = Average::kthSmallest($Δx, $this->number_of_points - 1);
     $arg = Single::min(Single::divide($Δx, $αᵗʰΔx * max($α, 1)), 1);
     // Kernel function: tricube = (1-arg³)³
     $tricube = Single::cube(Single::multiply(Single::subtract(Single::cube($arg), 1), -1));
     $weights = $tricube;
     // Local Regression Parameters
     $parameters = $this->leastSquares($this->ys, $this->xs, $weights, $λ);
     $X = new VandermondeMatrix([$x], $λ + 1);
     return $X->multiply($parameters)[0][0];
 }