Skip to content

libc++ std::nth_element is quadratic, should be linear #52747

@orlp

Description

@orlp

The reproduction program is almost the same as my 7 year old one for std::sort that got fixed only recently:

#include <algorithm>
#include <iostream>
#include <vector>

void make_killer(int size, std::vector<int>& v) {
    int candidate = 0;
    int num_solid = 0;
    int gas = size - 1;

    std::vector<int> tmp(size);
    v.resize(size);

    for (int i = 0; i < size; ++i) {
        tmp[i] = i;
        v[i] = gas;
    }

    std::nth_element(tmp.begin(), tmp.end() - 2, tmp.end(), [&](int x, int y) {
        if (v[x] == gas && v[y] == gas) {
            if (x == candidate) v[x] = num_solid++;
            else v[y] = num_solid++;
        }

        if (v[x] == gas) candidate = x;
        else if (v[y] == gas) candidate = y;

        return v[x] < v[y];
    });
}

int main(int argc, char** argv) {
    std::vector<int> v;
    int comparison_count;

    auto counter = [&](int x, int y) { ++comparison_count; return x < y; };

    std::cout << "N: comparisons\n";
    for (int i = 100; i <= 6400; i *= 2) {
        // to nullify small constants we multiply by 100
        make_killer(i, v);

        comparison_count = 0;
        std::nth_element(v.begin(), v.end() - 2, v.end(), counter);
        std::cout << i << ": " << comparison_count << "\n";
    }

    return 0;
}

The output:

N: comparisons
100: 2741
200: 10491
400: 40991
800: 161991
1600: 643991
3200: 2567991
6400: 10255991

Evidently quadratic, but the standard requires:

Complexity: For the overloads with no ExecutionPolicy, linear on average. For the overloads
with an ExecutionPolicy, O(N) applications of the predicate, and O(N log N) swaps, where N = last - first.

The problem is that pure quickselect is implemented without a fallback for worst cases, like median of medians.

Metadata

Metadata

Assignees

No one assigned

    Labels

    confirmedVerified by a second partylibc++libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions