Browse Source

Combinatorics: generate or select random permutation, combination or variation #14

pull/316/merge
Christoph Ruegg 11 years ago
parent
commit
2e13d298e6
  1. 326
      src/Numerics/Combinatorics.cs
  2. 20
      src/Numerics/Properties/Resources.Designer.cs
  3. 6
      src/Numerics/Properties/Resources.resx
  4. 8
      src/Numerics/Random/RandomSource.cs

326
src/Numerics/Combinatorics.cs

@ -4,7 +4,7 @@
// http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com
//
// Copyright (c) 2009-2010 Math.NET
// Copyright (c) 2009-2015 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
@ -28,17 +28,21 @@
// OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
using System;
using System.Collections.Generic;
using System.Linq;
using MathNet.Numerics.Properties;
using MathNet.Numerics.Random;
namespace MathNet.Numerics
{
using System;
/// <summary>
/// Enumerative Combinatorics and Counting.
/// </summary>
public static class Combinatorics
{
/// <summary>
/// Counts the number of possible variations without repetition.
/// Count the number of possible variations without repetition.
/// The order matters and each object can be chosen only once.
/// </summary>
/// <param name="n">Number of elements in the set.</param>
@ -53,12 +57,12 @@ namespace MathNet.Numerics
return Math.Floor(
0.5 + Math.Exp(
SpecialFunctions.FactorialLn(n)
- SpecialFunctions.FactorialLn(n - k)));
SpecialFunctions.FactorialLn(n)
- SpecialFunctions.FactorialLn(n - k)));
}
/// <summary>
/// Counts the number of possible variations with repetition.
/// Count the number of possible variations with repetition.
/// The order matters and each object can be chosen more than once.
/// </summary>
/// <param name="n">Number of elements in the set.</param>
@ -75,7 +79,7 @@ namespace MathNet.Numerics
}
/// <summary>
/// Counts the number of possible combinations without repetition.
/// Count the number of possible combinations without repetition.
/// The order does not matter and each object can be chosen only once.
/// </summary>
/// <param name="n">Number of elements in the set.</param>
@ -87,7 +91,7 @@ namespace MathNet.Numerics
}
/// <summary>
/// Counts the number of possible combinations with repetition.
/// Count the number of possible combinations with repetition.
/// The order does not matter and an object can be chosen more than once.
/// </summary>
/// <param name="n">Number of elements in the set.</param>
@ -107,13 +111,13 @@ namespace MathNet.Numerics
return Math.Floor(
0.5 + Math.Exp(
SpecialFunctions.FactorialLn(n + k - 1)
- SpecialFunctions.FactorialLn(k)
- SpecialFunctions.FactorialLn(n - 1)));
SpecialFunctions.FactorialLn(n + k - 1)
- SpecialFunctions.FactorialLn(k)
- SpecialFunctions.FactorialLn(n - 1)));
}
/// <summary>
/// Counts the number of possible permutations (without repetition).
/// Count the number of possible permutations (without repetition).
/// </summary>
/// <param name="n">Number of (distinguishable) elements in the set.</param>
/// <returns>Maximum number of permutations without repetition.</returns>
@ -121,5 +125,299 @@ namespace MathNet.Numerics
{
return SpecialFunctions.Factorial(n);
}
/// <summary>
/// Generate a random permutation, without repetition, by generating the index numbers 0 to N-1 and shuffle them randomly.
/// Implemented using Fisher-Yates Shuffling.
/// </summary>
/// <returns>An array of length <c>N</c> that contains (in any order) the integers of the interval <c>[0, N)</c>.</returns>
/// <param name="n">Number of (distinguishable) elements in the set.</param>
/// <param name="randomSource">The random number generator to use. Optional; the default random source will be used if null.</param>
public static int[] GeneratePermutation(int n, System.Random randomSource = null)
{
if (n < 0) throw new ArgumentOutOfRangeException("n", n, Resources.ArgumentNotNegative);
int[] indices = new int[n];
for (int i = 0; i < indices.Length; i++)
{
indices[i] = i;
}
SelectPermutationInplace(indices, randomSource);
return indices;
}
/// <summary>
/// Select a random permutation, without repetition, from a data array by reordering the provided array in-place.
/// Implemented using Fisher-Yates Shuffling. The provided data array will be modified.
/// </summary>
/// <param name="data">The data array to be reordered. The array will be modified by this routine.</param>
/// <param name="randomSource">The random number generator to use. Optional; the default random source will be used if null.</param>
public static void SelectPermutationInplace<T>(T[] data, System.Random randomSource = null)
{
var random = randomSource ?? SystemRandomSource.Default;
// Fisher-Yates Shuffling
for (int i = data.Length - 1; i > 0; i--)
{
int swapIndex = random.Next(i + 1);
T swap = data[i];
data[i] = data[swapIndex];
data[swapIndex] = swap;
}
}
/// <summary>
/// Select a random permutation from a data sequence by returning the provided data in random order.
/// Implemented using Fisher-Yates Shuffling.
/// </summary>
/// <param name="data">The data elements to be reordered.</param>
/// <param name="randomSource">The random number generator to use. Optional; the default random source will be used if null.</param>
public static IEnumerable<T> SelectPermutation<T>(this IEnumerable<T> data, System.Random randomSource = null)
{
var random = randomSource ?? SystemRandomSource.Default;
T[] array = data.ToArray();
// Fisher-Yates Shuffling
for (int i = array.Length - 1; i >= 0; i--)
{
int k = random.Next(i + 1);
yield return array[k];
array[k] = array[i];
}
}
/// <summary>
/// Generate a random combination, without repetition, by randomly selecting some of N elements.
/// </summary>
/// <param name="n">Number of elements in the set.</param>
/// <param name="randomSource">The random number generator to use. Optional; the default random source will be used if null.</param>
/// <returns>Boolean mask array of length <c>N</c>, for each item true if it is selected.</returns>
public static bool[] GenerateCombination(int n, System.Random randomSource = null)
{
if (n < 0) throw new ArgumentOutOfRangeException("n", n, Resources.ArgumentNotNegative);
var random = randomSource ?? SystemRandomSource.Default;
bool[] mask = new bool[n];
for (int i = 0; i < mask.Length; i++)
{
mask[i] = random.NextBoolean();
}
return mask;
}
/// <summary>
/// Generate a random combination, without repetition, by randomly selecting k of N elements.
/// </summary>
/// <param name="n">Number of elements in the set.</param>
/// <param name="k">Number of elements to choose from the set. Each element is chosen at most once.</param>
/// <param name="randomSource">The random number generator to use. Optional; the default random source will be used if null.</param>
/// <returns>Boolean mask array of length <c>N</c>, for each item true if it is selected.</returns>
public static bool[] GenerateCombination(int n, int k, System.Random randomSource = null)
{
if (n < 0) throw new ArgumentOutOfRangeException("n", n, Resources.ArgumentNotNegative);
if (k < 0) throw new ArgumentOutOfRangeException("k", k, Resources.ArgumentNotNegative);
if (k > n) throw new ArgumentOutOfRangeException("k", k, string.Format(Resources.ArgumentOutOfRangeSmallerEqual, "k", "n"));
var random = randomSource ?? SystemRandomSource.Default;
bool[] mask = new bool[n];
if (k*3 < n)
{
// just pick and try
int selectionCount = 0;
while (selectionCount < k)
{
int index = random.Next(n);
if (!mask[index])
{
mask[index] = true;
selectionCount++;
}
}
return mask;
}
// based on permutation
int[] permutation = GeneratePermutation(n, random);
for (int i = 0; i < k; i++)
{
mask[permutation[i]] = true;
}
return mask;
}
/// <summary>
/// Select a random combination, without repetition, from a data sequence by selecting k elements in original order.
/// </summary>
/// <param name="data">The data source to choose from.</param>
/// <param name="elementsToChoose">Number of elements (k) to choose from the data set. Each element is chosen at most once.</param>
/// <param name="randomSource">The random number generator to use. Optional; the default random source will be used if null.</param>
/// <returns>The chosen combination, in the original order.</returns>
public static IEnumerable<T> SelectCombination<T>(this IEnumerable<T> data, int elementsToChoose, System.Random randomSource = null)
{
T[] array = data as T[] ?? data.ToArray();
if (elementsToChoose < 0) throw new ArgumentOutOfRangeException("elementsToChoose", elementsToChoose, Resources.ArgumentNotNegative);
if (elementsToChoose > array.Length) throw new ArgumentOutOfRangeException("elementsToChoose", elementsToChoose, string.Format(Resources.ArgumentOutOfRangeSmallerEqual, "elementsToChoose", "data.Count"));
bool[] mask = GenerateCombination(array.Length, elementsToChoose, randomSource);
for (int i = 0; i < mask.Length; i++)
{
if (mask[i])
{
yield return array[i];
}
}
}
/// <summary>
/// Generates a random combination, with repetition, by randomly selecting k of N elements.
/// </summary>
/// <param name="n">Number of elements in the set.</param>
/// <param name="k">Number of elements to choose from the set. Elements can be chosen more than once.</param>
/// <param name="randomSource">The random number generator to use. Optional; the default random source will be used if null.</param>
/// <returns>Integer mask array of length <c>N</c>, for each item the number of times it was selected.</returns>
public static int[] GenerateCombinationWithRepetition(int n, int k, System.Random randomSource = null)
{
if (n < 0) throw new ArgumentOutOfRangeException("n", n, Resources.ArgumentNotNegative);
if (k < 0) throw new ArgumentOutOfRangeException("k", k, Resources.ArgumentNotNegative);
var random = randomSource ?? SystemRandomSource.Default;
int[] mask = new int[n];
for (int i = 0; i < k; i++)
{
mask[random.Next(n)]++;
}
return mask;
}
/// <summary>
/// Select a random combination, with repetition, from a data sequence by selecting k elements in original order.
/// </summary>
/// <param name="data">The data source to choose from.</param>
/// <param name="elementsToChoose">Number of elements (k) to choose from the data set. Elements can be chosen more than once.</param>
/// <param name="randomSource">The random number generator to use. Optional; the default random source will be used if null.</param>
/// <returns>The chosen combination with repetition, in the original order.</returns>
public static IEnumerable<T> SelectCombinationWithRepetition<T>(this IEnumerable<T> data, int elementsToChoose, System.Random randomSource = null)
{
if (elementsToChoose < 0) throw new ArgumentOutOfRangeException("elementsToChoose", elementsToChoose, Resources.ArgumentNotNegative);
T[] array = data as T[] ?? data.ToArray();
int[] mask = GenerateCombinationWithRepetition(array.Length, elementsToChoose, randomSource);
for (int i = 0; i < mask.Length; i++)
{
for (int j = 0; j < mask[i]; j++)
{
yield return array[i];
}
}
}
/// <summary>
/// Generate a random variation, without repetition, by randomly selecting k of n elements with order.
/// Implemented using partial Fisher-Yates Shuffling.
/// </summary>
/// <param name="n">Number of elements in the set.</param>
/// <param name="k">Number of elements to choose from the set. Each element is chosen at most once.</param>
/// <param name="randomSource">The random number generator to use. Optional; the default random source will be used if null.</param>
/// <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>
public static int[] GenerateVariation(int n, int k, System.Random randomSource = null)
{
if (n < 0) throw new ArgumentOutOfRangeException("n", n, Resources.ArgumentNotNegative);
if (k < 0) throw new ArgumentOutOfRangeException("k", k, Resources.ArgumentNotNegative);
if (k > n) throw new ArgumentOutOfRangeException("k", k, string.Format(Resources.ArgumentOutOfRangeSmallerEqual, "k", "n"));
var random = randomSource ?? SystemRandomSource.Default;
int[] indices = new int[n];
for (int i = 0; i < indices.Length; i++)
{
indices[i] = i;
}
// Partial Fisher-Yates Shuffling
int[] selection = new int[k];
for (int i = 0, j = indices.Length - 1; i < selection.Length; i++, j--)
{
int swapIndex = random.Next(j + 1);
selection[i] = indices[swapIndex];
indices[swapIndex] = indices[j];
}
return selection;
}
/// <summary>
/// Select a random variation, without repetition, from a data sequence by randomly selecting k elements in random order.
/// Implemented using partial Fisher-Yates Shuffling.
/// </summary>
/// <param name="data">The data source to choose from.</param>
/// <param name="elementsToChoose">Number of elements (k) to choose from the set. Each element is chosen at most once.</param>
/// <param name="randomSource">The random number generator to use. Optional; the default random source will be used if null.</param>
/// <returns>The chosen variation, in random order.</returns>
public static IEnumerable<T> SelectVariation<T>(this IEnumerable<T> data, int elementsToChoose, System.Random randomSource = null)
{
var random = randomSource ?? SystemRandomSource.Default;
T[] array = data.ToArray();
if (elementsToChoose < 0) throw new ArgumentOutOfRangeException("elementsToChoose", elementsToChoose, Resources.ArgumentNotNegative);
if (elementsToChoose > array.Length) throw new ArgumentOutOfRangeException("elementsToChoose", elementsToChoose, string.Format(Resources.ArgumentOutOfRangeSmallerEqual, "elementsToChoose", "data.Count"));
// Partial Fisher-Yates Shuffling
for (int i = array.Length - 1; i >= array.Length - elementsToChoose; i--)
{
int swapIndex = random.Next(i + 1);
yield return array[swapIndex];
array[swapIndex] = array[i];
}
}
/// <summary>
/// Generate a random variation, with repetition, by randomly selecting k of n elements with order.
/// </summary>
/// <param name="n">Number of elements in the set.</param>
/// <param name="k">Number of elements to choose from the set. Elements can be chosen more than once.</param>
/// <param name="randomSource">The random number generator to use. Optional; the default random source will be used if null.</param>
/// <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>
public static int[] GenerateVariationWithRepetition(int n, int k, System.Random randomSource = null)
{
if (n < 0) throw new ArgumentOutOfRangeException("n", n, Resources.ArgumentNotNegative);
if (k < 0) throw new ArgumentOutOfRangeException("k", k, Resources.ArgumentNotNegative);
var random = randomSource ?? SystemRandomSource.Default;
int[] ret = new int[k];
random.NextInt32s(ret, 0, n);
return ret;
}
/// <summary>
/// Select a random variation, with repetition, from a data sequence by randomly selecting k elements in random order.
/// </summary>
/// <param name="data">The data source to choose from.</param>
/// <param name="elementsToChoose">Number of elements (k) to choose from the data set. Elements can be chosen more than once.</param>
/// <param name="randomSource">The random number generator to use. Optional; the default random source will be used if null.</param>
/// <returns>The chosen variation with repetition, in random order.</returns>
public static IEnumerable<T> SelectVariationWithRepetition<T>(this IEnumerable<T> data, int elementsToChoose, System.Random randomSource = null)
{
if (elementsToChoose < 0) throw new ArgumentOutOfRangeException("elementsToChoose", elementsToChoose, Resources.ArgumentNotNegative);
T[] array = data as T[] ?? data.ToArray();
int[] indices = GenerateVariationWithRepetition(array.Length, elementsToChoose, randomSource);
for (int i = 0; i < indices.Length; i++)
{
yield return array[indices[i]];
}
}
}
}
}

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

