С C ++ 17 std::sample
делает именно то, что вам нужно, но для c ++ 98 это немного сложнее.
Самый короткий код, совместимый с c ++98 - это:
unsigned pick_below(unsigned n)
{
// poor distribution:
return std::rand() % n;
}
std::vector<std::pair<std::string, int> >
sample(const std::map<std::string, int> & data_in,
unsigned p)
{
std::vector<std::pair<std::string, int> > shuffled(data_in.begin(), data_in.end());
for (unsigned i=shuffled.size() ; i > 1 ; --i)
std::swap(shuffled[i-1], shuffled[pick_below(i)]);
shuffled.erase(shuffled.begin() +p, shuffled.end());
}
Этот код проблематичен на двух уровнях:
std::random
качество не гарантируется. - использование по модулю в pick_below искажает распределение .
Чтобы преодолеть проблему номер 2, либо используйте boost::random::uniform_int_distribution
, либо переписайте функцию pick_below
в соответствии с this :
unsigned pick_below(unsigned n)
{
unsigned x;
do {
x = rand();
} while (x >= (RAND_MAX - RAND_MAX % n));
return x % n;
}
Исправление проблемы 1 можно преодолеть с помощью стороннего генератора случайных чисел, такого как boost::random::mt19937
.
К сожалению, сложность этого решения в среднем составляет O (n) (поскольку pick_below
не гарантированно завершится, но при любом значении p < RAND_MAX / 2
вероятность его повторения более K раз экспоненциально уменьшается доменьше, чем 0,5 K . Сложность не может быть лучше, чем O (n), поскольку нет способа выбрать элемент * k th карты, если не выполнять итерацию всего этого.