Browse Source

Random: fix RandomSource.Next(x, x+1) #427

Merge branch 'pr-428' (early part)
pull/436/head
Christoph Ruegg 10 years ago
parent
commit
ceb697f92f
  1. 12
      src/Numerics/Numerics.csproj
  2. 9
      src/Numerics/Properties/Resources.Designer.cs
  3. 57
      src/Numerics/Properties/Resources.resx
  4. 119
      src/Numerics/Random/RandomSource.cs
  5. 9
      src/Numerics/Random/SystemRandomSource.cs
  6. 23
      src/UnitTests/Random/RandomTests.cs

12
src/Numerics/Numerics.csproj

@ -167,6 +167,11 @@
<Compile Include="LinearRegression\WeightedRegression.cs" />
<Compile Include="LinearRegression\SimpleRegression.cs" />
<Compile Include="LinearRegression\Util.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Providers\LinearAlgebra\Acml\AcmlLinearAlgebraProvider.Complex.cs" />
<Compile Include="Providers\LinearAlgebra\Acml\AcmlLinearAlgebraProvider.Complex32.cs" />
<Compile Include="Providers\LinearAlgebra\Acml\AcmlLinearAlgebraProvider.Double.cs" />
@ -428,11 +433,6 @@
<Compile Include="Interpolation\SplineBoundaryCondition.cs" />
<Compile Include="Precision.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Random\RandomSource.cs" />
<Compile Include="Random\Mcg31m1.cs" />
<Compile Include="Random\Mcg59.cs" />
@ -472,8 +472,8 @@
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

9
src/Numerics/Properties/Resources.Designer.cs

