隨機數(三)

黃志斌發表於2016-07-10

測試程式

圖靈社群:隨機數(二)中,我們給出了一個 NrRandom 類,能夠產生更好的偽隨機序列。那麼,它與 .NET Framework 中的 Random 類相比,效能如何呢?讓我們寫一個測試程式吧:

using System;
using System.Diagnostics;

namespace Skyiv.Tester
{
  static class NrRandomTester
  {
    static readonly Stopwatch timer = new Stopwatch();

    static void TestNext1(Random rand, int n)
    {
      long sum = 0;
      timer.Restart();
      for (var i = 0; i < n; i++) sum += rand.Next();
      timer.Stop();
      Console.WriteLine("{0,14} {1,6:F3} {2:N0}", rand.GetType(),
        timer.Elapsed.TotalSeconds, (double)sum/n);
    }

    static void TestNext2(Random rand, int n, int max)
    {
      long sum = 0;
      timer.Restart();
      for (var i = 0; i < n; i++) sum += rand.Next(max);
      timer.Stop();
      Console.WriteLine("{0,14} {1,6:F3} {2:N6}", rand.GetType(),
        timer.Elapsed.TotalSeconds, (double)sum/n);
    }

    static void TestNext3(Random rand, int n, int min, int max)
    {
      long sum = 0;
      timer.Restart();
      for (var i = 0; i < n; i++) sum += rand.Next(min, max);
      timer.Stop();
      Console.WriteLine("{0,14} {1,6:F3} {2,12:N3}", rand.GetType(),
        timer.Elapsed.TotalSeconds, (double)sum/n);
    }

    static void TestNextDouble(Random rand, int n)
    {
      double sum = 0;
      timer.Restart();
      for (var i = 0; i < n; i++) sum += rand.NextDouble();
      timer.Stop();
      Console.WriteLine("{0,14} {1,6:F3} {2:N10}", rand.GetType(),
        timer.Elapsed.TotalSeconds, sum/n);
    }

    static void TestNextBytes(Random rand, int n, int size)
    {
      long sum = 0;
      var bs = new byte[size];
      timer.Restart();
      for (var i = 0; i < n; i++) {
        rand.NextBytes(bs);
        foreach (var v in bs) sum += v;
      }
      timer.Stop();
      Console.WriteLine("{0,14} {1,6:F3} {2:N6}", rand.GetType(),
        timer.Elapsed.TotalSeconds, (double)sum/n/size);
    }

    static void Main()
    {
      Console.WriteLine("{0} {1}-bit", Environment.OSVersion,
        Environment.Is64BitOperatingSystem ? 64 : 32);
      Console.WriteLine("CLR {0}", Environment.Version);

      var r0 = new Random();
      var nr = new NrRandom();

      Console.WriteLine();  int n = 1000000000, max = int.MaxValue;
      Console.WriteLine("Next(): {0,27:N0}", (max-1)/2.0);
      TestNext1(r0, n); TestNext1(nr, n);

      Console.WriteLine(); max = 10000;
      Console.WriteLine("Next(max): {0,23:N6}", (max-1)/2.0);
      TestNext2(r0, n, max); TestNext2(nr, n, max);

      Console.WriteLine(); max = int.MaxValue; int min = 1 - max;
      Console.WriteLine("Next(min,max): {0,19:N3}", (max-1L+min)/2.0);
      TestNext3(r0, n, min, max); TestNext3(nr, n, min, max);

      Console.WriteLine();
      Console.WriteLine("NextDouble(): {0,20:N10}", 1/2.0);
      TestNextDouble(r0, n); TestNextDouble(nr, n);

      Console.WriteLine(); int size = 1000000, m = n / size;
      Console.WriteLine("NextBytes(buffer): {0,13:N6}", byte.MaxValue/2.0);
      TestNextBytes(r0, m, size); TestNextBytes(nr, m, size);
    }
  }
}

編譯和執行

