diff --git a/.gitignore b/.gitignore index 096e60a44..3ee4d94fc 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,6 @@ docs/content/Contributing.md docs/content/Contributors.md docs/content/ReleaseNotes.md docs/content/ReleaseNotes-*.md + +NUnitTests.csproj +NUnitTests/ \ No newline at end of file diff --git a/.paket/Paket.Restore.targets b/.paket/Paket.Restore.targets index d93abfef9..52f41c608 100644 --- a/.paket/Paket.Restore.targets +++ b/.paket/Paket.Restore.targets @@ -17,23 +17,43 @@ native /Library/Frameworks/Mono.framework/Commands/mono mono - - $(PaketToolsPath)paket - $(PaketRootPath)paket.exe - $(PaketToolsPath)paket.exe - $(PaketToolsPath)paket.exe - $(PaketToolsPath)paket + + $(PaketRootPath)paket.bootstrapper.exe + $(PaketToolsPath)paket.bootstrapper.exe + $([System.IO.Path]::GetDirectoryName("$(PaketBootStrapperExePath)"))\ + + + + + $(PaketRootPath)paket.exe + $(PaketToolsPath)paket.exe + $(PaketToolsPath)paket.exe + $(_PaketBootStrapperExeDir)paket.exe + paket.exe + + + $(PaketRootPath)paket + $(PaketToolsPath)paket + $(PaketToolsPath)paket + + + $(PaketRootPath)paket.exe + $(PaketToolsPath)paket.exe + + + $(PaketBootStrapperExeDir)paket.exe + + + paket <_PaketExeExtension>$([System.IO.Path]::GetExtension("$(PaketExePath)")) dotnet "$(PaketExePath)" - "$(PaketExePath)" $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)" - "$(PaketExePath)" + "$(PaketExePath)" + - $(PaketRootPath)paket.bootstrapper.exe - $(PaketToolsPath)paket.bootstrapper.exe "$(PaketBootStrapperExePath)" $(MonoPath) --runtime=v4.0.30319 "$(PaketBootStrapperExePath)" @@ -42,6 +62,9 @@ true true + + + True @@ -82,7 +105,11 @@ true - + + true @@ -163,6 +190,7 @@ runtime runtime true + true diff --git a/MathNet.Numerics.sln b/MathNet.Numerics.sln index 357c7c3c1..b19c6969d 100644 --- a/MathNet.Numerics.sln +++ b/MathNet.Numerics.sln @@ -18,9 +18,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Readme", "Readme", "{C2F374 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Numerics", "src\Numerics\Numerics.csproj", "{B7CAE5F4-A23F-4438-B5BE-41226618B695}" EndProject -Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp", "src\FSharp\FSharp.fsproj", "{37E8E802-A354-4114-BFC1-6E1357DA605B}" +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp", "src\FSharp\FSharp.fsproj", "{37E8E802-A354-4114-BFC1-6E1357DA605B}" EndProject -Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Tests", "src\FSharp.Tests\FSharp.Tests.fsproj", "{F2F8032B-A31D-4E33-A05E-F2CDCBFAA75D}" +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Tests", "src\FSharp.Tests\FSharp.Tests.fsproj", "{F2F8032B-A31D-4E33-A05E-F2CDCBFAA75D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Numerics.Tests", "src\Numerics.Tests\Numerics.Tests.csproj", "{DAF07AA8-C5C9-4963-98F7-2C3285064DAD}" EndProject diff --git a/src/FSharp/FSharp.fsproj b/src/FSharp/FSharp.fsproj index 3bf79fe91..7d625d566 100644 --- a/src/FSharp/FSharp.fsproj +++ b/src/FSharp/FSharp.fsproj @@ -57,4 +57,4 @@ Linear Algebra: optimized range checking in vectors and matrices - + \ No newline at end of file diff --git a/src/Numerics.Tests/CombinatoricsTests/CombinatoricsCountingTest.cs b/src/Numerics.Tests/CombinatoricsTests/CombinatoricsCountingTest.cs index 592416f77..2968a7f4f 100644 --- a/src/Numerics.Tests/CombinatoricsTests/CombinatoricsCountingTest.cs +++ b/src/Numerics.Tests/CombinatoricsTests/CombinatoricsCountingTest.cs @@ -28,6 +28,7 @@ // using NUnit.Framework; +using MathNet.Numerics; namespace MathNet.Numerics.UnitTests.CombinatoricsTests { diff --git a/src/Numerics.Tests/CombinatoricsTests/CombinatoricsTest.cs b/src/Numerics.Tests/CombinatoricsTests/CombinatoricsTest.cs new file mode 100644 index 000000000..069914946 --- /dev/null +++ b/src/Numerics.Tests/CombinatoricsTests/CombinatoricsTest.cs @@ -0,0 +1,103 @@ +using MathNet.Numerics.Combinations; +using NUnit.Framework; +using System; + +namespace NUnitTests +{ + /// + /// Permutation tests. + /// + [TestFixture()] + public class CombinatoricsTest + { + /// + /// Can create permutation from int array. + /// + /// Permutations index set. + [TestCase(new[] { -1 })] + [TestCase(new[] { 0 })] + [TestCase(new[] { 0, 1, 2, 3, 4, 5 })] + [TestCase(new[] { 0, 1, 2, 3, 4, 4 })] + [TestCase(new[] { 5, 4, 3, 2, 1, 0 })] + [TestCase(new[] { 0, 4, 3, 2, 1, 5 })] + [TestCase(new[] { 0, 3, 2, 1, 4, 5 })] + [TestCase(new[] { 5, 4, 3, 2, 1, 7 })] + public void CanCreateIntegerPermutation(int[] idx) + { + GC.KeepAlive(new Permutations(idx)); + } + + /// + /// Can create permutation from char array. + /// + /// Permutations index set. + [TestCase(new[] { 'A' })] + [TestCase(new[] { 'A', 'A', 'B', 'C', 'D', 'E' })] + [TestCase(new[] { 'A', 'B', 'C', 'D', 'E', 'F' })] + [TestCase(new[] { 'D', 'E', 'F', 'A', 'B', 'C' })] + public void CanCreateCharPermutation(char[] idx) + { + GC.KeepAlive(new Permutations(idx)); + } + + /// + /// Can create permutation from int array. + /// + /// Permutations index set. + [TestCase(new[] { -1 })] + [TestCase(new[] { 0 })] + [TestCase(new[] { 0, 1, 2, 3, 4, 5 })] + [TestCase(new[] { 0, 1, 2, 3, 4, 4 })] + [TestCase(new[] { 5, 4, 3, 2, 1, 0 })] + [TestCase(new[] { 0, 4, 3, 2, 1, 5 })] + [TestCase(new[] { 0, 3, 2, 1, 4, 5 })] + [TestCase(new[] { 5, 4, 3, 2, 1, 7 })] + public void CanCreateIntegerCombination(int[] idx) + { + GC.KeepAlive(new Combinations(idx, 3)); + } + + /// + /// Can create permutation from char array. + /// + /// Permutations index set. + [TestCase(new[] { 'A' })] + [TestCase(new[] { 'A', 'A', 'B', 'C', 'D', 'E' })] + [TestCase(new[] { 'A', 'B', 'C', 'D', 'E', 'F' })] + [TestCase(new[] { 'D', 'E', 'F', 'A', 'B', 'C' })] + public void CanCreateCharCombination(char[] idx) + { + GC.KeepAlive(new Combinations(idx, 3)); + } + + /// + /// Can create permutation from int array. + /// + /// Permutations index set. + [TestCase(new[] { -1 })] + [TestCase(new[] { 0 })] + [TestCase(new[] { 0, 1, 2, 3, 4, 5 })] + [TestCase(new[] { 0, 1, 2, 3, 4, 4 })] + [TestCase(new[] { 5, 4, 3, 2, 1, 0 })] + [TestCase(new[] { 0, 4, 3, 2, 1, 5 })] + [TestCase(new[] { 0, 3, 2, 1, 4, 5 })] + [TestCase(new[] { 5, 4, 3, 2, 1, 7 })] + public void CanCreateIntegerVariation(int[] idx) + { + GC.KeepAlive(new Variations(idx, 3)); + } + + /// + /// Can create permutation from char array. + /// + /// Permutations index set. + [TestCase(new[] { 'A' })] + [TestCase(new[] { 'A', 'A', 'B', 'C', 'D', 'E' })] + [TestCase(new[] { 'A', 'B', 'C', 'D', 'E', 'F' })] + [TestCase(new[] { 'D', 'E', 'F', 'A', 'B', 'C' })] + public void CanCreateCharVariation(char[] idx) + { + GC.KeepAlive(new Variations(idx, 3)); + } + } +} \ No newline at end of file diff --git a/src/Numerics/Combinatorics.cs b/src/Numerics/Combinatorics.cs index 63e66c9d9..c49a3424f 100644 --- a/src/Numerics/Combinatorics.cs +++ b/src/Numerics/Combinatorics.cs @@ -418,5 +418,73 @@ public static IEnumerable SelectVariationWithRepetition(this IEnumerable + /// Test if two lists are equal + /// + /// true, if equal was unordereded, false otherwise. + /// The first list to compare. + /// The second list to compare. + /// The 1st type parameter. + public static bool UnorderedEqual(ICollection a, ICollection b) + { + // Require that the counts are equal + if (a.Count != b.Count) + { + return false; + } + + // Initialize new Dictionary of the type + Dictionary d = new Dictionary(); + + // Add each key's frequency from collection A to the Dictionary + foreach (T item in a) + { + int c; + if (d.TryGetValue(item, out c)) + { + d[item] = c + 1; + } + else + { + d.Add(item, 1); + } + } + + // Add each key's frequency from collection B to the Dictionary + // Return early if we detect a mismatch + foreach (T item in b) + { + int c; + if (d.TryGetValue(item, out c)) + { + if (c == 0) + { + return false; + } + else + { + d[item] = c - 1; + } + } + else + { + // Not in dictionary + return false; + } + } + + // Verify that all frequencies are zero + foreach (int v in d.Values) + { + if (v != 0) + { + return false; + } + } + + // We know the collections are equal + return true; + } } } diff --git a/src/Numerics/Combinatorics/Combinations.cs b/src/Numerics/Combinatorics/Combinations.cs new file mode 100755 index 000000000..98c1f3250 --- /dev/null +++ b/src/Numerics/Combinatorics/Combinations.cs @@ -0,0 +1,348 @@ +// Copyright 2008 Adrian Akison +// Distributed under license terms of CPOL http://www.codeproject.com/info/cpol10.aspx +using System; +using System.Collections.Generic; +using System.Text; + +namespace MathNet.Numerics.Combinations +{ + /// + /// Combinations defines a meta-collection, typically a list of lists, of all possible + /// subsets of a particular size from the set of values. This list is enumerable and + /// allows the scanning of all possible combinations using a simple foreach() loop. + /// Within the returned set, there is no prescribed order. This follows the mathematical + /// concept of choose. For example, put 10 dominoes in a hat and pick 5. The number of possible + /// combinations is defined as "10 choose 5", which is calculated as (10!) / ((10 - 5)! * 5!). + /// + /// + /// The MetaCollectionType parameter of the constructor allows for the creation of + /// two types of sets, those with and without repetition in the output set when + /// presented with repetition in the input set. + /// + /// When given a input collect {A B C} and lower index of 2, the following sets are generated: + /// MetaCollectionType.WithRepetition => + /// {A A}, {A B}, {A C}, {B B}, {B C}, {C C} + /// MetaCollectionType.WithoutRepetition => + /// {A B}, {A C}, {B C} + /// + /// Input sets with multiple equal values will generate redundant combinations in proprotion + /// to the likelyhood of outcome. For example, {A A B B} and a lower index of 3 will generate: + /// {A A B} {A A B} {A B B} {A B B} + /// + /// The type of the values within the list. + public class Combinations : IMetaCollection { + #region Constructors + + /// + /// No default constructor, must provided a list of values and size. + /// + protected Combinations() { + ; + } + + /// + /// Create a combination set from the provided list of values. + /// The upper index is calculated as values.Count, the lower index is specified. + /// Collection type defaults to MetaCollectionType.WithoutRepetition + /// + /// List of values to select combinations from. + /// The size of each combination set to return. + public Combinations(IList values, int lowerIndex) { + Initialize(values, lowerIndex, GenerateOption.WithoutRepetition); + } + + /// + /// Create a combination set from the provided list of values. + /// The upper index is calculated as values.Count, the lower index is specified. + /// + /// List of values to select combinations from. + /// The size of each combination set to return. + /// The type of Combinations set to generate. + public Combinations(IList values, int lowerIndex, GenerateOption type) { + Initialize(values, lowerIndex, type); + } + + #endregion + + #region IEnumerable Interface + + /// + /// Gets an enumerator for collecting the list of combinations. + /// + /// The enumerator. + public IEnumerator> GetEnumerator() { + return new Enumerator(this); + } + + /// + /// Gets an enumerator for collecting the list of combinations. + /// + /// The enumerator. + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return new Enumerator(this); + } + + #endregion + + #region Enumerator Inner Class + + /// + /// The enumerator that enumerates each meta-collection of the enclosing Combinations class. + /// + public class Enumerator : IEnumerator> { + + #region Constructors + + /// + /// Construct a enumerator with the parent object. + /// + /// The source combinations object. + public Enumerator(Combinations source) { + myParent = source; + myPermutationsEnumerator = (Permutations.Enumerator)myParent.myPermutations.GetEnumerator(); + } + + #endregion + + #region IEnumerator interface + /// + /// Resets the combinations enumerator to the first combination. + /// + public void Reset() { + myPermutationsEnumerator.Reset(); + } + + /// + /// Advances to the next combination of items from the set. + /// + /// True if successfully moved to next combination, False if no more unique combinations exist. + /// + /// The heavy lifting is done by the permutations object, the combination is generated + /// by creating a new list of those items that have a true in the permutation parrellel array. + /// + public bool MoveNext() { + bool ret = myPermutationsEnumerator.MoveNext(); + myCurrentList = null; + return ret; + } + + /// + /// The current combination + /// + public IList Current { + get { + ComputeCurrent(); + return myCurrentList; + } + } + + /// + /// The current combination + /// + object System.Collections.IEnumerator.Current { + get { + ComputeCurrent(); + return myCurrentList; + } + } + + /// + /// Cleans up non-managed resources, of which there are none used here. + /// + public void Dispose() { + ; + } + + #endregion + + #region Heavy Lifting Members + + /// + /// The only complex function of this entire wrapper, ComputeCurrent() creates + /// a list of original values from the bool permutation provided. + /// The exception for accessing current (InvalidOperationException) is generated + /// by the call to .Current on the underlying enumeration. + /// + /// + /// To compute the current list of values, the underlying permutation object + /// which moves with this enumerator, is scanned differently based on the type. + /// The items have only two values, true and false, which have different meanings: + /// + /// For type WithoutRepetition, the output is a straightforward subset of the input array. + /// E.g. 6 choose 3 without repetition + /// Input array: {A B C D E F} + /// Permutations: {0 1 0 0 1 1} + /// Generates set: {A C D } + /// Note: size of permutation is equal to upper index. + /// + /// For type WithRepetition, the output is defined by runs of characters and when to + /// move to the next element. + /// E.g. 6 choose 5 with repetition + /// Input array: {A B C D E F} + /// Permutations: {0 1 0 0 1 1 0 0 1 1} + /// Generates set: {A B B D D } + /// Note: size of permutation is equal to upper index - 1 + lower index. + /// + private void ComputeCurrent() { + if(myCurrentList == null) { + myCurrentList = new List(); + int index = 0; + IList currentPermutation = (IList)myPermutationsEnumerator.Current; + for(int i = 0; i < currentPermutation.Count; ++i) { + if(currentPermutation[i] == false) { + myCurrentList.Add(myParent.myValues[index]); + if(myParent.Type == GenerateOption.WithoutRepetition) { + ++index; + } + } + else { + ++index; + } + } + } + } + + #endregion + + #region Data + + /// + /// Parent object this is an enumerator for. + /// + private Combinations myParent; + + /// + /// The current list of values, this is lazy evaluated by the Current property. + /// + private List myCurrentList; + + /// + /// An enumertor of the parents list of lexicographic orderings. + /// + private Permutations.Enumerator myPermutationsEnumerator; + + #endregion + } + #endregion + + #region IMetaList Interface + + /// + /// The number of unique combinations that are defined in this meta-collection. + /// This value is mathematically defined as Choose(M, N) where M is the set size + /// and N is the subset size. This is M! / (N! * (M-N)!). + /// + public long Count { + get { + return myPermutations.Count; + } + } + + /// + /// The type of Combinations set that is generated. + /// + public GenerateOption Type { + get { + return myMetaCollectionType; + } + } + + /// + /// The upper index of the meta-collection, equal to the number of items in the initial set. + /// + public int UpperIndex { + get { + return myValues.Count; + } + } + + /// + /// The lower index of the meta-collection, equal to the number of items returned each iteration. + /// + public int LowerIndex { + get { + return myLowerIndex; + } + } + + #endregion + + #region Heavy Lifting Members + + /// + /// Initialize the combinations by settings a copy of the values from the + /// + /// List of values to select combinations from. + /// The size of each combination set to return. + /// The type of Combinations set to generate. + /// + /// Copies the array and parameters and then creates a map of booleans that will + /// be used by a permutations object to refence the subset. This map is slightly + /// different based on whether the type is with or without repetition. + /// + /// When the type is WithoutRepetition, then a map of upper index elements is + /// created with lower index false's. + /// E.g. 8 choose 3 generates: + /// Map: {1 1 1 1 1 0 0 0} + /// Note: For sorting reasons, false denotes inclusion in output. + /// + /// When the type is WithRepetition, then a map of upper index - 1 + lower index + /// elements is created with the falses indicating that the 'current' element should + /// be included and the trues meaning to advance the 'current' element by one. + /// E.g. 8 choose 3 generates: + /// Map: {1 1 1 1 1 1 1 1 0 0 0} (7 trues, 3 falses). + /// + private void Initialize(IList values, int lowerIndex, GenerateOption type) { + myMetaCollectionType = type; + myLowerIndex = lowerIndex; + myValues = new List(); + myValues.AddRange(values); + List myMap = new List(); + if(type == GenerateOption.WithoutRepetition) { + for(int i = 0; i < myValues.Count; ++i) { + if(i >= myValues.Count - myLowerIndex) { + myMap.Add(false); + } + else { + myMap.Add(true); + } + } + } + else { + for(int i = 0; i < values.Count - 1; ++i) { + myMap.Add(true); + } + for(int i = 0; i < myLowerIndex; ++i) { + myMap.Add(false); + } + } + myPermutations = new Permutations(myMap); + } + + #endregion + + #region Data + + /// + /// Copy of values object is intialized with, required for enumerator reset. + /// + private List myValues; + + /// + /// Permutations object that handles permutations on booleans for combination inclusion. + /// + private Permutations myPermutations; + + /// + /// The type of the combination collection. + /// + private GenerateOption myMetaCollectionType; + + /// + /// The lower index defined in the constructor. + /// + private int myLowerIndex; + + #endregion + } +} diff --git a/src/Numerics/Combinatorics/GenerateOption.cs b/src/Numerics/Combinatorics/GenerateOption.cs new file mode 100755 index 000000000..776abe04b --- /dev/null +++ b/src/Numerics/Combinatorics/GenerateOption.cs @@ -0,0 +1,23 @@ +// Copyright 2008 Adrian Akison +// Distributed under license terms of CPOL http://www.codeproject.com/info/cpol10.aspx +using System; +using System.Collections.Generic; +using System.Text; + +namespace MathNet.Numerics.Combinations +{ + /// + /// Indicates whether a Permutation, Combination or Variation meta-collections + /// generate repetition sets. + /// + public enum GenerateOption { + /// + /// Do not generate additional sets, typical implementation. + /// + WithoutRepetition, + /// + /// Generate additional sets even if repetition is required. + /// + WithRepetition + } +} diff --git a/src/Numerics/Combinatorics/IMetaCollection.cs b/src/Numerics/Combinatorics/IMetaCollection.cs new file mode 100755 index 000000000..75e0afb1e --- /dev/null +++ b/src/Numerics/Combinatorics/IMetaCollection.cs @@ -0,0 +1,39 @@ +// Copyright 2008 Adrian Akison +// Distributed under license terms of CPOL http://www.codeproject.com/info/cpol10.aspx +using System; +using System.Collections.Generic; +using System.Text; + +namespace MathNet.Numerics.Combinations +{ + /// + /// Interface for Permutations, Combinations and any other classes that present + /// a collection of collections based on an input collection. The enumerators that + /// this class inherits defines the mechanism for enumerating through the collections. + /// + /// The of the elements in the collection, not the type of the collection. + interface IMetaCollection : IEnumerable> { + /// + /// The count of items in the collection. This is not inherited from + /// ICollection since this meta-collection cannot be extended by users. + /// + long Count { get; } + + /// + /// The type of the meta-collection, determining how the collections are + /// determined from the inputs. + /// + GenerateOption Type { get; } + + /// + /// The upper index of the meta-collection, which is the size of the input collection. + /// + int UpperIndex { get; } + + /// + /// The lower index of the meta-collection, which is the size of each output collection. + /// + int LowerIndex { get; } + } + +} diff --git a/src/Numerics/Combinatorics/Permutations.cs b/src/Numerics/Combinatorics/Permutations.cs new file mode 100755 index 000000000..00e4bad52 --- /dev/null +++ b/src/Numerics/Combinatorics/Permutations.cs @@ -0,0 +1,457 @@ +// Copyright 2008 Adrian Akison +// Distributed under license terms of CPOL http://www.codeproject.com/info/cpol10.aspx +using System; +using System.Collections; +using System.Collections.Generic; + +namespace MathNet.Numerics.Combinations +{ + /// + /// Permutations defines a meta-collection, typically a list of lists, of all + /// possible orderings of a set of values. This list is enumerable and allows + /// the scanning of all possible permutations using a simple foreach() loop. + /// The MetaCollectionType parameter of the constructor allows for the creation of + /// two types of sets, those with and without repetition in the output set when + /// presented with repetition in the input set. + /// + /// + /// When given a input collect {A A B}, the following sets are generated: + /// MetaCollectionType.WithRepetition => + /// {A A B}, {A B A}, {A A B}, {A B A}, {B A A}, {B A A} + /// MetaCollectionType.WithoutRepetition => + /// {A A B}, {A B A}, {B A A} + /// + /// When generating non-repetition sets, ordering is based on the lexicographic + /// ordering of the lists based on the provided Comparer. + /// If no comparer is provided, then T must be IComparable on T. + /// + /// When generating repetition sets, no comparisions are performed and therefore + /// no comparer is required and T does not need to be IComparable. + /// + /// The type of the values within the list. + public class Permutations : IMetaCollection { + + #region Constructors + + /// + /// No default constructor, must at least provided a list of values. + /// + protected Permutations() { + ; + } + + /// + /// Create a permutation set from the provided list of values. + /// The values (T) must implement IComparable. + /// If T does not implement IComparable use a constructor with an explict IComparer. + /// The repetition type defaults to MetaCollectionType.WithholdRepetitionSets + /// + /// List of values to permute. + public Permutations(IList values) { + Initialize(values, GenerateOption.WithoutRepetition, null); + } + + /// + /// Create a permutation set from the provided list of values. + /// If type is MetaCollectionType.WithholdRepetitionSets, then values (T) must implement IComparable. + /// If T does not implement IComparable use a constructor with an explict IComparer. + /// + /// List of values to permute. + /// The type of permutation set to calculate. + public Permutations(IList values, GenerateOption type) { + Initialize(values, type, null); + } + + /// + /// Create a permutation set from the provided list of values. + /// The values will be compared using the supplied IComparer. + /// The repetition type defaults to MetaCollectionType.WithholdRepetitionSets + /// + /// List of values to permute. + /// Comparer used for defining the lexigraphic order. + public Permutations(IList values, IComparer comparer) { + Initialize(values, GenerateOption.WithoutRepetition, comparer); + } + + #endregion + + #region IEnumerable Interface + + /// + /// Gets an enumerator for collecting the list of permutations. + /// + /// The enumerator. + public virtual IEnumerator GetEnumerator() { + return new Enumerator(this); + } + + /// + /// Gets an enumerator for collecting the list of permutations. + /// + /// The enumerator. + IEnumerator> IEnumerable>.GetEnumerator() { + return new Enumerator(this); + } + + #endregion + + #region Enumerator Inner-Class + + /// + /// The enumerator that enumerates each meta-collection of the enclosing Permutations class. + /// + public class Enumerator : IEnumerator> { + + #region Constructors + + /// + /// Construct a enumerator with the parent object. + /// + /// The source Permutations object. + public Enumerator(Permutations source) { + myParent = source; + myLexicographicalOrders = new int[source.myLexicographicOrders.Length]; + source.myLexicographicOrders.CopyTo(myLexicographicalOrders, 0); + Reset(); + } + + #endregion + + #region IEnumerator Interface + + /// + /// Resets the permutations enumerator to the first permutation. + /// This will be the first lexicographically order permutation. + /// + public void Reset() { + myPosition = Position.BeforeFirst; + } + + /// + /// Advances to the next permutation. + /// + /// True if successfully moved to next permutation, False if no more permutations exist. + /// + /// Continuation was tried (i.e. yield return) by was not nearly as efficient. + /// Performance is further increased by using value types and removing generics, that is, the LexicographicOrder parellel array. + /// This is a issue with the .NET CLR not optimizing as well as it could in this infrequently used scenario. + /// + public bool MoveNext() { + if(myPosition == Position.BeforeFirst) { + myValues = new List(myParent.myValues.Count); + myValues.AddRange(myParent.myValues); + Array.Sort(myLexicographicalOrders); + myPosition = Position.InSet; + } else if(myPosition == Position.InSet) { + if(myValues.Count < 2) { + myPosition = Position.AfterLast; + } + else if(NextPermutation() == false) { + myPosition = Position.AfterLast; + } + } + return myPosition != Position.AfterLast; + } + + /// + /// The current permutation. + /// + public object Current { + get { + if(myPosition == Position.InSet) { + return new List(myValues); + } + else { + throw new InvalidOperationException(); + } + } + } + + /// + /// The current permutation. + /// + IList IEnumerator>.Current { + get { + if(myPosition == Position.InSet) { + return new List(myValues); + } + else { + throw new InvalidOperationException(); + } + } + } + + /// + /// Cleans up non-managed resources, of which there are none used here. + /// + public virtual void Dispose() { + ; + } + + #endregion + + #region Heavy Lifting Methods + + /// + /// Calculates the next lexicographical permutation of the set. + /// This is a permutation with repetition where values that compare as equal will not + /// swap positions to create a new permutation. + /// http://www.cut-the-knot.org/do_you_know/AllPerm.shtml + /// E. W. Dijkstra, A Discipline of Programming, Prentice-Hall, 1997 + /// + /// True if a new permutation has been returned, false if not. + /// + /// This uses the integers of the lexicographical order of the values so that any + /// comparison of values are only performed during initialization. + /// + private bool NextPermutation() { + int i = myLexicographicalOrders.Length - 1; + while(myLexicographicalOrders[i - 1] >= myLexicographicalOrders[i]) { + --i; + if(i == 0) { + return false; + } + } + int j = myLexicographicalOrders.Length; + while(myLexicographicalOrders[j - 1] <= myLexicographicalOrders[i - 1]) { + --j; + } + Swap(i - 1, j - 1); + ++i; + j = myLexicographicalOrders.Length; + while(i < j) { + Swap(i - 1, j - 1); + ++i; + --j; + } + return true; + } + + /// + /// Helper function for swapping two elements within the internal collection. + /// This swaps both the lexicographical order and the values, maintaining the parallel array. + /// + private void Swap(int i, int j) { + myTemp = myValues[i]; + myValues[i] = myValues[j]; + myValues[j] = myTemp; + myKviTemp = myLexicographicalOrders[i]; + myLexicographicalOrders[i] = myLexicographicalOrders[j]; + myLexicographicalOrders[j] = myKviTemp; + } + + #endregion + + #region Data and Internal Members + /// + /// Single instance of swap variable for T, small performance improvement over declaring in Swap function scope. + /// + private T myTemp; + + /// + /// Single instance of swap variable for int, small performance improvement over declaring in Swap function scope. + /// + private int myKviTemp; + + /// + /// Flag indicating the position of the enumerator. + /// + private Position myPosition = Position.BeforeFirst; + + /// + /// Parrellel array of integers that represent the location of items in the myValues array. + /// This is generated at Initialization and is used as a performance speed up rather that + /// comparing T each time, much faster to let the CLR optimize around integers. + /// + private int[] myLexicographicalOrders; + + /// + /// The list of values that are current to the enumerator. + /// + private List myValues; + + /// + /// The set of permuations that this enumerator enumerates. + /// + private Permutations myParent; + + /// + /// Internal position type for tracking enumertor position. + /// + private enum Position { + BeforeFirst, + InSet, + AfterLast + } + + #endregion + + } + + #endregion + + #region IMetaList Interface + + /// + /// The count of all permutations that will be returned. + /// If type is MetaCollectionType.WithholdGeneratedSets, then this does not double count permutations with multiple identical values. + /// I.e. count of permutations of "AAB" will be 3 instead of 6. + /// If type is MetaCollectionType.WithRepetition, then this is all combinations and is therefore N!, where N is the number of values. + /// + public long Count { + get { + return myCount; + } + } + + /// + /// The type of Permutations set that is generated. + /// + public GenerateOption Type { + get { + return myMetaCollectionType; + } + } + + /// + /// The upper index of the meta-collection, equal to the number of items in the initial set. + /// + public int UpperIndex { + get { + return myValues.Count; + } + } + + /// + /// The lower index of the meta-collection, equal to the number of items returned each iteration. + /// For Permutation, this is always equal to the UpperIndex. + /// + public int LowerIndex { + get { + return myValues.Count; + } + } + + #endregion + + #region Heavy Lifting Members + + /// + /// Common intializer used by the multiple flavors of constructors. + /// + /// + /// Copies information provided and then creates a parellel int array of lexicographic + /// orders that will be used for the actual permutation algorithm. + /// The input array is first sorted as required for WithoutRepetition and always just for consistency. + /// This array is constructed one of two way depending on the type of the collection. + /// + /// When type is MetaCollectionType.WithRepetition, then all N! permutations are returned + /// and the lexicographic orders are simply generated as 1, 2, ... N. + /// E.g. + /// Input array: {A A B C D E E} + /// Lexicograhpic Orders: {1 2 3 4 5 6 7} + /// + /// When type is MetaCollectionType.WithoutRepetition, then fewer are generated, with each + /// identical element in the input array not repeated. The lexicographic sort algorithm + /// handles this natively as long as the repetition is repeated. + /// E.g. + /// Input array: {A A B C D E E} + /// Lexicograhpic Orders: {1 1 2 3 4 5 5} + /// + private void Initialize(IList values, GenerateOption type, IComparer comparer) { + myMetaCollectionType = type; + myValues = new List(values.Count); + myValues.AddRange(values); + myLexicographicOrders = new int[values.Count]; + if(type == GenerateOption.WithRepetition) { + for(int i = 0; i < myLexicographicOrders.Length; ++i) { + myLexicographicOrders[i] = i; + } + } + else { + if(comparer == null) { + comparer = new SelfComparer(); + } + myValues.Sort(comparer); + int j = 1; + if(myLexicographicOrders.Length > 0) { + myLexicographicOrders[0] = j; + } + for(int i = 1; i < myLexicographicOrders.Length; ++i) { + if(comparer.Compare(myValues[i - 1], myValues[i]) != 0) { + ++j; + } + myLexicographicOrders[i] = j; + } + } + myCount = GetCount(); + } + + /// + /// Calculates the total number of permutations that will be returned. + /// As this can grow very large, extra effort is taken to avoid overflowing the accumulator. + /// While the algorithm looks complex, it really is just collecting numerator and denominator terms + /// and cancelling out all of the denominator terms before taking the product of the numerator terms. + /// + /// The number of permutations. + private long GetCount() { + int runCount = 1; + List divisors = new List(); + List numerators = new List(); + for(int i = 1; i < myLexicographicOrders.Length; ++i) { + numerators.AddRange(SmallPrimeUtility.Factor(i + 1)); + if(myLexicographicOrders[i] == myLexicographicOrders[i - 1]) { + ++runCount; + } + else { + for(int f = 2; f <= runCount; ++f) { + divisors.AddRange(SmallPrimeUtility.Factor(f)); + } + runCount = 1; + } + } + for(int f = 2; f <= runCount; ++f) { + divisors.AddRange(SmallPrimeUtility.Factor(f)); + } + return SmallPrimeUtility.EvaluatePrimeFactors(SmallPrimeUtility.DividePrimeFactors(numerators, divisors)); + } + + #endregion + + #region Data and Internal Members + + /// + /// A list of T that represents the order of elements as originally provided, used for Reset. + /// + private List myValues; + + /// + /// Parrellel array of integers that represent the location of items in the myValues array. + /// This is generated at Initialization and is used as a performance speed up rather that + /// comparing T each time, much faster to let the CLR optimize around integers. + /// + private int[] myLexicographicOrders; + + /// + /// Inner class that wraps an IComparer around a type T when it is IComparable + /// + private class SelfComparer : IComparer { + public int Compare(U x, U y) { + return ((IComparable)x).CompareTo(y); + } + } + + /// + /// The count of all permutations. Calculated at Initialization and returned by Count property. + /// + private long myCount; + + /// + /// The type of Permutations that this was intialized from. + /// + private GenerateOption myMetaCollectionType; + + #endregion + + } +} diff --git a/src/Numerics/Combinatorics/SmallPrimeUtility.cs b/src/Numerics/Combinatorics/SmallPrimeUtility.cs new file mode 100755 index 000000000..baaa514f6 --- /dev/null +++ b/src/Numerics/Combinatorics/SmallPrimeUtility.cs @@ -0,0 +1,151 @@ +// Copyright 2008 Adrian Akison +// Distributed under license terms of CPOL http://www.codeproject.com/info/cpol10.aspx +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections; + +namespace MathNet.Numerics.Combinations +{ + /// + /// Utility class that maintains a small table of prime numbers and provides + /// simple implementations of Prime Factorization algorithms. + /// This is a quick and dirty utility class to support calculations of permutation + /// sets with indexes under 2^31. + /// The prime table contains all primes up to Sqrt(2^31) which are all of the primes + /// requires to factorize any Int32 positive integer. + /// + public class SmallPrimeUtility { + /// + /// Utility class, no instances allowed. + /// + private SmallPrimeUtility() { + ; + } + + /// + /// Performs a prime factorization of a given integer using the table of primes in PrimeTable. + /// Since this will only factor Int32 sized integers, a simple list of factors is returned instead + /// of the more scalable, but more difficult to consume, list of primes and associated exponents. + /// + /// The number to factorize, must be positive. + /// A simple list of factors. + public static List Factor(int i) { + int primeIndex = 0; + int prime = PrimeTable[primeIndex]; + List factors = new List(); + while(i > 1) { + if(i % prime == 0) { + factors.Add(prime); + i /= prime; + } + else { + ++primeIndex; + prime = PrimeTable[primeIndex]; + } + } + return factors; + } + + /// + /// Given two integers expressed as a list of prime factors, multiplies these numbers + /// together and returns an integer also expressed as a set of prime factors. + /// This allows multiplication to overflow well beyond a Int64 if necessary. + /// + /// Left Hand Side argument, expressed as list of prime factors. + /// Right Hand Side argument, expressed as list of prime factors. + /// Product, expressed as list of prime factors. + public static List MultiplyPrimeFactors(IList lhs, IList rhs) { + List product = new List(); + foreach(int prime in lhs) { + product.Add(prime); + } + foreach(int prime in rhs) { + product.Add(prime); + } + product.Sort(); + return product; + } + + /// + /// Given two integers expressed as a list of prime factors, divides these numbers + /// and returns an integer also expressed as a set of prime factors. + /// If the result is not a integer, then the result is undefined. That is, 11 / 5 + /// when divided by this function will not yield a correct result. + /// As such, this function is ONLY useful for division with combinatorial results where + /// the result is known to be an integer AND the division occurs as the last operation(s). + /// + /// Numerator argument, expressed as list of prime factors. + /// Denominator argument, expressed as list of prime factors. + /// Resultant, expressed as list of prime factors. + public static List DividePrimeFactors(IList numerator, IList denominator) { + List product = new List(); + foreach(int prime in numerator) { + product.Add(prime); + } + foreach(int prime in denominator) { + product.Remove(prime); + } + return product; + } + + /// + /// Given a list of prime factors returns the long representation. + /// + /// Integer, expressed as list of prime factors. + /// Standard long representation. + public static long EvaluatePrimeFactors(IList value) { + long accumulator = 1; + foreach(int prime in value) { + accumulator *= prime; + } + return accumulator; + } + + /// + /// Static initializer, set up prime table. + /// + static SmallPrimeUtility() { + CalculatePrimes(); + } + + /// + /// Calculate all primes up to Sqrt(2^32) = 2^16. + /// This table will be large enough for all factorizations for Int32's. + /// Small tables are best built using the Sieve Of Eratosthenes, + /// Reference: http://primes.utm.edu/glossary/page.php?sort=SieveOfEratosthenes + /// + private static void CalculatePrimes() { + // Build Sieve Of Eratosthenes + BitArray sieve = new BitArray(65536, true); + for(int possiblePrime = 2; possiblePrime <= 256; ++possiblePrime) { + if(sieve[possiblePrime] == true) { + // It is prime, so remove all future factors... + for(int nonPrime = 2 * possiblePrime; nonPrime < 65536; nonPrime += possiblePrime) { + sieve[nonPrime] = false; + } + } + } + // Scan sieve for primes... + myPrimes = new List(); + for(int i = 2; i < 65536; ++i) { + if(sieve[i] == true) { + myPrimes.Add(i); + } + } + + } + + /// + /// A List of all primes from 2 to 2^16. + /// + public static IList PrimeTable { + get { + return myPrimes; + } + } + + private static List myPrimes = new List(); + + } +} diff --git a/src/Numerics/Combinatorics/Variations.cs b/src/Numerics/Combinatorics/Variations.cs new file mode 100755 index 000000000..2e3140952 --- /dev/null +++ b/src/Numerics/Combinatorics/Variations.cs @@ -0,0 +1,454 @@ +// Copyright 2008 Adrian Akison +// Distributed under license terms of CPOL http://www.codeproject.com/info/cpol10.aspx +using System; +using System.Collections.Generic; +using System.Text; + +namespace MathNet.Numerics.Combinations +{ + /// + /// Variations defines a meta-collection, typically a list of lists, of all possible + /// ordered subsets of a particular size from the set of values. + /// This list is enumerable and allows the scanning of all possible Variations using a simple + /// foreach() loop even though the variations are not all in memory. + /// + /// + /// The MetaCollectionType parameter of the constructor allows for the creation of + /// normal Variations and Variations with Repetition. + /// + /// When given an input collect {A B C} and lower index of 2, the following sets are generated: + /// MetaCollectionType.WithoutRepetition generates 6 sets: => + /// {A B}, {A B}, {B A}, {B C}, {C A}, {C B} + /// MetaCollectionType.WithRepetition generates 9 sets: + /// {A A}, {A B}, {A B}, {B A}, {B B }, {B C}, {C A}, {C B}, {C C} + /// + /// The equality of multiple inputs is not considered when generating variations. + /// + /// The type of the values within the list. + public class Variations : IMetaCollection { + #region Constructors + + /// + /// No default constructor, must provided a list of values and size. + /// + protected Variations() { + ; + } + + /// + /// Create a variation set from the indicated list of values. + /// The upper index is calculated as values.Count, the lower index is specified. + /// Collection type defaults to MetaCollectionType.WithoutRepetition + /// + /// List of values to select Variations from. + /// The size of each variation set to return. + public Variations(IList values, int lowerIndex) { + Initialize(values, lowerIndex, GenerateOption.WithoutRepetition); + } + + /// + /// Create a variation set from the indicated list of values. + /// The upper index is calculated as values.Count, the lower index is specified. + /// + /// List of values to select variations from. + /// The size of each vatiation set to return. + /// Type indicates whether to use repetition in set generation. + public Variations(IList values, int lowerIndex, GenerateOption type) { + Initialize(values, lowerIndex, type); + } + + #endregion + + #region IEnumerable Interface + + /// + /// Gets an enumerator for the collection of Variations. + /// + /// The enumerator. + public IEnumerator> GetEnumerator() { + if(Type == GenerateOption.WithRepetition) { + return new EnumeratorWithRepetition(this); + } + else { + return new EnumeratorWithoutRepetition(this); + } + } + + /// + /// Gets an enumerator for the collection of Variations. + /// + /// The enumerator. + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + if(Type == GenerateOption.WithRepetition) { + return new EnumeratorWithRepetition(this); + } + else { + return new EnumeratorWithoutRepetition(this); + } + } + + #endregion + + #region Enumerator Inner Class + + /// + /// An enumerator for Variations when the type is set to WithRepetition. + /// + public class EnumeratorWithRepetition : IEnumerator> { + + #region Constructors + + /// + /// Construct a enumerator with the parent object. + /// + /// The source Variations object. + public EnumeratorWithRepetition(Variations source) { + myParent = source; + Reset(); + } + + #endregion + + #region IEnumerator interface + + /// + /// Resets the Variations enumerator to the first variation. + /// + public void Reset() { + myCurrentList = null; + myListIndexes = null; + } + + /// + /// Advances to the next variation. + /// + /// True if successfully moved to next variation, False if no more variations exist. + /// + /// Increments the internal myListIndexes collection by incrementing the last index + /// and overflow/carrying into others just like grade-school arithemtic. If the + /// finaly carry flag is set, then we would wrap around and are therefore done. + /// + public bool MoveNext() { + int carry = 1; + if(myListIndexes == null) { + myListIndexes = new List(); + for(int i = 0; i < myParent.LowerIndex; ++i) { + myListIndexes.Add(0); + } + carry = 0; + } + else { + for(int i = myListIndexes.Count - 1; i >= 0 && carry > 0; --i) { + myListIndexes[i] += carry; + carry = 0; + if(myListIndexes[i] >= myParent.UpperIndex) { + myListIndexes[i] = 0; + carry = 1; + } + } + } + myCurrentList = null; + return carry != 1; + } + + /// + /// The current variation + /// + public IList Current { + get { + ComputeCurrent(); + return myCurrentList; + } + } + + /// + /// The current variation. + /// + object System.Collections.IEnumerator.Current { + get { + ComputeCurrent(); + return myCurrentList; + } + } + + /// + /// Cleans up non-managed resources, of which there are none used here. + /// + public void Dispose() { + ; + } + + #endregion + + #region Heavy Lifting Members + + /// + /// Computes the current list based on the internal list index. + /// + private void ComputeCurrent() { + if(myCurrentList == null) { + myCurrentList = new List(); + foreach(int index in myListIndexes) { + myCurrentList.Add(myParent.myValues[index]); + } + } + } + + #endregion + + #region Data + + /// + /// Parent object this is an enumerator for. + /// + private Variations myParent; + + /// + /// The current list of values, this is lazy evaluated by the Current property. + /// + private List myCurrentList; + + /// + /// An enumertor of the parents list of lexicographic orderings. + /// + private List myListIndexes; + + #endregion + } + + /// + /// An enumerator for Variations when the type is set to WithoutRepetition. + /// + public class EnumeratorWithoutRepetition : IEnumerator> { + + #region Constructors + + /// + /// Construct a enumerator with the parent object. + /// + /// The source Variations object. + public EnumeratorWithoutRepetition(Variations source) { + myParent = source; + myPermutationsEnumerator = (Permutations.Enumerator)myParent.myPermutations.GetEnumerator(); + } + + #endregion + + #region IEnumerator interface + + /// + /// Resets the Variations enumerator to the first variation. + /// + public void Reset() { + myPermutationsEnumerator.Reset(); + } + + /// + /// Advances to the next variation. + /// + /// True if successfully moved to next variation, False if no more variations exist. + public bool MoveNext() { + bool ret = myPermutationsEnumerator.MoveNext(); + myCurrentList = null; + return ret; + } + + /// + /// The current variation. + /// + public IList Current { + get { + ComputeCurrent(); + return myCurrentList; + } + } + + /// + /// The current variation. + /// + object System.Collections.IEnumerator.Current { + get { + ComputeCurrent(); + return myCurrentList; + } + } + + /// + /// Cleans up non-managed resources, of which there are none used here. + /// + public void Dispose() { + ; + } + + #endregion + + #region Heavy Lifting Members + + /// + /// Creates a list of original values from the int permutation provided. + /// The exception for accessing current (InvalidOperationException) is generated + /// by the call to .Current on the underlying enumeration. + /// + /// + /// To compute the current list of values, the element to use is determined by + /// a permutation position with a non-MaxValue value. It is placed at the position in the + /// output that the index value indicates. + /// + /// E.g. Variations of 6 choose 3 without repetition + /// Input array: {A B C D E F} + /// Permutations: {- 1 - - 3 2} (- is Int32.MaxValue) + /// Generates set: {B F E} + /// + private void ComputeCurrent() { + if(myCurrentList == null) { + myCurrentList = new List(); + int index = 0; + IList currentPermutation = (IList)myPermutationsEnumerator.Current; + for(int i = 0; i < myParent.LowerIndex; ++i) { + myCurrentList.Add(myParent.myValues[0]); + } + for(int i = 0; i < currentPermutation.Count; ++i) { + int position = currentPermutation[i]; + if(position != Int32.MaxValue) { + myCurrentList[position] = myParent.myValues[index]; + if(myParent.Type == GenerateOption.WithoutRepetition) { + ++index; + } + } + else { + ++index; + } + } + } + } + + #endregion + + #region Data + + /// + /// Parent object this is an enumerator for. + /// + private Variations myParent; + + /// + /// The current list of values, this is lazy evaluated by the Current property. + /// + private List myCurrentList; + + /// + /// An enumertor of the parents list of lexicographic orderings. + /// + private Permutations.Enumerator myPermutationsEnumerator; + + #endregion + } + #endregion + + #region IMetaList Interface + + /// + /// The number of unique variations that are defined in this meta-collection. + /// + /// + /// Variations with repetitions does not behave like other meta-collections and it's + /// count is equal to N^P, where N is the upper index and P is the lower index. + /// + public long Count { + get { + if(Type == GenerateOption.WithoutRepetition) { + return myPermutations.Count; + } + else { + return (long)Math.Pow(UpperIndex, LowerIndex); + } + } + } + + /// + /// The type of Variations set that is generated. + /// + public GenerateOption Type { + get { + return myMetaCollectionType; + } + } + + /// + /// The upper index of the meta-collection, equal to the number of items in the initial set. + /// + public int UpperIndex { + get { + return myValues.Count; + } + } + + /// + /// The lower index of the meta-collection, equal to the number of items returned each iteration. + /// + public int LowerIndex { + get { + return myLowerIndex; + } + } + + #endregion + + #region Heavy Lifting Members + + /// + /// Initialize the variations for constructors. + /// + /// List of values to select variations from. + /// The size of each variation set to return. + /// The type of variations set to generate. + private void Initialize(IList values, int lowerIndex, GenerateOption type) { + myMetaCollectionType = type; + myLowerIndex = lowerIndex; + myValues = new List(); + myValues.AddRange(values); + if(type == GenerateOption.WithoutRepetition) { + List myMap = new List(); + int index = 0; + for(int i = 0; i < myValues.Count; ++i) { + if(i >= myValues.Count - myLowerIndex) { + myMap.Add(index++); + } + else { + myMap.Add(Int32.MaxValue); + } + } + myPermutations = new Permutations(myMap); + } + else { + ; // myPermutations isn't used. + } + } + + #endregion + + #region Data + + /// + /// Copy of values object is intialized with, required for enumerator reset. + /// + private List myValues; + + /// + /// Permutations object that handles permutations on int for variation inclusion and ordering. + /// + private Permutations myPermutations; + + /// + /// The type of the variation collection. + /// + private GenerateOption myMetaCollectionType; + + /// + /// The lower index defined in the constructor. + /// + private int myLowerIndex; + + #endregion + } +} diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index 620bb6f0b..4cc973c96 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -32,7 +32,10 @@ Linear Algebra: optimized range checking in vectors and matricestrue 1701;1702;1705;1591;1573 true + net40 + +