C#: Генерация уникальных перестановок длины N из элементов массива

Сегодня я решил рассказать о решении задачи генерации перестановок различной длины из заданного массива.
Сама задача выглядит следующим образом: Дан массив чисел. Необходимо получить все уникальные комбинации данных чисел длины N. (комбинации [1, 2, 3], [2, 1, 3], [3, 2, 1] … — считается одной и той же). Так же необходимо предусмотреть возможность исключать из исходного массива определенные значения (или индексы). С первого взгляда данная задача кажется простой. Однако на практике при реализации данной задачи многие сталкиваются с трудностями.
Для такого чтобы решить данную задачу нам необходимо использовать рекурсию. Общая логика решения задачи такая: проходим по исходному массиву слева направо. Если текущий индекс (или значение) попадает под фильтр — переходим к следующему элементу, в противном случае добавляем значение в результат «A». Как только в результате «A» становиться число элементов равное N нам необходимо сохранить полученный массив в массив «B», и откатиться в массиве «A» на шаг до добавления последнего элемента (по сути удалить последнее добавление, которое привело к числу элементов N).
Реализация:

//array - исходный массив
//skip - индексы, которые нужно пропустить
//current - служебный параметр (индекс с которого начинаем работу в массиве array - передаем значение 0 при первом вызове)
//max - число элементов в перестановке
//result - переменная для сохранения результата
private void Сombinations(int[] array, List skip, int current, int max, List> result)
        {
            if (result.Count == 0) //частный случай
            {
                result.Add(new List());
            }
            if (max == 0) //частный случай
            {
                return;
            }
            var currentLevelResult = new List(result.Last()); //сохраняем текущее состояние (до добавления нового элемента)
            for (int i = current; i < array.Length; ++i)
            {
                if (skip.Contains(i)) //если пропускаем значения skip.Contains(array[i])
                {
                    continue;
                }
                result.Last().Add(array[i]);
                if (result.Last().Count != max)
                {
                    Сombinations(array, skip, i + 1, max, result);
                }
                result.Add(new List(currentLevelResult));
            }
            if (result.Last().Count != max) //Если исходный массив закончился и в результате недостаточно элементов - удаляем такой результат
            {
                result.Remove(result.Last());
            }
        }

Пример использования:

List> result = new List>();
Сombinations(new int[] {1, 2, 3}, new List(), 0, 2, result);
/*
result
[0] = [1,2]
[1] = [1,3]
[2] = [2,3]
*/
Запись опубликована в рубрике C# с метками , , , , , , , . Добавьте в закладки постоянную ссылку.