esac
esac

Reputation: 24685

How to write a Linear Congruential Generator (LCG) in C#? Or are there any well known implementations?

I am wanting to generate a random array of sequences that repeat and only use each number once. For example, given a range of 0-9 with 2 different seeds you might get

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

From what I understand, a Linear Congruential Random Number Generator or LCRNG or LCG can give me this http://en.wikipedia.org/wiki/Linear_congruential_generator

Any idea if an implementation exists in C# or how I would get started with writing one.

How does a Mersenne Twister differ from an LCG?

Not sure all of my questions are answered, but here is what I ended up using. Because I am bounding the sample size to the range of values from max to min, I am selecting a different prime number that stays static as long as the same initial seed is given. I do this because I want the same sequence given the same seed and the same min/max bounds for repeatability of testing.

Please critique anything I do here, this is just what I came up with in a jiffy:

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;
        }
    }
}

Upvotes: 2

Views: 7345

Answers (3)

ng256
ng256

Reputation: 87

Try the following code:

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();
            }
        }
    }
} 

Upvotes: 0

LukeH
LukeH

Reputation: 269388

Regarding your edit, there are several problems with your LCG as a random number generator...

  1. It can produce obvious patterns:

    // 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. It can repeat itself:

    // 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));
    

There are many other seed/min/max combinations that will generate problematic results.

Upvotes: 1

Ron Warholic
Ron Warholic

Reputation: 10074

Why not just use the existing Random class and a Knuth shuffle on your input sequence?

Upvotes: 1

Related Questions