生成随机序列的奇怪行为

本文关键字:随机 | 更新日期: 2023-09-27 18:35:22

我有生成随机线性方程组的类

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

和此类的 NUnit 测试:

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

测试失败,但此代码有效。我尝试使用 TestDriven.Net 年的调试器工具调试此测试,并且所有工作和测试都通过了。谁能描述为什么这个测试失败了?

生成随机序列的奇怪行为

比你创建新的随机实例,它的种子初始化为当前时间,以毫秒为单位。如果紧接着创建一个实例,则种子可以使用一个值初始化。为了防止这种情况,您需要将一个随机实例传递给 Next 方法。

测试:

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

新的下一个方法:

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

虽然这不是一个完全重复的,但@L.B的链接回答了这个问题。Random以当前时间戳作为种子进行初始化。如果您使用相同的时间,您将获得相同的"随机"数字。

但是,由于您正在创建一个随机数数组,并且该方法还执行其他操作,因此它可能会按预期工作。但这只是因为它需要更多时间在当前机器上执行,所以下一次调用 Next 会创建另一个双精度值列表。

因此,该软件可能在机器上运行得足够快,以始终保持相同的顺序。这就是测试失败的原因。

一个解决方案是始终使用相同的Random实例,例如使用成员变量而不是局部变量。

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

在这种情况下,最好将随机实例传递给 MatrixGenerator 的构造函数或作为参数传递给 Next 方法。

var random = new Random();
for(int i = 0; i< 1000; i++)
{
    var mGen = new MatrixGenerator(4, 5, 10, random);
    mGen.Next();
}