我想从数组中提取n
个随机值。
我可以使用x=random.sample(listName, numberOfValuesNeeded)
[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
答案 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;
}