【问题标题】:C++ Random number within range with exclusion排除范围内的 C++ 随机数
【发布时间】:2013-02-17 09:46:11
【问题描述】:

我想生成 0 到 5 范围内的随机数,但例如在某些情况下我不需要数字 3,我只需要 0、1、2、4、5。如何生成随机数在范围内,但可以选择排除我不需要的数字。

【问题讨论】:

  • 那么如果你排除了 3,那么其余的数字是否应该得到统一的概率?不谈概率分布就谈不上随机数。
  • 你介意我删除 Qt 标签吗?谢谢;)
  • 可能是 stackoverflow.com/questions/6443176/… 的副本,但上下文尚不清楚。
  • 是 0..5 和一个数字,仅排除一个示例,您计划使用更大的范围。由于可扩展性,这很重要。当使用 0..1000000000 并排除一半的数字时,肯定会更棘手
  • 放弃并重试?

标签: c++


【解决方案1】:

这是另一个使用 C++ 11 的现代特性的解决方案。

#include <functional>
#include <iostream>
#include <ostream>
#include <random>

int main()
{
    std::random_device rd;

    unsigned long seed = rd();
    std::cout << "seed " << seed << std::endl;

    std::mt19937 engine(seed);

    // Distribution  {0, 1, 2, 4, 5}
    std::discrete_distribution<> dist {{1, 1, 1, 0, 1, 1}};
    auto rng = std::bind(dist, std::ref(engine));

    const int n = 10;
    for (int i = 0; i != n; ++i)
    {
        int x = rng();
        std::cout << x << std::endl;
    }

    return 0;
}

【讨论】:

    【解决方案2】:

    您要排除的数字在编译时是否已知?然后应该做一个简单的查找表:

    static int table[] = {0, 1, 2, 4, 5};
    int index = rand() % (sizeof table / sizeof *table);
    int number = table[index];
    

    当然,rand() 是一个糟糕的伪随机数生成器,但这是另一个话题。

    【讨论】:

    • rand 本身怎么可能是一个糟糕的伪随机数生成器?当然这取决于实施?
    • 我发现这个解决方案是最好的,但我使用的是 rand()。如果随机数有更好的功能,请告诉我。
    • @Alen C++11 添加了一个充满随机数生成器的整个标头:&lt;random&gt;
    【解决方案3】:

    循环扔掉 3:

    template <typename Rng>
    int rand_no_three (Rng& rng)
    {
        std::uniform_int_distribution<> dist (0, 5);
        int c;
    
        do {
           c = dist (rng);
        } while (c == 3);
    
        return c;
    }
    

    示例用法:

    std::mt19937 rng;
    int c = rand_no_three (rng);
    

    【讨论】:

      【解决方案4】:

      如果您有内存限制,这是一种无需查找表的方法。

      int map_fun(int i)
      { 
        switch(i)
        {
          case 3: return 4;
          case 4: return 5;
        }
        return i;
      }
      
      int main()
      {
         int i= map_fun(random_between(0, 4));
      }
      

      【讨论】:

        【解决方案5】:

        这是一个使用这篇文章中的 java 代码的示例:How can I generate a random number within a range but exclude some? 翻译成 c++。

        这有点通用,没有使用不必要的循环。

        我还做了一个分布、范围和发生率——在这个程序中检查:

        #include <iostream>
        #include <vector>
        #include <cstdlib>
        #include <time.h>
        #include <map>
        using namespace std;
        
        
        int getRandomWithExclusion(int start, int end, vector<int> &excludes){
            unsigned int max = end - start + 1 - excludes.size();
            int random = start+(rand() % max);
            for (unsigned int i = 0;i<excludes.size();i++) {
                int ex = excludes[i];
                if (random < ex){
                    break;
                }
                random++;
            }
            return random;
        }
        
        int main()
        {
            srand (time(NULL));
            vector<int> excl;
        
            map<int,int> distMap;
        
            excl.push_back(-2);
            excl.push_back(3);
        
        
        
        
            for(int i=0;i<100000;i++){
        
                int val = getRandomWithExclusion(-5,5,excl);
                if (distMap.find(val) == distMap.end()){
                    distMap[val]=0;
                }
                int key = distMap[val]+1;
                distMap[val]=key;
            }
            map<int, int>::iterator p;
            for(p = distMap.begin(); p != distMap.end(); p++) {
                cout << p->first <<" occurs " <<p->second << endl;
        
            }
        
            cout << "seems legit: even distributed and correct" << endl;
        
            return 0;
        }
        

        【讨论】:

        • excl 向量似乎需要对其值进行升序排序,对吧?
        【解决方案6】:

        这个 c++ 函数应该处理这个任务:

        int getRandomIntExcludingRange(int start, int end, int start_range, int end_range) {
        
            // handle bad input gracefully
            if (start > end) {
                std::swap(start, end);
            }
            if (start_range > end_range) {
                std::swap(start_range, end_range);
            }
            std::clamp(start_range, start, end);
            std::clamp(end_range, start, end);
        
            auto range_length = end_range - start_range;
            auto adj_end      = end - range_length;
            auto shifted_end  = adj_end - start;
        
            int rand_int = (rand() % shifted_end) + start;
        
            if (rand_int >= start_range) {
                rand_int = (rand_int - start_range) + end_range;
            }
            return rand_int;
        }
        

        【讨论】:

          猜你喜欢
          • 2016-09-25
          • 1970-01-01
          • 2011-12-16
          • 1970-01-01
          • 2021-12-02
          • 1970-01-01
          • 1970-01-01
          • 2011-11-25
          相关资源
          最近更新 更多