0

I have class that generates random system of linear equations

public class MatrixGenerator: IMatrixGenerator
    {
        private int vSize;
        private int hSize;
        private double[,] _matrix;
        private double[] _right;
        private double[] _solution;
        private double maxValue;

        public MatrixGenerator(int vSize, int hSize, double maxValue)
        {
            this.vSize = vSize;
            this.hSize = hSize;
            this.maxValue = maxValue;
            _matrix = new double[vSize, hSize];
            _right = new double[vSize];
            _solution = new double[hSize];
        }

        public void Next()
        {
            _matrix = new double[vSize, hSize];
            _right = new double[vSize];
            Random r = new Random();
            _solution = Enumerable.Repeat(0.0, hSize).Select(m => m = r.NextDouble()*maxValue).ToArray();

            for (int i = 0; i < vSize; i++)
            {
                for (int j = 0; j < hSize; j++)
                {
                    _matrix[i, j] = r.NextDouble() * maxValue;
                }
                for (int j = 0; j < hSize; j++)
                {

                    _right[i] += _solution[j] * _matrix[i, j];
                } 
            } 
        }

        public double[,] Matrix
        {
            get { return _matrix; }
        }

        public double[] RightVector
        {
            get { return _right; }
        }

        public double[] SolutionVector
        {
            get { return _solution; }
        }
    }

and NUnit test for this class:

[Test]
        public void CanGenerateAnotherMatrixandVector()
        {
            MatrixGenerator mGen = new MatrixGenerator(vSize, hSize, maxValue);
            mGen.Next();
            double[,] firstMatrix = new double[mGen.Matrix.GetLength(0), mGen.Matrix.GetLength(1)];
            double[] firstVector = new double[mGen.RightVector.GetLength(0)];
            for (int i = 0; i < mGen.Matrix.GetLength(0); i++)
            {
                firstVector[i] = mGen.RightVector[i]; 
                for (int j = 0; j < mGen.Matrix.GetLength(1); j++)
                {
                    firstMatrix[i,j] = mGen.Matrix[i, j];
                }
            }
            mGen.Next();

            Assert.That(firstMatrix, Is.Not.EqualTo(mGen.Matrix));
            Assert.That(firstVector, Is.Not.EqualTo(mGen.RightVector));
        }

Test is failed, but this code is work. I try debug this test with Debugger tools from TestDriven.Net and everything work and test passed. Can anybody describe me why this test fails?

Cœur
  • 37,241
  • 25
  • 195
  • 267
HaMI
  • 1
  • 1
  • 2
    You should probably reuse your instance of the `Random` object as instantiating it each time can plausibly result in the same random numbers if it's processing fast enough. – Chris Sinclair May 26 '12 at 23:52
  • As @ChrisSinclair said if you call `Random r = new Random();` fast enough you can get the same sequence. Create `r` only once. http://stackoverflow.com/questions/767999/random-number-generator-not-working-the-way-i-had-planned-c – L.B May 26 '12 at 23:58
  • I think you are right when I use some kind of delay before call last Next() method this test passes – HaMI May 27 '12 at 00:05

2 Answers2

0

Although this is not an exact duplicate, the link of @L.B answers this question. A Random is initialized with the current timestamp as seed. If you use the same time, you will get the same "random" number.

But because you're creating an array of random numbers and the method does other things as well, it might work as desired. But only because it needs more time to execute on your current machine, so the next call of Next crates another list of double values.

Hence it's possible that this piece of software runs on a machine fast enough to always crate the same sequence. That is why the test fails.

A solution would be to use always the same instance of Random, for example by using a member variable instead of a local variable.

public class MatrixGenerator: IMatrixGenerator
{
    // ...
    Random r = new Random();

    // ...

    public void Next()
    {
        _matrix = new double[vSize, hSize];
        _right = new double[vSize];
        _solution =  Enumerable.Repeat(new Random(), hSize)
                               .Select(r => r.NextDouble() * maxValue)
                               .ToArray(); 
        // ...

In this case it's even better to pass the random instance to the constructor of MatrixGenerator or as argument to the Next method.

var random = new Random();
for(int i = 0; i< 1000; i++)
{
    var mGen = new MatrixGenerator(4, 5, 10, random);
    mGen.Next();
}
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
0

Than you create new Random instance, its seed initialised form the current time in milliseconds. If create one instance right after another, then seed may initialise with one value. To prevent this, you neet to pass one random instance to the Next method.

Test:

        var random = new Random();
        var mGen = new MatrixGenerator(4, 5, 10);
        mGen.Next(random);

        var firstMatrix = new double[mGen.Matrix.GetLength(0),mGen.Matrix.GetLength(1)];
        var firstVector = new double[mGen.RightVector.GetLength(0)];

        for (int i = 0; i < mGen.Matrix.GetLength(0); i++) {
            firstVector[i] = mGen.RightVector[i];
            for (int j = 0; j < mGen.Matrix.GetLength(1); j++) {
                firstMatrix[i, j] = mGen.Matrix[i, j];
            }
        }
        mGen.Next(random);

        CollectionAssert.AreNotEqual(firstMatrix, mGen.Matrix);
        CollectionAssert.AreNotEqual(firstVector, mGen.RightVector);

New Next method:

    public void Next(Random random) {
        _matrix = new double[vSize,hSize];
        _right = new double[vSize];

        _solution = Enumerable.Repeat(0.0, hSize).Select(m => random.NextDouble()*maxValue).ToArray();

        for (int i = 0; i < vSize; i++) {
            for (int j = 0; j < hSize; j++) {
                _matrix[i, j] = random.NextDouble()*maxValue;
            }
            for (int j = 0; j < hSize; j++) {
                _right[i] += _solution[j]*_matrix[i, j];
            }
        }
    }
Unril
  • 106
  • 1
  • 3