C#随机数生成器,具有单个数字的限制

时间:2017-02-16 16:23:10

标签: c# algorithm

我想在1到100之间生成random个数字.1到100之间的任何数字都可以有3个最大限制。

例如,如果数字2生成3次,则它不应返回数字2,因为它已经生成3次。这里1到100之间的任何数字的最大限制是3,但任何数字都可以生成少于3次。

如何在不跟踪1到100之间的每个数字的情况下实现此目的?最大限制和数字范围可以更改。

修改 根据Zoran Horvat和Alexei Levenkov提供的答案,如果不跟踪数字而生成具有出现限制的随机数将很困难并且涉及复杂的解决方案。我厌倦了使用字典编写自己的解决方案,如下所示。

//for storing output
        List<int> output = new List<int>();
        //to keep track of numbers
        Dictionary<int, int> numberTrack = new Dictionary<int, int>();
        Random ran = new Random();
        //define max limit and range of numbers
        int maxLimit = 2, min = 1, max = 100 ;
        int no, count;
        //this for loop must run for any number less than max*maxLimit otherwise it will last for long time(may be forever). 
        for (int i = 0; i < max*maxLimit; i++)
        {
            //get random number.
            no = ran.Next(min, max+1);
            //check if random number exists in dictionary
            if (numberTrack.TryGetValue(no, out count))
            {
                //if exists than check for it occurrences 
                if (count >= maxLimit)
                {
                    //if occurrence is greater than maxLimit continue for next number.
                    i--;
                    continue;
                }
                else
                {
                    //else add number to output and update its occurrence count
                    numberTrack[no] += 1;
                    output.Add(no);
                }
            }
            else
            {
                //if random number does not exists in the dictionary than add it 
                //with occurrence of 1 and also add it to the ouptput.
                numberTrack.Add(no, 1);
                output.Add(no);
            }
        }

但谢尔盖别列佐夫斯基和RenéVogt的解决方案完成了几行代码的任务。我非常感谢我从这个社区获得的所有帮助。

4 个答案:

答案 0 :(得分:6)

  1. 生成3 * 1..100个数字
  2. 随机播放
  3. 迭代洗牌收集
  4. 样品:

    var random = new Random();
    var maxRepetitionsCount = 3;
    var numbers = Enumerable.Range(1,100)
           .SelectMany(i => Enumerable.Repeat(i, maxRepetitionsCount))
           .OrderBy(i => random.Next())
           .ToList();
    

    注意:没有选项可以跟踪事件计数而不跟踪事件计数。这就像“我想做X而不做X”

答案 1 :(得分:3)

您可以创建生成这些数字的linq查询:

private Random random = new Random();
public IEnumerable<int> RandomNumbers(int limit, int maxOccurence)
{
    return Enumerable.Range(1, limit)
                     .SelectMany(i => Enumerable.Repeat(i, maxOccurence))
                     .OrderBy(i => random.Next());
}

这会创建一个从1到limit的数字序列,然后重复每个数字maxOccurence次,然后按(伪)随机数对结果进行排序。 random只应实例化一次,以避免重复相同的伪随机序列。

但是请注意,这至少对于第一个数字来说会很慢,因为它必须开始对整个序列进行排序(在您的示例中将是300个数字,因此性能不会成为问题)。

答案 2 :(得分:1)

只保留2个“值”的非实用方法。

由于限制“范围1-100中每个数字的最大3倍”基本上要求对3组数字1-100进行混洗,我们可以将整个任务表示为首先选择随机排列并以某种排列顺序记住其位置(例如lexicographic order)并且对于300个随机数请求中的每一个,在该排列中选择下一个元素(首先恢复排列)。

请注意,存储代表排列数的“值”的成本将非常疯狂,并且可能高于仅仅存储整个序列。通过其位置恢复排列将更不实用。

假设您使用可重复的随机数生成器(如.Net中的on),更合理的版本将重播每个请求的重排。这样,您只需要为随机数生成器存储种子,并在混洗集合中存储最后“随机”数字的索引(每个请求的增量)。你仍然需要为洗牌分配数组,但不需要在调用之间保持它。复杂性是非常合理的,因为改组只需要O(n),所以整体生成所有数字,你需要每次调用O(n)临时存储+ PRNG种子和索引的2个固定大小值,以及O(n ^ 2)时间。

答案 3 :(得分:0)

以下是解决方案的主要概要。实施不会是微不足道的,在某些情况下甚至不可能。

<强>先决条件

  • 给定范围1..M,从中获取数字。
  • 将K视为输出序列中任何数字的最大重复次数。
  • 给定L作为必须生成的序列的长度。

精确解决方案

  1. 为数字范围1..N构建完美哈希函数,其中 N> = M并且散列函数产生均匀分布。一种可能性是应用线性全等生成器,就像常见的伪随机数生成器一样。
  2. 构造一个算法,将L分成加法分量l1 + l2 + ... + lK = L.该算法必须从所有可能集合的均匀分布中随机选取一组分量。
  3. 对于数字1,2,3 ...使用第一点的完美哈希将每个数字映射到一个随机数。
  4. 对l1,l2,...,lK重复步骤3,这将导致生成总共L个伪随机数。
  5. 底线是在给定约束内生成伪随机序列非常复杂。只有当N很大并且记住以前生成的数字时,这个算法才有意义。

    近似解决方案

    如果避免重复数字仍然很重要,并且无法记住以前的所有数字,因为它们的范围或总数很大,您可以选择近似解决方案。

    例如,您可以记录生成的一些值,并确保该子序列中不存在新值。您可以通过概率计算来自M和K的子序列的长度,其中来自序列1..M的任何数字出现超过K次的值低于最大可接受值。就像确保任何数字出现超过K次的概率不大于10%或1%左右。

    同样,实施这个算法会非常困难,只有当数字N和K使得其他解决方案的运行时成本过高时才能获得回报。