Генерируйте последовательности числа с LINQ

Я пытаюсь записать оператор LINQ, который возвращает меня все возможные комбинации чисел (мне нужно это для теста, и я был вдохновлен этой статьей Eric Lippert). Прототип метода, который я называю, похож:

IEnumerable> AllSequences( int start, int end, int size );

Правила:

  • все возвращенные наборы имеют длину size
  • числовые значения в наборе должны увеличиться
  • каждое число между start и end должен использоваться

Так вызов AllSequences( 1, 5, 3 ) должен привести к 10 наборам, каждому размеру 3:

1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4 
2 3 5
2 4 5 
3 4 5

Теперь, так или иначе я действительно хотел бы видеть чистое решение LINQ. Я могу записать не решение LINQ самостоятельно, поэтому не приложите усилия к решению без LINQ.
Мои попытки до сих пор закончились в точке, где я должен присоединиться к числу с результатом рекурсивного вызова моего метода - что-то как:

return from i in Enumerable.Range( start, end - size + 1 )
       select BuildCollection(i, AllSequences( i, end, size -1));

Но я не могу управлять им для реализации BuildCollection() на основе LINQ - или даже пропускают этот вызов метода. Можно ли помочь мне здесь?

38
задан tanascius 29 April 2010 в 12:04
поделиться

2 ответа

Думаю, я понял.

IEnumerable<List<int>> AllSequences(int start, int end, int size)
{
    if (size == 0)
        return Enumerable.Repeat<List<int>>(new List<int>(), 1);

    return from i in Enumerable.Range(start, end - size - start + 2)
           from seq in AllSequences(i + 1, end, size - 1)
           select new List<int>{i}.Concat(seq).ToList();
}
35
ответ дан 27 November 2019 в 03:14
поделиться

Думаю, что-то вроде следующего должно сработать.

public static IEnumerable<IEnumerable<int>> AllSequences(int start, int end,
    int size)
{
    return size <= 0 ? new[] { new int[0] } :
           from i in Enumerable.Range(start, end - size - start + 2)
           from seq in AllSequences(i + 1, end, size - 1)
           select Enumerable.Concat(new[] { i }, seq);
}

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

Обратите внимание, что я немного изменил сигнатуру метода на IEnumerable > , поскольку это более удобно при использовании (чистого) LINQ. Однако вы всегда можете легко преобразовать его в IEnumerable > в конце, если хотите.

Сообщите мне, нуждается ли код в каких-либо пояснениях, но я надеюсь, что синтаксис LINQ проясняет его.

Редактировать 1: Исправлена ​​ошибка и улучшена лаконичность.

Правка 2: Поскольку мне скучно и мне больше нечего делать (нет, не совсем), я подумал, что напишу метод расширения, который вычисляет комбинации заданного списка элементы, используя метод AllSequences .

public static IEnumerable<IEnumerable<T>> Combinations<T>(this IList<T> source,
    int num)
{
    return AllSequences(0, source.Count - 1, num).Select(
        seq => seq.Select(i => source[i]));
}

Возможно, не самый эффективный способ вычисления комбинаций, но, безусловно, довольно компактный код!

13
ответ дан 27 November 2019 в 03:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: