如何从数组c ++中获取'n'个随机值

时间:2015-04-24 09:36:26

标签: python c++ arrays random

我想从数组中提取n个随机值。 我可以使用x=random.sample(listName, numberOfValuesNeeded)

在Python中完成
[code]
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <algorithm>
#include <iterator>

using namespace std;

int main()
{
    const int MAX_Name=100;

    srand((unsigned)time( NULL));

    // Names
    string PsDisp[MAX_Name]={"Alberto", "Bruno", "Carlo", "Dario", "Elio", "Francesco", "Giovanni", "Luca", "Marco", "Nicola", "Oreste", "Pietro",   "Rino", "Sandro", "Tonino", "Valerio", "Vittorio"};


    for(int x=1; x<=6; x++)
    {
        random_shuffle(begin(PsDisp), end(PsDisp));

        cout << x << ": " << endl; // snomething missing before endl
    }

    return 0;
}

我想在屏幕上打印:

  

1:随机名称1   2:随机名3 ...
  6:随机名称6

3 个答案:

答案 0 :(得分:1)

Python的random.sample返回样本,无需替换。这意味着你不能两次获得相同的元素。

您的算法显然不会这样做。如果在获得每个值后重新洗牌,那就是替换值。但是有一种简单的方法可以解决这个问题:只需在循环外进行一次洗牌,而不是每次循环。

这意味着循环很简单:你只想在混洗的dispNames中打印前6个数字。所以:

random_shuffle(begin(dispNames), end(dispNames));
for (int x=1; x<=6; x++) {
    cout << x << ": " << dispNames[x-1] << endl;
}

一些附注:

  • random_shuffle已弃用。具有默认PRNG的shuffle也是如此。这是有充分理由的;即使您不关心加密随机性或任何花哨的东西,您仍然希望使用特定的PRNG和shuffle的新版本,只是为了便于统一分发。
  • 0<6计算比从1计算到<=6更为正常。这意味着您不需要x-1
  • 使用迭代器而不是索引会更好;即使用其他随机可访问类型而不是数组替换dispNames,相同的代码也会起作用。

从17个清单中挑选6个样本,这很好。 (嗯,只要破坏性地修改数组是可以接受的,Python的random.sample不会这样做......但如果这是一个问题,用复制shuffle替换它或者只是复制加上并不难破坏性的洗牌......)

但如果你的价值变得太大,你需要考虑效率。混乱本身需要O(N)时间,因为它必须洗牌大部分N值。但是从N中选择K值不应该花那么长时间。 (如果你切换到复制shuffle,你也可以添加O(N)空间,这也不是必需的。)

我认为C ++ stdlib中没有相应的东西,但是没关系;值得学习如何自己实现这一点,这样你才能理解它在做什么。有几个不同的选择:您可以在置换生成器(C ++在stdlib中具有它)之上构建它,或者在一个普通的线性同余生成器之上构建它,使下一个素数大于列表的长度(丢弃)价值太大了),或部分费雪 - 耶茨洗牌而不是完整的,或者可能是其他我没有想过的事情。 (有些在特定的情况下是死的简单而且非常快,但不是一般的 - 例如,如果K很小,只选择替换并跟踪先前的选择很有效,但如果K是相当大的一块,那就太可怕了名词)

或者,当然,你可以read the CPython source,这是一个很好的方法来弄清楚如何编写与Python完全相同的C ++代码。

答案 1 :(得分:0)

如果您不关心重复,可以生成6个随机索引并打印相应的值,如下所示:

for(int x=1; x<=6; x++)
{
    cout << x << ": " << dispNames[rand() % MAX_Name] << endl;
}

答案 2 :(得分:0)

您可以使用名为Reservoir Sampling的技术。它只在输入上迭代一次,因此需要O(n)。这是您使用C ++ 11水库采样的示例代码:

#include <iostream>
#include <string>
#include <vector>
#include <random>
#include <iterator>
#include <algorithm>
#include <cassert>


template<class InputIt, class Generator>
std::vector<typename std::iterator_traits<InputIt>::value_type> sample(InputIt first, InputIt last, size_t n, Generator& engine) {
    assert(distance(first, last) >= n);
    InputIt nth = next(first, n);
    std::vector<typename std::iterator_traits<InputIt>::value_type> result{first, nth};
    size_t k=n+1;
    for (InputIt it = nth; it != last; ++it, ++k) {
        size_t r = std::uniform_int_distribution<size_t>{0, k}(engine);
        if (r<n)
            result[r] = *it;
    }
    return result;
}


using namespace std;

int main()
{

    string xs[] = {"Alberto", "Bruno", "Carlo", "Dario", "Elio", "Francesco", "Giovanni", "Luca", "Marco", "Nicola", "Oreste", "Pietro",   "Rino", "Sandro", "Tonino", "Valerio", "Vittorio"};

    mt19937 engine{random_device{}()};
    vector<string> ys = sample(begin(xs), end(xs), 5, engine);

    for (const string& s : ys)
        cout << s << endl;

    return 0;
}