Skip to content

Commit 7e0c11f

Browse files
authored
Merge pull request #906 from Silver-Fang/BigInteger
Combinatorics.GenerateVariation for large N
2 parents 6107c2e + 37f8e42 commit 7e0c11f

2 files changed

Lines changed: 116 additions & 0 deletions

File tree

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// <copyright file="CombinatoricsGenerationTest.cs" company="Math.NET">
2+
// Math.NET Numerics, part of the Math.NET Project
3+
// http://numerics.mathdotnet.com
4+
// http://github.com/mathnet/mathnet-numerics
5+
//
6+
// Copyright (c) 2009-2016 Math.NET
7+
//
8+
// Permission is hereby granted, free of charge, to any person
9+
// obtaining a copy of this software and associated documentation
10+
// files (the "Software"), to deal in the Software without
11+
// restriction, including without limitation the rights to use,
12+
// copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the
14+
// Software is furnished to do so, subject to the following
15+
// conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be
18+
// included in all copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22+
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27+
// OTHER DEALINGS IN THE SOFTWARE.
28+
// </copyright>
29+
30+
using NUnit.Framework;
31+
using System.Numerics;
32+
namespace MathNet.Numerics.UnitTests.CombinatoricsTests
33+
{
34+
35+
/// <summary>
36+
/// Test if combinatoric generation functions work correctly.
37+
/// </summary>
38+
[TestFixture]
39+
public class CombinatoricsGenerationTest
40+
{
41+
private void CGBIV(BigInteger n,int k)
42+
{
43+
BigInteger[] selection = Combinatorics.GenerateVariation(n, k);
44+
Assert.AreEqual(k, selection.Length);
45+
for (int i = 0; i < k; i++)
46+
{
47+
Assert.GreaterOrEqual(selection[i], 0);
48+
Assert.Less(selection[i], n);
49+
System.Console.Write(selection[i] + " ");
50+
}
51+
}
52+
static readonly BigInteger TestBI = new BigInteger(ulong.MaxValue) * 6;
53+
/// <summary>
54+
/// BigInteger isn't const so we can't simply set TestCase attributes. <br/>
55+
/// You need to check if the generated variation is statistically likely to be correct. Auto assertion won't find such error.
56+
/// </summary>
57+
/// <param name="n">N parameter.</param>
58+
/// <param name="k">K parameter.</param>
59+
/// <param name="expected">Expected value.</param>
60+
[Test]
61+
public void CanGenerateBigIntegerVariations()
62+
{
63+
//Main case
64+
CGBIV(TestBI, 6);
65+
//Border cases
66+
CGBIV(0, 0);
67+
CGBIV(6, 0);
68+
CGBIV(6, 6);
69+
}
70+
}
71+
}

src/Numerics/Combinatorics.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
using System.Collections.Generic;
3232
using System.Linq;
3333
using MathNet.Numerics.Random;
34+
using System.Numerics;
3435

3536
namespace MathNet.Numerics
3637
{
@@ -354,6 +355,50 @@ public static int[] GenerateVariation(int n, int k, System.Random randomSource =
354355
return selection;
355356
}
356357

358+
/// <summary>
359+
/// Generate a random variation, without repetition, by randomly selecting k of n elements with order. This is an O(k) space-complexity implementation optimized for very large N.<br/>
360+
/// The space complexity of Fisher-Yates Shuffling is O(n+k). When N is very large, the algorithm will be unexecutable in limited memory, and a more memory-efficient algorithm is needed.<br/>
361+
/// You can explicitly cast N to <see cref="BigInteger"/> if N is out of range of <see cref="int"/> or memory, so that this special implementation is called. However, this implementation is slower than Fisher-Yates Shuffling: don't call it if time is more critical than space.<br/>
362+
/// The K of type <see cref="BigInteger"/> seems impossible, because the returned array is of size K and must all be stored in memory.
363+
/// </summary>
364+
/// <param name="n">Number of elements in the set.</param>
365+
/// <param name="k">Number of elements to choose from the set. Each element is chosen at most once.</param>
366+
/// <param name="randomSource">The random number generator to use. Optional; the default random source will be used if null.</param>
367+
/// <returns>An array of length <c>K</c> that contains the indices of the selections as integers of the interval <c>[0, N)</c>.</returns>
368+
public static BigInteger[] GenerateVariation(BigInteger n, int k, System.Random randomSource = null)
369+
{
370+
if (n < 0) throw new ArgumentOutOfRangeException(nameof(n), "Value must not be negative (zero is ok).");
371+
if (k < 0) throw new ArgumentOutOfRangeException(nameof(k), "Value must not be negative (zero is ok).");
372+
if (k > n) throw new ArgumentOutOfRangeException(nameof(k), $"k must be smaller than or equal to n.");
373+
374+
var random = randomSource ?? SystemRandomSource.Default;
375+
BigInteger[] selection = new BigInteger[k];
376+
if (n == 0 || k == 0)
377+
return selection;
378+
selection[0] = random.NextBigIntegerSequence(0, n).First();
379+
bool[] CompareCache;
380+
bool KeepLooping;
381+
BigInteger RandomNumber;
382+
for (int a = 1; a < k; a++)
383+
{
384+
RandomNumber = random.NextBigIntegerSequence(0, n - a).First();
385+
CompareCache = Enumerable.Repeat(true, a).ToArray();
386+
do
387+
{
388+
KeepLooping = false;
389+
for (int b = 0; b < a; ++b)
390+
if (CompareCache[b] && RandomNumber >= selection[b])
391+
{
392+
CompareCache[b] = false;
393+
KeepLooping = true;
394+
RandomNumber++;
395+
}
396+
} while (KeepLooping);
397+
selection[a] = RandomNumber;
398+
}
399+
return selection;
400+
}
401+
357402
/// <summary>
358403
/// Select a random variation, without repetition, from a data sequence by randomly selecting k elements in random order.
359404
/// Implemented using partial Fisher-Yates Shuffling.

0 commit comments

Comments
 (0)