#region License Information /* HeuristicLab * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.Linq; namespace HeuristicLab.Common { public static class EnumerableExtensions { /// /// Selects all elements in the sequence that are maximal with respect to the given value. /// /// /// Runtime complexity of the operation is O(N). /// /// The type of the elements. /// The enumeration in which the items with a maximal value should be found. /// The function that selects the value to compare. /// All elements in the enumeration where the selected value is the maximum. public static IEnumerable MaxItems(this IEnumerable source, Func valueSelector) { IEnumerator enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) return Enumerable.Empty(); IComparable max = valueSelector(enumerator.Current); var result = new List(); result.Add(enumerator.Current); while (enumerator.MoveNext()) { T item = enumerator.Current; IComparable comparison = valueSelector(item); if (comparison.CompareTo(max) > 0) { result.Clear(); result.Add(item); max = comparison; } else if (comparison.CompareTo(max) == 0) { result.Add(item); } } return result; } /// /// Selects all elements in the sequence that are minimal with respect to the given value. /// /// /// Runtime complexity of the operation is O(N). /// /// The type of the elements. /// The enumeration in which items with a minimal value should be found. /// The function that selects the value. /// All elements in the enumeration where the selected value is the minimum. public static IEnumerable MinItems(this IEnumerable source, Func valueSelector) { IEnumerator enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) return Enumerable.Empty(); IComparable min = valueSelector(enumerator.Current); var result = new List(); result.Add(enumerator.Current); while (enumerator.MoveNext()) { T item = enumerator.Current; IComparable comparison = valueSelector(item); if (comparison.CompareTo(min) < 0) { result.Clear(); result.Add(item); min = comparison; } else if (comparison.CompareTo(min) == 0) { result.Add(item); } } return result; } /// /// Compute the n-ary cartesian product of arbitrarily many sequences: http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx /// /// The type of the elements inside each sequence /// The collection of sequences /// An enumerable sequence of all the possible combinations of elements public static IEnumerable> CartesianProduct(this IEnumerable> sequences) { IEnumerable> result = new[] { Enumerable.Empty() }; return sequences.Where(s => s.Any()).Aggregate(result, (current, s) => (from seq in current from item in s select seq.Concat(new[] { item }))); } /// /// Compute all k-combinations of elements from the provided collection. /// The collection of elements /// The combination group size /// An enumerable sequence of all the possible k-combinations of elements /// public static IEnumerable> Combinations(this IList elements, int k) { if (k > elements.Count) throw new ArgumentException(); if (k == 1) { foreach (var element in elements) yield return new[] { element }; yield break; } int n = elements.Count; var range = Enumerable.Range(0, k).ToArray(); var length = BinomialCoefficient(n, k); for (int i = 0; i < length; ++i) { yield return range.Select(x => elements[x]); if (i == length - 1) break; var m = k - 1; var max = n - 1; while (range[m] == max) { --m; --max; } range[m]++; for (int j = m + 1; j < k; ++j) { range[j] = range[j - 1] + 1; } } } /// /// This function gets the total number of unique combinations based upon N and K, /// where N is the total number of items and K is the size of the group. /// It calculates the total number of unique combinations C(N, K) = N! / ( K! (N - K)! ) /// using the recursion C(N+1, K+1) = (N+1 / K+1) * C(N, K). /// http://blog.plover.com/math/choose.html /// https://en.wikipedia.org/wiki/Binomial_coefficient#Multiplicative_formula /// The number of elements /// The size of the group /// The binomial coefficient C(N, K) /// public static long BinomialCoefficient(long n, long k) { if (k > n) return 0; if (k == n) return 1; if (k > n - k) k = n - k; long r = 1; for (long d = 1; d <= k; d++) { r *= n--; r /= d; } return r; } } }