diff --git a/src/Numerics/Combinatorics.cs b/src/Numerics/Combinatorics.cs index 131f78a5..5ceb8476 100644 --- a/src/Numerics/Combinatorics.cs +++ b/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. // +using System; +using System.Collections.Generic; +using System.Linq; +using MathNet.Numerics.Properties; +using MathNet.Numerics.Random; + namespace MathNet.Numerics { - using System; - /// /// Enumerative Combinatorics and Counting. /// public static class Combinatorics { /// - /// 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. /// /// Number of elements in the set. @@ -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))); } /// - /// 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. /// /// Number of elements in the set. @@ -75,7 +79,7 @@ namespace MathNet.Numerics } /// - /// 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. /// /// Number of elements in the set. @@ -87,7 +91,7 @@ namespace MathNet.Numerics } /// - /// 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. /// /// Number of elements in the set. @@ -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))); } /// - /// Counts the number of possible permutations (without repetition). + /// Count the number of possible permutations (without repetition). /// /// Number of (distinguishable) elements in the set. /// Maximum number of permutations without repetition. @@ -121,5 +125,299 @@ namespace MathNet.Numerics { return SpecialFunctions.Factorial(n); } + + /// + /// Generate a random permutation, without repetition, by generating the index numbers 0 to N-1 and shuffle them randomly. + /// Implemented using Fisher-Yates Shuffling. + /// + /// An array of length N that contains (in any order) the integers of the interval [0, N). + /// Number of (distinguishable) elements in the set. + /// The random number generator to use. Optional; the default random source will be used if null. + 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; + } + + /// + /// 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. + /// + /// The data array to be reordered. The array will be modified by this routine. + /// The random number generator to use. Optional; the default random source will be used if null. + public static void SelectPermutationInplace(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; + } + } + + /// + /// Select a random permutation from a data sequence by returning the provided data in random order. + /// Implemented using Fisher-Yates Shuffling. + /// + /// The data elements to be reordered. + /// The random number generator to use. Optional; the default random source will be used if null. + public static IEnumerable SelectPermutation(this IEnumerable 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]; + } + } + + /// + /// Generate a random combination, without repetition, by randomly selecting some of N elements. + /// + /// Number of elements in the set. + /// The random number generator to use. Optional; the default random source will be used if null. + /// Boolean mask array of length N, for each item true if it is selected. + 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; + } + + /// + /// Generate a random combination, without repetition, by randomly selecting k of N elements. + /// + /// Number of elements in the set. + /// Number of elements to choose from the set. Each element is chosen at most once. + /// The random number generator to use. Optional; the default random source will be used if null. + /// Boolean mask array of length N, for each item true if it is selected. + 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; + } + + /// + /// Select a random combination, without repetition, from a data sequence by selecting k elements in original order. + /// + /// The data source to choose from. + /// Number of elements (k) to choose from the data set. Each element is chosen at most once. + /// The random number generator to use. Optional; the default random source will be used if null. + /// The chosen combination, in the original order. + public static IEnumerable SelectCombination(this IEnumerable 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]; + } + } + } + + /// + /// Generates a random combination, with repetition, by randomly selecting k of N elements. + /// + /// Number of elements in the set. + /// Number of elements to choose from the set. Elements can be chosen more than once. + /// The random number generator to use. Optional; the default random source will be used if null. + /// Integer mask array of length N, for each item the number of times it was selected. + 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; + } + + /// + /// Select a random combination, with repetition, from a data sequence by selecting k elements in original order. + /// + /// The data source to choose from. + /// Number of elements (k) to choose from the data set. Elements can be chosen more than once. + /// The random number generator to use. Optional; the default random source will be used if null. + /// The chosen combination with repetition, in the original order. + public static IEnumerable SelectCombinationWithRepetition(this IEnumerable 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]; + } + } + } + + /// + /// Generate a random variation, without repetition, by randomly selecting k of n elements with order. + /// Implemented using partial Fisher-Yates Shuffling. + /// + /// Number of elements in the set. + /// Number of elements to choose from the set. Each element is chosen at most once. + /// The random number generator to use. Optional; the default random source will be used if null. + /// An array of length K that contains the indices of the selections as integers of the interval [0, N). + 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; + } + + /// + /// Select a random variation, without repetition, from a data sequence by randomly selecting k elements in random order. + /// Implemented using partial Fisher-Yates Shuffling. + /// + /// The data source to choose from. + /// Number of elements (k) to choose from the set. Each element is chosen at most once. + /// The random number generator to use. Optional; the default random source will be used if null. + /// The chosen variation, in random order. + public static IEnumerable SelectVariation(this IEnumerable 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]; + } + } + + /// + /// Generate a random variation, with repetition, by randomly selecting k of n elements with order. + /// + /// Number of elements in the set. + /// Number of elements to choose from the set. Elements can be chosen more than once. + /// The random number generator to use. Optional; the default random source will be used if null. + /// An array of length K that contains the indices of the selections as integers of the interval [0, N). + 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; + } + + /// + /// Select a random variation, with repetition, from a data sequence by randomly selecting k elements in random order. + /// + /// The data source to choose from. + /// Number of elements (k) to choose from the data set. Elements can be chosen more than once. + /// The random number generator to use. Optional; the default random source will be used if null. + /// The chosen variation with repetition, in random order. + public static IEnumerable SelectVariationWithRepetition(this IEnumerable 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]]; + } + } } -} \ No newline at end of file +} diff --git a/src/Numerics/Properties/Resources.Designer.cs b/src/Numerics/Properties/Resources.Designer.cs index 996bcedc..301c0a64 100644 --- a/src/Numerics/Properties/Resources.Designer.cs +++ b/src/Numerics/Properties/Resources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // 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 { } } + /// + /// Looks up a localized string similar to {0} must be smaller than {1}.. + /// + public static string ArgumentOutOfRangeSmaller { + get { + return ResourceManager.GetString("ArgumentOutOfRangeSmaller", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} must be smaller than or equal to {1}.. + /// + public static string ArgumentOutOfRangeSmallerEqual { + get { + return ResourceManager.GetString("ArgumentOutOfRangeSmallerEqual", resourceCulture); + } + } + /// /// Looks up a localized string similar to The chosen parameter set is invalid (probably some value is out of range).. /// diff --git a/src/Numerics/Properties/Resources.resx b/src/Numerics/Properties/Resources.resx index e1360503..6713e906 100644 --- a/src/Numerics/Properties/Resources.resx +++ b/src/Numerics/Properties/Resources.resx @@ -183,6 +183,12 @@ {0} must be greater than or equal to {1}. + + {0} must be smaller than {1}. + + + {0} must be smaller than or equal to {1}. + The chosen parameter set is invalid (probably some value is out of range). diff --git a/src/Numerics/Random/RandomSource.cs b/src/Numerics/Random/RandomSource.cs index 601ddf9f..f40ab8ee 100644 --- a/src/Numerics/Random/RandomSource.cs +++ b/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