我试图让它工作时遇到问题。我打算在1&之间挑选6个唯一的数字。 49.我有一个功能正确地执行此操作,但努力检查数组的重复和替换。
srand(static_cast<unsigned int>(time(NULL))); // Seeds a random number
int picked[6];
int number,i,j;
const int MAX_NUMBERS = 6;
for (i = 0; i < MAX_NUMBERS; i++)
{
number = numberGen();
for (int j = 0; j < MAX_NUMBERS; j++)
{
if (picked[i] == picked[j])
{
picked[j] = numberGen();
}
}
}
我的号码生成器只创建1到1之间的随机数。 49我认为工作正常。我刚刚开始使用C ++,任何帮助都会很棒
int numberGen()
{
int number = rand();
int target = (number % 49) + 1;
return target;
}
答案 0 :(得分:1)
让我们打破这段代码。
for (i = 0; i < MAX_NUMBERS; i++)
我们正在进行6次迭代的for循环。
number = numberGen();
我们正在生成一个新号码,并将其存储到变量number
中。此变量不会在其他任何地方使用。
for (int j = 0; j < MAX_NUMBERS; j++)
我们再次循环播放数组......
if (picked[i] == picked[j])
检查这两个值是否匹配(fyi,picked[n] == picked[n]
将始终匹配)
picked[j] = numberGen();
如果匹配,则为现有值分配新的随机数。
这里更好的方法是消除重复值(如果存在),然后将其分配给您的数组。例如:
for (i = 0; i < MAX_NUMBERS; i++)
{
bool isDuplicate = false;
do
{
number = numberGen(); // Generate the number
// Check for duplicates
for (int j = 0; j < MAX_NUMBERS; j++)
{
if (number == picked[j])
{
isDuplicate = true;
break; // Duplicate detected
}
}
}
while (isDuplicate); // equivalent to while(isDuplicate == true)
picked[j] = number;
}
在这里,我们运行一个do-while循环。循环的第一次迭代将生成一个随机数,并检查它是否已经在数组中重复。如果是,则重新运行循环,直到找到非重复。一旦循环中断,我们就有一个有效的,不重复的数字,然后我们将它分配给数组。
随着课程的进展,会有更好的解决方案。
答案 1 :(得分:1)
C ++ 17为此提供了一种算法(转换图):
template< class PopulationIterator, class SampleIterator, class Distance, class UniformRandomBitGenerator > SampleIterator sample( PopulationIterator first, PopulationIterator last, SampleIterator out, Distance n, UniformRandomBitGenerator&& g);
(自C ++ 17起)
从序列中选择n个元素[first;最后)这样每个人 可能的样本具有相同的出现概率,并写出那些 选中的元素进入输出迭代器。随机数是 使用随机数生成器g生成。 [...]
constexpr int min_value = 1;
constexpr int max_value = 49;
constexpr int picked_size = 6;
constexpr int size = max_value - min_value + 1;
// fill array with [min value, max_value] sequence
std::array<int, size> numbers{};
std::iota(numbers.begin(), numbers.end(), min_value);
// select 6 radom
std::array<int, picked_size> picked{};
std::sample(numbers.begin(), numbers.end(), picked.begin(), picked_size,
std::mt19937{std::random_device{}()});
如果你还不能使用C ++ 17,那么这样做的方法是生成数组中的所有数字,对数组进行洗牌,然后选择数组中的前6个数字:
// fill array with [min value, max_value] sequence
std::array<int, size> numbers{};
std::iota(numbers.begin(), numbers.end(), min_value);
// shuffle the array
std::random_device rd;
std::mt19937 e{rd()};
std::shuffle(numbers.begin(), numbers.end(), e);
// (optional) copy the picked ones:
std::array<int, picked_size> picked{};
std::copy(numbers.begin(), numbers.begin() + picked_size, picked.begin());
附注:请使用新的C ++ 11随机库。并且更喜欢std::array
裸C阵列。他们不会衰退到指针并提供begin
,end
,size
等方法。
答案 2 :(得分:0)
为了从m的池中绘制n个数,你需要n个随机调用这个方法(在你的情况下为6)而不是m-1(在你的情况下为49),只需改组整个数组或向量。所以下面显示的方法比简单地改组整个数组更有效,而不需要任何重复检查。
下面的解决方案不会进行任何重复检查,并且对n个随机数正好调用rand()n次。因此,需要对您的numberGen稍作修改。虽然你真的应该使用随机库函数而不是rand()。
下面的代码绘制所有数字,只是为了验证一切正常,但很容易看出你如何只绘制6个数字: - )
如果你需要重复绘制,你可以简单地添加一个再次设置drawn = 0
的reset()成员函数。然后,载体处于洗牌状态,但这没有任何伤害。
如果您无法负担std::vector.at()
中的范围检查,您当然可以通过索引访问运算符[]轻松替换它。但是我想在()处尝试代码是一个更好的选择,这样就可以得到错误检查,以便绘制太多的数字。
<强>用法:强> 使用构造函数创建n_out_of_m的类实例,该构造函数将可用数量作为参数。
重复调用draw()来绘制数字。
如果更频繁地调用draw(),那么数字可用std :: vector.at()将抛出out_of_range异常,如果你不喜欢,你需要为这种情况添加一个检查。
我希望有人喜欢这种做法。
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
size_t numberGen(size_t limit)
{
size_t number = rand();
size_t target = (number % limit) + 1;
return target;
}
class n_out_of_m {
public:
n_out_of_m(int m) {numbers.reserve(m); for(int i=1; i<=m; ++i) numbers.push_back(i);}
int draw();
private:
std::vector<int> numbers;
size_t drawn = 0;
};
int n_out_of_m::draw()
{
size_t index = numberGen(numbers.size()-drawn) - 1;
std::swap(numbers.at(index), numbers.at(numbers.size()-drawn-1));
drawn++;
return numbers.at(numbers.size()-drawn);
};
int main(int argc, const char * argv[]) {
n_out_of_m my_gen(49);
for(int n=0; n<49; ++n)
std::cout << n << "\t" << my_gen.draw() << "\n";
return 0;
}