挑选6个随机唯一数字

时间:2017-09-07 18:52:44

标签: c++

我试图让它工作时遇到问题。我打算在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;
}

3 个答案:

答案 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样本

C ++ 17为此提供了一种算法(转换图):

std::sample

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 ++ 11 shuffle

如果你还不能使用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阵列。他们不会衰退到指针并提供beginendsize等方法。

答案 2 :(得分:0)

有效的方法:有限的Fisher-Yates洗牌

为了从m的池中绘制n个数,你需要n个随机调用这个方法(在你的情况下为6)而不是m-1(在你的情况下为49),只需改组整个数组或向量。所以下面显示的方法比简单地改组整个数组更有效,而不需要任何重复检查

  1. 随机数可能会变得非常昂贵,所以我认为永远不要产生比必要更多的随机数是一个好主意。只需多次运行rand()直到拟合数字出来似乎不是一个好主意。
  2. 在需要绘制几乎所有可用数字的情况下,重复的双重检查图特别昂贵
  3. 我想把它做成有状态的,所以你实际要求的49个数字无关紧要
  4. 下面的解决方案不会进行任何重复检查,并且对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;
    }