Сочетания слов без повтора

У меня есть 10 слов. Как получить все возможные комбинации из 5 слов (n=10, k=5). Порядок не имеет значения.

Например: "A", "B", "C", if k=2 (n=3 in this case), это должно быть AB, BC и AC. Может быть, вы знаете какой-нибудь полезный код или пример.

P.S. Извините, если я недостаточно прав, потому что я не очень хорошо знаю английский.


person Frankie Drake    schedule 27.02.2011    source источник


Ответы (4)


Вы пытаетесь получить все перестановки коллекции.

Вот фрагмент кода:

static void Main(string[] args)
{
    var list = new List<string> { "a", "b", "c", "d", "e" };
    var result = GetPermutations(list, 3);
    foreach (var perm in result)
    {
        foreach (var c in perm)
        {
            Console.Write(c + " ");
        }
        Console.WriteLine();
    }
    Console.ReadKey();
}

static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> items, int count)
{
    int i = 0;
    foreach (var item in items)
    {
        if (count == 1)
            yield return new T[] { item };
        else
        {
            foreach (var result in GetPermutations(items.Skip(i + 1), count - 1))
                yield return new T[] { item }.Concat(result);
        }

        ++i;
    }
}

Выходы:

a b c 
a b d 
a b e 
a c d 
a c e 
a d e 
b c d 
b c e 
b d e 
c d e 
person jrbeverly    schedule 26.07.2013

Вот что я собрал:

static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> CombinationsWithoutRepetition<T>(
        this IEnumerable<T> items,
        int ofLength)
    {
        return (ofLength == 1) ?
            items.Select(item => new[] { item }) :
            items.SelectMany((item, i) => items.Skip(i + 1)
                                               .CombinationsWithoutRepetition(ofLength - 1)
                                               .Select(result => new T[] { item }.Concat(result)));
    }

    public static IEnumerable<IEnumerable<T>> CombinationsWithoutRepetition<T>(
        this IEnumerable<T> items,
        int ofLength,
        int upToLength)
    {
        return Enumerable.Range(ofLength, Math.Max(0, upToLength - ofLength + 1))
                         .SelectMany(len => items.CombinationsWithoutRepetition(ofLength: len));
    }

}

...

foreach (var c in new[] {"a","b","c","d"}.CombinationsWithoutRepetition(ofLength: 2, upToLength: 4))
{
    Console.WriteLine(string.Join(',', c));
}

производит:

a,b
a,c
a,d
b,c
b,d
c,d
a,b,c
a,b,d
a,c,d
b,c,d
a,b,c,d

Обратите внимание, что это кратко, но неэффективно и не должно использоваться для больших наборов или внутренних циклов. Примечательно, что короткие массивы повторно создаются несколько раз и могут быть запомнены, а IEnumerable будет повторяться несколько раз, что может вызвать неожиданную работу, если не будут приняты меры.

Кроме того, если входные данные содержат дубликаты, то и выходные данные также будут. Либо сначала используйте .Distinct().ToArray(), либо другое решение, которое включает проверку равенства и, предположительно, принимает IEqualityComparer для общности.

person Tim Sylvester    schedule 25.06.2018

public IActionResult Index() 
{
    var list = new List<string> { "a", "b", "c", "d", "e" };
    List<string> ret = GetAllCombinations(list).OrderBy(_ => _).ToList();

    return View();
}

static IEnumerable<string> GetAllCombinations(IEnumerable<string> list)
{
    return list.SelectMany((mainItem, mi) => list.Where((otherItem, oi) => mi < oi)
                              .Select(otherItem => mainItem + otherItem));
}

Выход:

ab
ac
ad
ae
bc
bd
be
cd
ce
de
person 小小皮    schedule 03.11.2020
comment
Добро пожаловать в Stack Overflow. Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и / или как этот код отвечает на вопрос, улучшает его долгосрочную ценность. Как ответить. С уважением. - person Elletlar; 03.11.2020

А как насчет более функционального решения

var list = new List<string> { "a", "b", "c", "d", "e" };
GetAllCombinations(list).OrderBy(_ => _).ToList().ForEach(Console.WriteLine);


static IEnumerable<string> GetAllCombinations(IEnumerable<string> list)
{
    return list.SelectMany(mainItem => list.Where(otherItem => !otherItem.Equals(mainItem))
                              .Select(otherItem => mainItem + otherItem));
}

Выход:

ab
ac
ad
ae
ba
bc
bd
be
ca
cb
cd
ce
da
db
dc
de
ea
eb
ec
ed
person Jhonattan    schedule 16.02.2017
comment
OP указал, что порядок не имеет значения, поэтому ab и ba, которые появляются в результате этого кода, одинаковы. - person Andrew Morton; 03.09.2020