@ -314,6 +314,15 @@ namespace MathNet.Numerics.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to In the specified range, the exclusive maximum must be greater than the inclusive minimum..
/// </summary>
public static string ArgumentMaxExclusiveMustBeLargerThanMinInclusive {
get {
return ResourceManager.GetString("ArgumentMaxExclusiveMustBeLargerThanMinInclusive", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to In the specified range, the minimum is greater than maximum..
/// </summary>

57
src/Numerics/Properties/Resources.resx

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@ -282,6 +282,9 @@
<data name="ArgumentMinValueGreaterThanMaxValue" xml:space="preserve">
<value>In the specified range, the minimum is greater than maximum.</value>
</data>
<data name="ArgumentMaxExclusiveMustBeLargerThanMinInclusive" xml:space="preserve">
<value>In the specified range, the exclusive maximum must be greater than the inclusive minimum.</value>
</data>
<data name="ArgumentUpperBoundMustBeLargerThanOrEqualToLowerBound" xml:space="preserve">
<value>The upper bound must be at least as large as the lower bound.</value>
</data>

119
src/Numerics/Random/RandomSource.cs

@ -3,7 +3,7 @@
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
//
// Copyright (c) 2009-2015 Math.NET
// Copyright (c) 2009-2016 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
@ -133,28 +133,39 @@ namespace MathNet.Numerics.Random
return DoSampleInteger();
}
}
return DoSampleInteger();
else
{
return DoSampleInteger();
}
}
/// <summary>
/// Returns a random number less then a specified maximum.
/// </summary>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive ≥ 1.</param>
/// <returns>A 32-bit signed integer less than <paramref name="maxExclusive"/>.</returns>
/// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="maxExclusive"/> is negative. </exception>
/// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="maxExclusive"/> is zero or negative.</exception>
public sealed override int Next(int maxExclusive)
{
// Invalid case: Zero and less are not valid use cases.
if (maxExclusive <= 0)
{
throw new ArgumentException(Resources.ArgumentMustBePositive);
}
// Fast case: Only zero is allowed to be returned. No sampling is needed.
if (maxExclusive == 1)
{
return 0;
}
// Simple case: standard range
if (maxExclusive == int.MaxValue)
{
return Next();
}
// Sample with maxExclusive ≥ 2
if (_threadSafe)
{
lock (_lock)
@ -162,28 +173,39 @@ namespace MathNet.Numerics.Random
return DoSampleInteger(maxExclusive);
}
}
return DoSampleInteger(maxExclusive);
else
{
return DoSampleInteger(maxExclusive);
}
}
/// <summary>
/// Returns a random number within a specified range.
/// </summary>
/// <param name="minInclusive">The inclusive lower bound of the random number returned.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. <paramref name="maxExclusive"/> must be greater than or equal to <paramref name="minInclusive"/>.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive > minExclusive.</param>
/// <returns>
/// A 32-bit signed integer greater than or equal to <paramref name="minInclusive"/> and less than <paramref name="maxExclusive"/>; that is, the range of return values includes <paramref name="minInclusive"/> but not <paramref name="maxExclusive"/>. If <paramref name="minInclusive"/> equals <paramref name="maxExclusive"/>, <paramref name="minInclusive"/> is returned.
/// </returns>
/// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="minInclusive"/> is greater than <paramref name="maxExclusive"/>. </exception>
public sealed override int Next(int minInclusive, int maxExclusive)
{
if (minInclusive > maxExclusive)
// Invalid case: empty range.
if (minInclusive >= maxExclusive)
{
throw new ArgumentException(Resources.ArgumentMinValueGreaterThanMaxValue);
throw new ArgumentException(Resources.ArgumentMaxExclusiveMustBeLargerThanMinInclusive);
}
// Fast case: Only minInclusive is allowed to be returned. No sampling is needed.
if (maxExclusive == minInclusive + 1)
{
return minInclusive;
}
// Simple case: simple range
if (minInclusive == 0)
{
// Simple case: standard range
if (maxExclusive == int.MaxValue)
{
return Next();
@ -192,6 +214,7 @@ namespace MathNet.Numerics.Random
return Next(maxExclusive);
}
// Sample with maxExclusive ≥ minExclusive + 2
if (_threadSafe)
{
lock (_lock)
@ -199,8 +222,10 @@ namespace MathNet.Numerics.Random
return DoSampleInteger(minInclusive, maxExclusive);
}
}
return DoSampleInteger(minInclusive, maxExclusive);
else
{
return DoSampleInteger(minInclusive, maxExclusive);
}
}
/// <summary>
@ -243,15 +268,30 @@ namespace MathNet.Numerics.Random
/// Fills an array with random numbers within a specified range.
/// </summary>
/// <param name="values">The array to fill with random values.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive ≥ 1.</param>
public void NextInt32s(int[] values, int maxExclusive)
{
// Invalid case: Zero and less are not valid use cases.
if (maxExclusive <= 0)
{
throw new ArgumentException(Resources.ArgumentMustBePositive);
}
// Fast case: Only zero is allowed to be returned. No sampling is needed.
if (maxExclusive == 1)
{
Array.Clear(values, 0, values.Length);
return;
}
// Simple case: standard range
if (maxExclusive == int.MaxValue)
{
NextInt32s(values);
return;
}
// Sample with maxExclusive ≥ 2
if (_threadSafe)
{
lock (_lock)
@ -271,27 +311,46 @@ namespace MathNet.Numerics.Random
}
}
/// <summary>
/// Returns an array with random 32-bit signed integers within the specified range.
/// </summary>
/// <param name="count">The size of the array to fill.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive ≥ 1.</param>
public int[] NextInt32s(int count, int maxExclusive)
{
var values = new int[count];
NextInt32s(values, maxExclusive);
return values;
}
/// <summary>
/// Fills an array with random numbers within a specified range.
/// </summary>
/// <param name="values">The array to fill with random values.</param>
/// <param name="minInclusive">The inclusive lower bound of the random number returned.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. <paramref name="maxExclusive"/> must be greater than or equal to <paramref name="minInclusive"/>.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive > minExclusive.</param>
public void NextInt32s(int[] values, int minInclusive, int maxExclusive)
{
if (minInclusive > maxExclusive)
// Invalid case: empty range.
if (minInclusive >= maxExclusive)
{
throw new ArgumentException(Resources.ArgumentMinValueGreaterThanMaxValue);
throw new ArgumentException(Resources.ArgumentMaxExclusiveMustBeLargerThanMinInclusive);
}
if (maxExclusive == int.MaxValue && minInclusive == 0)
// Fast case: Only minInclusive is allowed to be returned. No sampling is needed.
if (maxExclusive == minInclusive + 1)
{
NextInt32s(values);
for (var i = 0; i < values.Length; i++)
{
values[i] = minInclusive;
}
return;
}
// Simple case: simple range
if (minInclusive == 0)
{
// Simple case: standard range
if (maxExclusive == int.MaxValue)
{
NextInt32s(values);
@ -302,6 +361,7 @@ namespace MathNet.Numerics.Random
return;
}
// Sample with maxExclusive ≥ minExclusive + 2
if (_threadSafe)
{
lock (_lock)
@ -326,7 +386,7 @@ namespace MathNet.Numerics.Random
/// </summary>
/// <param name="count">The size of the array to fill.</param>
/// <param name="minInclusive">The inclusive lower bound of the random number returned.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. <paramref name="maxExclusive"/> must be greater than or equal to <paramref name="minInclusive"/>.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive > minExclusive.</param>
public int[] NextInt32s(int count, int minInclusive, int maxExclusive)
{
var values = new int[count];
@ -359,7 +419,7 @@ namespace MathNet.Numerics.Random
/// Returns an infinite sequence of random numbers within a specified range.
/// </summary>
/// <param name="minInclusive">The inclusive lower bound of the random number returned.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. <paramref name="maxExclusive"/> must be greater than or equal to <paramref name="minInclusive"/>.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive > minExclusive.</param>
public IEnumerable<int> NextInt32Sequence(int minInclusive, int maxExclusive)
{
if (minInclusive > maxExclusive)
@ -455,6 +515,13 @@ namespace MathNet.Numerics.Random
/// </summary>
protected virtual int DoSampleInt32WithNBits(int bitCount)
{
// Fast case: Only 0 is allowed to be returned
// No random call is needed
if (bitCount == 0)
{
return 0;
}
var bytes = new byte[4];
DoSampleBytes(bytes);
@ -472,6 +539,13 @@ namespace MathNet.Numerics.Random
/// </summary>
protected virtual long DoSampleInt64WithNBits(int bitCount)
{
// Fast case: Only 0 is allowed to be returned
// No random call is needed
if (bitCount == 0)
{
return 0;
}
var bytes = new byte[8];
DoSampleBytes(bytes);
@ -486,7 +560,7 @@ namespace MathNet.Numerics.Random
/// <summary>
/// Returns a random 32-bit signed integer within the specified range.
/// </summary>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive ≥ 2 (not verified, must be ensured by caller).</param>
protected virtual int DoSampleInteger(int maxExclusive)
{
// non-biased implementation
@ -516,9 +590,10 @@ namespace MathNet.Numerics.Random
/// Returns a random 32-bit signed integer within the specified range.
/// </summary>
/// <param name="minInclusive">The inclusive lower bound of the random number returned.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. <paramref name="maxExclusive"/> must be greater than or equal to <paramref name="minInclusive"/>.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive ≥ minExclusive + 2 (not verified, must be ensured by caller).</param>
protected virtual int DoSampleInteger(int minInclusive, int maxExclusive)
{
// Sample with maxExclusive ≥ 2
return DoSampleInteger(maxExclusive - minInclusive) + minInclusive;
}
}

9
src/Numerics/Random/SystemRandomSource.cs

@ -126,6 +126,10 @@ namespace MathNet.Numerics.Random
return _random.Next();
}
/// <summary>
/// Returns a random 32-bit signed integer within the specified range.
/// </summary>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive ≥ 2 (not verified, must be ensured by caller).</param>
protected override int DoSampleInteger(int maxExclusive)
{
return _random.Next(maxExclusive);
@ -135,12 +139,15 @@ namespace MathNet.Numerics.Random
/// Returns a random 32-bit signed integer within the specified range.
/// </summary>
/// <param name="minInclusive">The inclusive lower bound of the random number returned.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. <paramref name="maxExclusive"/> must be greater than or equal to <paramref name="minInclusive"/>.</param>
/// <param name="maxExclusive">The exclusive upper bound of the random number returned. Range: maxExclusive ≥ minExclusive + 2 (not verified, must be ensured by caller).</param>
protected override int DoSampleInteger(int minInclusive, int maxExclusive)
{
return _random.Next(minInclusive, maxExclusive);
}
/// <summary>
/// Fills the elements of a specified array of bytes with random numbers in full range, including zero and 255 (<see cref="F:System.Byte.MaxValue"/>).
/// </summary>
protected override void DoSampleBytes(byte[] buffer)
{
_random.NextBytes(buffer);

23
src/UnitTests/Random/RandomTests.cs

@ -78,6 +78,29 @@ namespace MathNet.Numerics.UnitTests.Random
Assert.IsTrue(sum >= (N/2.0) - (.05*N));
Assert.IsTrue(sum <= (N/2.0) + (.05*N));
var disposable = random as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
/// <summary>
/// Next() result is in boundaries.
/// </summary>
[Test]
public void Boundaries()
{
var random = (System.Random)Activator.CreateInstance(_randomType, new object[] { false });
for (var i = 1; i < N; i++)
{
var j = N;
var next = random.Next(i, j);
Assert.IsTrue(next >= i, string.Format("Value {0} is smaller than lower bound {1}", next, i));
Assert.IsTrue(next < j, string.Format("Value {0} is larger or equal to upper bound {1}", next, j));
}
var disposable = random as IDisposable;
if (disposable != null)
{

Loading…
Cancel
Save