【问题标题】:Algorithm for creating an array of 5 unique integers between 1 and 20 [duplicate]用于创建 1 到 20 之间的 5 个唯一整数数组的算法 [重复]
【发布时间】:2021-11-01 11:28:43
【问题描述】:

我的目标是创建一个由 5 个介于 1 到 20 之间的唯一整数组成的数组。有没有比我下面使用的算法更好的算法?

它有效,我认为它具有恒定的时间复杂度,因为循环不依赖于可变输入,但我想知道是否有更高效、更简洁或更简单的方法来编写它。

int * getRandom( ) {

  static int choices[5] = {};
  srand((unsigned)time(NULL));
   
  for (int i = 0; i < 5; i++) {

    int generated = 1 + rand() % 20;
    for (int j = 0; j < 5; j++){
      if(choices[j] == generated){
        i--;
      }
    }
    
    choices[i] = generated;
    cout << choices[i] << endl;
  }

  return choices;
}

非常感谢您的任何反馈。我是算法新手。

【问题讨论】:

  • 我不确定这到底是做什么的,所以我怀疑有一种更清洁、更简单的方法来编写它。您将i 作为外部循环的索引,但是内部循环i 混淆?你能口头描述一下这个算法是如何生成 5 个唯一整数的吗?见rubber duck debugging
  • 不要在使用它的函数中植入随机数生成器。
  • 使用% 20 会在您的随机数中引入偏差,因此它们不会像应有的那样随机。
  • 您的标题中有一个奇怪的措辞:“C++ 中是否有更好的算法 [...]?” 算法是抽象的,不限于单一语言。语言可能会影响哪些算法易于实现,但不会影响哪些算法存在。 (另外,建议不要在 Help: Tagging 的标题中强制添加标签。)
  • 对于数字的随机性没有要求,因此每次返回 (1,2,3,4,5) 符合规范。

标签: c++ performance random array-algorithms


【解决方案1】:

我能想到的最简单的方法就是用choices[i] = i+1 创建所有20 个数字的数组,用std::random_shuffle 将它们随机排列并取5 个第一个元素。可能会更慢,但很难引入错误,并且给定较小的固定大小 - 可能没问题。

顺便说一句,您的版本有错误。即使找到生成的 - 也可能会执行 choices[i] = generated; 行,这可能会创建 generated 值的副本。比如说,i = 3,生成的元素等于 j = 0 处的元素,现在你递减 i 并分配 choices[2] - 等于 choices[0]

【讨论】:

  • 谢谢。我不关心这个项目的速度,所以这应该可以正常工作。也感谢您的错误解释。我又运行了几次,看到几个输出有 6 个数字和重复。
【解决方案2】:

C++17 代码,解释了原因和内容。 如果您还有任何问题,请随时提出,我很乐意为您提供帮助

#include <iostream>
#include <array>
#include <string>
#include <random>
#include <type_traits>

// container for random numbers.
// by putting the random numbers + generator inside a class
// we get better control over the lifecycle.
// e.g. what gets called when.
// Now we know the generation gets called at constructor time.
class integer_random_numbers
{
public:
    // use std::size_t for things used in loops and must be >= 0
    integer_random_numbers(std::size_t number, int minimum, int maximum)
    {
        // initialize the random generator to be trully random
        // look at documentation for <random>, it is the C++ way for random numbers
        std::mt19937 generator(std::random_device{}());

        // make sure all numbers have an equal chance. range is inclusive 
        std::uniform_int_distribution<int> distribution(minimum, maximum);

        // m_values is a std::vector, which is an array of which
        // the length be resized at runtime.  
        for (auto n = 0; n < number; ++n)
        {
            int new_random_value{};

            // generate unique number
            do
            {
                new_random_value = distribution(generator);
            } while (std::find(m_values.begin(), m_values.end(), new_random_value) != m_values.end());
        
            m_values.push_back(new_random_value);
        }
    }

    // give the class an array index operator
    // so we can use it as an array later
    int& operator[](const std::size_t index)
    {
        // use bounds checking from std::vector
        return m_values.at(index);
    }

    // reutnr the number of numbers we generated
    std::size_t size() const noexcept
    {
        return m_values.size();
    }

private:
    // use a vector, since we specify the size at runtime.
    std::vector<int> m_values;
};

// Create a static instance of the class, this will
// run the constructor only once (at start of program)
static integer_random_numbers my_random_numbers{ 5, 1, 20 };

int main()
{
    // And now we can use my_random_numbers as an array
    for (auto n = 0; n < my_random_numbers.size(); ++n)
    {
        std::cout << my_random_numbers[n] << std::endl;
    }
}

【讨论】:

  • 添加了一个检查生成的随机数的唯一性以避免重复,我之前错过了这个要求
【解决方案3】:
  1. 从 1 到 16 生成 5 个随机数,允许重复
  2. 对它们进行排序
  3. 第 2 个数字加 1,第 3 个数字加 2,第 4 个数字加 3,第 5 个数字加 4。

最后一步通过将具有重复项的可能序列重新映射为具有唯一整数的序列,将范围从 [1,16] 转换为 [1,20]。例如,[1,2,10,10,16] 变为 [1,3,12,13,20]。转换是完全双射的,因此您永远不需要丢弃和重新采样。

【讨论】:

  • 如果我们试图从 [1,20] 中的所有 5 个不同整数的集合中统一采样,那么我认为这行不通;它有偏见。我们可以在第 1 步生成 5!=120 个序列,其中包含按某种顺序排列的数字 1、2、3、4、5,但只有 1 个序列包含 1、1、1、1、1,所以到第 3 步序列[1,3,5,7,9] 的生成可能性是[1,2,3,4,5] 的120 倍。
  • 是的,它在所有唯一整数集上并不完全一致。
猜你喜欢
  • 2016-05-31
  • 2021-04-29
  • 1970-01-01
  • 2018-11-12
  • 2017-04-21
  • 2011-08-10
  • 2011-09-12
  • 1970-01-01
相关资源
最近更新 更多