@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34014
// Runtime Version:4.0.30319.34209
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@ -386,6 +386,24 @@ namespace MathNet.Numerics.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to {0} must be smaller than {1}..
/// </summary>
public static string ArgumentOutOfRangeSmaller {
get {
return ResourceManager.GetString("ArgumentOutOfRangeSmaller", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} must be smaller than or equal to {1}..
/// </summary>
public static string ArgumentOutOfRangeSmallerEqual {
get {
return ResourceManager.GetString("ArgumentOutOfRangeSmallerEqual", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The chosen parameter set is invalid (probably some value is out of range)..
/// </summary>

6
src/Numerics/Properties/Resources.resx

@ -183,6 +183,12 @@
<data name="ArgumentOutOfRangeGreaterEqual" xml:space="preserve">
<value>{0} must be greater than or equal to {1}.</value>
</data>
<data name="ArgumentOutOfRangeSmaller" xml:space="preserve">
<value>{0} must be smaller than {1}.</value>
</data>
<data name="ArgumentOutOfRangeSmallerEqual" xml:space="preserve">
<value>{0} must be smaller than or equal to {1}.</value>
</data>
<data name="ArgumentParameterSetInvalid" xml:space="preserve">
<value>The chosen parameter set is invalid (probably some value is out of range).</value>
</data>

8
src/Numerics/Random/RandomSource.cs

@ -3,9 +3,9 @@
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com
//
//
// Copyright (c) 2009-2013 Math.NET
//
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
@ -14,10 +14,10 @@
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND

Loading…
Cancel
Save