2015-10-26 2 views
6

Я пытаюсь написать алгоритм для выбора всех комбинаций n значений из набора чисел.Как использовать LINQ, чтобы найти все комбинации n элементов из набора чисел?

К примеру, учитывая набор: 1, 2, 3, 7, 8, 9

Все комбинации 2 значений из множества:

(1, 2), (1, 3), (1, 7), (1, 8), (1, 9), (2, 3), (2, 7), (2, 8), (2, 9), (3, 7), (3, 8), (3 , 9), (7, 8), (7, 9), (8, 9)

И 3:

(1, 2, 3), (1, 2, 7), (1, 2, 8), (1, 2, 9), (1, 3, 7), (1, 3, 8), (1, 3, 9), (1, 7, 8), (1, 7, 9), (1, 8, 9), (2, 3, 7), (2, 3, 8), (2, 3, 9), (2, 7, 8), (2, 7, 9), (2, 8, 9), (3, 7, 8), (3, 7, 9), (3, 8, 9), (7, 8, 9)

etc!

В настоящее время я использую методы для получения наборов возвратов комбинаций значений 2, 3 и 4, но мне кажется, что это может быть обобщено в запросе LINQ.

Благодарим за помощь!

+1

Вы смотрели на: [это] (http://stackoverflow.com/a/774628/1698987) или [это] (http://stackoverflow.com/a/4326669/1698987) ответы? – Noctis

ответ

15

Использование:

var results = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }.DifferentCombinations(3); 

Код:

public static class Ex 
{ 
    public static IEnumerable<IEnumerable<T>> DifferentCombinations<T>(this IEnumerable<T> elements, int k) 
    { 
     return k == 0 ? new[] { new T[0] } : 
      elements.SelectMany((e, i) => 
      elements.Skip(i + 1).DifferentCombinations(k - 1).Select(c => (new[] {e}).Concat(c))); 
    } 
} 
+0

@Dink Я провел несколько тестов, и это всегда превзошло решение, которое я предоставил. Это также более красноречиво. Я бы пошел с этим. –

+2

Спасибо user109260 и @ Jared - это здорово, это действительно упрощает вещи и намного более гибко. – Dink

0

Хотя выше ответ очень аккуратно, я придумал решение, которое может быть гораздо быстрее, в зависимости от размера коллекции.

static class Combinations 
{ 
    private static void InitIndexes(int[] indexes) 
    { 
     for (int i = 0; i < indexes.Length; i++) 
     { 
      indexes[i] = i; 
     } 
    } 

    private static void SetIndexes(int[] indexes, int lastIndex, int count) 
    { 
     indexes[lastIndex]++; 
     if (lastIndex > 0 && indexes[lastIndex] == count) 
     { 
      SetIndexes(indexes, lastIndex - 1, count - 1); 
      indexes[lastIndex] = indexes[lastIndex - 1] + 1; 
     } 
    } 

    private static List<T> TakeAt<T>(int[] indexes, IEnumerable<T> list) 
    { 
     List<T> selected = new List<T>(); 
     for (int i = 0; i < indexes.Length; i++) 
     { 
      selected.Add(list.ElementAt(indexes[i])); 
     } 
     return selected; 
    } 

    private static bool AllPlacesChecked(int[] indexes, int places) 
    { 
     for (int i = indexes.Length - 1; i >= 0; i--) 
     { 
      if (indexes[i] != places) 
       return false; 
      places--; 
     } 
     return true; 
    } 

    public static IEnumerable<List<T>> GetDifferentCombinations<T>(this IEnumerable<T> collection, int count) 
    { 
     int[] indexes = new int[count]; 
     int listCount = collection.Count(); 
     if (count > listCount) 
      throw new InvalidOperationException($"{nameof(count)} is greater than the collection elements."); 
     InitIndexes(indexes); 
     do 
     { 
      var selected = TakeAt(indexes, collection); 
      yield return selected; 
      SetIndexes(indexes, indexes.Length - 1, listCount); 
     } 
     while (!AllPlacesChecked(indexes, listCount)); 

    } 
} 
Смежные вопросы