Quasi-Random Sequences in C# QuickStart Sample
Illustrates how to generate quasi-random sequences like Fauré and Sobol sequences using classes in the Numerics.NET.Statistics.Random namespace in C#.
This sample is also available in: Visual Basic, F#, IronPython.
Overview
This sample demonstrates how to use quasi-random sequences to compute multi-dimensional integrals efficiently and accurately.
The sample shows how to use both Halton and Sobol sequences to evaluate a specific 5-dimensional integral. It demonstrates the key differences between these sequence types and provides practical examples of their usage. The program:
- Creates a Halton sequence and uses it to estimate a 5-dimensional integral
- Shows how to iterate through the sequence points and evaluate the integrand
- Demonstrates progress monitoring by displaying intermediate results
- Illustrates the use of the more sophisticated Sobol sequences
- Shows how to skip points in a Sobol sequence efficiently
- Compares the results to the known exact value of the integral
The sample serves as a practical introduction to quasi-random number generation and shows how these sequences can be more effective than pseudo-random numbers for certain numerical integration tasks. It includes examples of proper sequence initialization and demonstrates recommended usage patterns for both sequence types.
The code
using System;
using Numerics.NET;
using Numerics.NET.Random;
// Illustrates the use of quasi-random sequences by computing
// a multi-dimensional integral.
public class QuasiRandomSequences
{
static void Main(string[] args)
{
// The license is verified at runtime. We're using
// a 30 day trial key here. For more information, see
// https://numerics.net/trial-key
Numerics.NET.License.Verify("your-trial-key-here");
// This QuickStart Sample demonstrates the use of
// quasi-random sequences by computing
// a multi-dimensional integral.
// We will use one million points.
int length = 1000000;
// The number of dimensions:
int dimension = 5;
// We will evaluate the function
//
// Product(i = 1 -> # dimensions) |4 x[i] - 2|
//
// over the hypercube 0 <= x[i] <= 1. The value of this integral
// is exactly 1.
// Create the sequence:
var sequence = QuasiRandom.HaltonSequence(dimension, length);
Console.WriteLine("# iter. Estimate");
// Compute the integral by summing over all points:
double sum = 0.0;
int i = 0;
foreach (var point in sequence)
{
if (i % 100000 == 0)
Console.WriteLine("{0,6} {1,8:F4}", i, sum / i);
// Evaluate the integrand:
double functionValue = 1.0;
for(int j = 0; j < dimension; j++)
functionValue *= Math.Abs(4.0*point[j]-2.0);
sum += functionValue;
i++;
}
Console.WriteLine($"Final estimate: {sum / length,8:F4}");
Console.WriteLine("Exact value: 1.0000");
// Sobol sequences require more data and more initialization.
// Fortunately, different sequences of the same dimension
// can share much of the work and storage. The
// SobolSequenceGenerator class should be used in this case:
int skip = 1000;
var sobol = new SobolSequenceGenerator(dimension, length + skip);
// Sobol sequences are more flexible: they let you skip
// a number of points at the start of the sequence.
// The cost of skipping points is O(1).
i = 0;
sum = 0.0;
foreach (var point in sobol.Generate(length, skip))
{
if (i % 100000 == 0)
Console.WriteLine("{0,6} {1,8:F4}", i, sum / i);
// Evaluate the integrand:
double functionValue = 1.0;
for (int j = 0; j < dimension; j++)
functionValue *= Math.Abs(4.0 * point[j] - 2.0);
sum += functionValue;
i++;
}
// Print the final result.
Console.WriteLine($"Final estimate: {sum / length,8:F4}");
Console.WriteLine("Exact value: 1.0000");
Console.Write("Press any key to exit.");
Console.ReadLine();
}
}