Linux 64-bit 作業系統

在 Arch Linux 64-bit 作業系統中編譯和執行:

$ mcs NrRandomTester.cs NrRandom.cs
$ mono NrRandomTester.exe
Unix 4.6.3.1 64-bit
CLR 4.0.30319.42000

Next():               1,073,741,823
 System.Random 14.920 1,073,744,468
Skyiv.NrRandom 16.619 1,073,777,257

Next(max):            4,999.500000
 System.Random 19.831 4,999.476177
Skyiv.NrRandom 15.244 4,999.590640

Next(min,max):               0.000
 System.Random 41.936    8,672.938
Skyiv.NrRandom 16.405  -13,744.444

NextDouble():         0.5000000000
 System.Random 17.517 0.4999909586
Skyiv.NrRandom 14.077 0.5000026850

NextBytes(buffer):    127.500000
 System.Random 15.483 127.498822
Skyiv.NrRandom  6.291 127.503321

輸出結果中:

方法名稱:                期望的平均值
 System.Random 執行時間(秒) 實際的平均值
Skyiv.NrRandom 執行時間(秒) 實際的平均值

其中每種方法都生成 109 個隨機數來進行測試。可以看出:

  • 對於 Next() 方法,NrRandom 類的效能略差於 Random 類。
  • 對於 Next(max) 方法,NrRandom 類的效能優於 Random 類。
  • 對於 Next(min,max) 方法,NrRandom 類的效能大大優於 Random 類。
  • 對於 NextDouble() 方法,NrRandom 類的效能優於 Random 類。
  • 對於 NextBytes(buffer) 方法,NrRandom 類的效能大大優於 Random 類。
  • 對於這兩個類,各個方法生成的隨機數的平均值都中規中矩。

Windows 64-bit 作業系統

Win2008

C> NrRandomTester.exe
Microsoft Windows NT 6.1.7601 Service Pack 1 64-bit
CLR 4.0.30319.42000

Next():               1,073,741,823
 System.Random 11.030 1,073,763,381
Skyiv.NrRandom 11.944 1,073,745,494

Next(max):            4,999.500000
 System.Random 16.585 4,999.546877
Skyiv.NrRandom 10.319 4,999.411534

Next(min,max):               0.000
 System.Random 31.271   37,382.863
Skyiv.NrRandom 11.942  -39,145.860

NextDouble():         0.5000000000
 System.Random 13.122 0.5000071087
Skyiv.NrRandom  9.548 0.4999977242

NextBytes(buffer):    127.500000
 System.Random 12.009 127.497139
Skyiv.NrRandom  7.090 127.503601

測試結果與 Linux 64-bit 作業系統一致。

Windows 32-bit 作業系統

Win7

C:> NrRandomTester.exe
Microsoft Windows NT 6.1.7601 Service Pack 1 32-bit
CLR 4.0.30319.42000

Next():               1,073,741,823
 System.Random 11.455 1,073,751,534
Skyiv.NrRandom 35.625 1,073,754,659

Next(max):            4,999.500000
 System.Random 18.431 4,999.490003
Skyiv.NrRandom 33.778 4,999.364559

Next(min,max):               0.000
 System.Random 50.525   17,712.270
Skyiv.NrRandom 46.544  -10,609.773

NextDouble():         0.5000000000
 System.Random 14.538 0.4999985216
Skyiv.NrRandom 26.370 0.4999974172

NextBytes(buffer):    127.500000
 System.Random 11.726 127.500430
Skyiv.NrRandom  9.558 127.500100

測試結果出乎意料,除了 Next(min,max)NextBytes(buffer) 方法,NrRandom 類的效能略優於 Random 類,其餘方法,NrRandom 類的效能均大大差於 Random 類。我想,這是由於 NrRandom 類生成隨機數時,使用 long(64-bit 整數)進行運算,而 32-bit 的作業系統的 64-bit 整數運算的效能低下造成的。

相關文章