【问题标题】:How to write a Linear Congruential Generator (LCG) in C#? Or are there any well known implementations?如何在 C# 中编写线性同余生成器 (LCG)?或者有没有众所周知的实现?
【发布时间】:2011-04-25 12:32:49
【问题描述】:

我想生成一个随机的序列数组,这些序列重复并且每个数字只使用一次。例如,给定 0-9 的范围内有 2 个不同的种子,您可能会得到

Seed 1: 7 3 5 9 0 8 1 2 6 4 | 7 3 5 9 0 8 1 2 6 4 | 7 3 5 9 0 8 1 2 6 4
Seed 2: 2 5 7 1 4 9 6 8 3 0 | 2 5 7 1 4 9 6 8 3 0 | 2 5 7 1 4 9 6 8 3 0

据我了解,线性同余随机数生成器或 LCRNG 或 LCG 可以给我这个http://en.wikipedia.org/wiki/Linear_congruential_generator

知道 C# 中是否存在实现,或者我将如何开始编写一个。

Mersenne Twister 与 LCG 有何不同?

不确定我的所有问题都已得到解答,但这是我最终使用的。因为我将样本大小限制在从最大值到最小值的值范围内,所以我选择了一个不同的素数,只要给出相同的初始种子,它就会保持静态。我这样做是因为我想要相同的序列给定相同的种子和相同的最小/最大界限以实现测试的可重复性。

请批评我在这里所做的任何事情,这只是我一时想到的:

using System;
using System.Collections.Generic;

namespace FULLCYCLE
{
    public class RandomNumber
    {
        private int _value;
        private int _prime;
        private static List<int> primes = new List<int>()
        {
            11,
            23,
            47,
            97,
            797,
            1597,
            6421,
            25717,
            51437,
            102877,
            411527,
            823117,
            1646237,
            3292489,
            6584983,
            13169977,
            26339969,
            52679969,
            105359939,
            210719881,
            421439783,
            842879579,
            1685759167
        };

        public RandomNumber( int seed )
        {
            _prime = primes[seed%primes.Count];
            _value = seed;
        }

        public int Next( int min, int max )
        {
            int maxrate = (max-min+1);
            if (_value > maxrate)
            {
                _value = _value % maxrate;
            }

            _value = (_value + _prime) % maxrate;
            return _value + min;
        }
    }
}

【问题讨论】:

  • 您链接的页面提供了您需要的信息。方程式就在页面上。

标签: c# algorithm random


【解决方案1】:

为什么不在您的输入序列上使用现有的Random 类和Knuth shuffle

【讨论】:

  • 根据我对 Knuth shuffle 的了解,如果我想要 0 到 10 亿之间的随机数,那么我需要将整个数组存储在内存中并在内存中随机播放。使用 LCG,我似乎可以用很少的内存完成同样的任务。
  • LCG 也不保证数字不会重复。根据您的种子,您可以连续五次获得 100。在不知道已经产生了哪些数字的情况下,您如何期望每个数字只生成一次?
  • 如果您正确设置了 LCG 的输入参数,那么您可以保证它会在重复之前生成该范围内所有可能的数字。
  • @LukeH- 如en.wikipedia.org/wiki/Full_cycle 所示(具有讽刺意味的是,它包含 C# 代码来执行 OP 想要的操作)。然而,这需要限制种子和样本大小(如果增量素数除以样本大小,您将得到重复)。
【解决方案2】:

关于您的编辑,您的 LCG 作为随机数生成器存在几个问题...

  1. 它可以产生明显的图案:

    // generates 3, 4, 5, 6, 7, 8, 9, 0, 1, 2
    var rng = new RandomNumber(42);
    for (int i = 0; i < 10; i++) Console.WriteLine(rng.Next(0, 9));
    
  2. 它可以重复自己:

    // generates 579, 579, 579, 579, 579, 579, 579, 579, 579, 579
    var rng = new RandomNumber(123);
    for (int i = 0; i < 10; i++) Console.WriteLine(rng.Next(456, 51892));
    

还有许多其他种子/最小值/最大值组合会产生有问题的结果。

【讨论】:

    【解决方案3】:

    试试下面的代码:

    namespace System
    {
        /// <summary>
        /// Represents a pseudo-random number generator using the linear congruential method:
        /// X[i+1] = (a • X[i] + c) % m (where i is greater than or equal to 0).
        /// </summary>
        public class LinearCongruentialRandom : Random
        {
            private int _x = 1971111612;
            private int _a = 2147483629;
            private int _c = 2147483587;
            private int _m = 2147483647;
    
            /// <summary>
            /// Initializes a new instance of <see cref="LinearCongruentialRandom"/> with default values.
            /// </summary>
            public LinearCongruentialRandom()
            {
                = base.Next(0, _m);
            }
    
            /// <summary>
            /// Initializes a new instance of <see cref="LinearCongruentialRandom"/> with default values.
            /// </summary>
            public LinearCongruentialRandom(int seed)
            {
                if (seed >= _m) throw new ArgumentOutOfRangeException(nameof(seed), seed, null);
                _x = seed;
    
            }
    
    
            /// <summary>
            /// Initializes a new instance of <see cref="LinearCongruentialRandom"/> with the specified parameters.
            /// </summary>
            /// <param name="seed">Initial seed value less than m.</param>
            /// <param name="a">A multiple of p for every prime p that is a divisor of m.</param>
            /// <param name="c">Increment c. The numbers c and m must be relatively prime.</param>
            /// <param name="m">The length of the repetition period m. The numbers c and m must be relatively prime.</param>
            public LinearCongruentialRandom(int seed, int a, int c, int m) : this(seed)
            {
                _a = a;
                _c=c;
                _m=m;
            }
    
            /// <inheritdoc cref="Random.Next()"/>
            public override int Next()
            {
                return _x = (_a * _x + _c) % _m;
            }
             
            /// <inheritdoc cref="Random.NextBytes(byte[])"/>
            public override void NextBytes(byte[] buffer)
            {
                if (buffer == null)
                    throw new ArgumentNullException(nameof(buffer));
    
                unchecked
                {
                    for (int index = 0; index < buffer.Length; ++index)
                        buffer[index] = (byte)Next();
                }
            }
        }
    } 
    

    【讨论】:

      猜你喜欢
      • 2020-12-23
      • 1970-01-01
      • 2015-08-22
      • 1970-01-01
      • 2013-06-06
      • 2013-10-09
      • 2016-07-03
      • 1970-01-01
      • 2015-11-15
      相关资源
      最近更新 更多