web-dev-qa-db-fra.com

Toutes les combinaisons possibles d'une liste de valeurs

J'ai une liste d'entiers dans mon programme C #. Cependant, je ne connais le nombre d'éléments que j'ai dans ma liste qu'au moment de l'exécution.

Supposons, par souci de simplicité, que ma liste est {1, 2, 3} Maintenant, je dois générer toutes les combinaisons possibles comme suit: 2 ►

Quelqu'un peut-il aider s'il vous plaît avec cela?

30
Sach

essaye ça:

static void Main(string[] args)
{

    GetCombination(new List<int> { 1, 2, 3 });
}

static void GetCombination(List<int> list)
{
    double count = Math.Pow(2, list.Count);
    for (int i = 1; i <= count - 1; i++)
    {
        string str = Convert.ToString(i, 2).PadLeft(list.Count, '0');
        for (int j = 0; j < str.Length; j++)
        {
            if (str[j] == '1')
            {
                Console.Write(list[j]);
            }
        }
        Console.WriteLine();
    }
}
62
ojlovecd

Voici deux solutions generic pour les listes fortement typées qui renverront toutes les combinaisons uniques de membres de liste (si vous pouvez résoudre ce problème avec un code plus simple, je vous salue):

// Recursive
public static List<List<T>> GetAllCombos<T>(List<T> list)
{
  List<List<T>> result = new List<List<T>>();
  // head
  result.Add(new List<T>());
  result.Last().Add(list[0]);
  if (list.Count == 1)
    return result;
  // tail
  List<List<T>> tailCombos = GetAllCombos(list.Skip(1).ToList());
  tailCombos.ForEach(combo =>
  {
    result.Add(new List<T>(combo));
    combo.Add(list[0]);
    result.Add(new List<T>(combo));
  });
  return result;
}

// Iterative, using 'i' as bitmask to choose each combo members
public static List<List<T>> GetAllCombos<T>(List<T> list)
{
  int comboCount = (int) Math.Pow(2, list.Count) - 1;
  List<List<T>> result = new List<List<T>>();
  for (int i = 1; i < comboCount + 1; i++)
  {
    // make each combo here
    result.Add(new List<T>());
    for (int j = 0; j < list.Count; j++)
    {
      if ((i >> j) % 2 != 0)
        result.Last().Add(list[j]);
    }
  }
  return result;
}

// Example usage
List<List<int>> combos = GetAllCombos(new int[] { 1, 2, 3 }.ToList());
11
jaolho

Voici une solution générique utilisant la récursivité

public static ICollection<ICollection<T>> Permutations<T>(ICollection<T> list) {
    var result = new List<ICollection<T>>();
    if (list.Count == 1) { // If only one possible permutation
        result.Add(list); // Add it and return it
        return result;
    }
    foreach (var element in list) { // For each element in that list
        var remainingList = new List<T>(list);
        remainingList.Remove(element); // Get a list containing everything except of chosen element
        foreach (var permutation in Permutations<T>(remainingList)) { // Get all possible sub-permutations
            permutation.Add(element); // Add that element
            result.Add(permutation);
        }
    }
    return result;
}

Je sais que c'est un ancien post, mais quelqu'un pourrait trouver cela utile.

10
Sindorej

Cette réponse utilise le même algorithme que ojlovecd et (pour sa solution itérative) jaolho. La seule chose que j'ajoute est une option permettant de filtrer les résultats pour un nombre minimal d'éléments dans les combinaisons. Cela peut être utile, par exemple, si vous ne vous intéressez qu'aux combinaisons contenant au moins deux éléments.

Edit: Comme demandé par @ user3610374, un filtre pour le nombre maximal d'éléments a été ajouté.

Edit 2: Comme suggéré par @stannius, l'algorithme a été modifié pour le rendre plus efficace dans les cas où toutes les combinaisons ne sont pas souhaitées.

  /// <summary>
  /// Method to create lists containing possible combinations of an input list of items. This is 
  /// basically copied from code by user "jaolho" on this thread:
  /// http://stackoverflow.com/questions/7802822/all-possible-combinations-of-a-list-of-values
  /// </summary>
  /// <typeparam name="T">type of the items on the input list</typeparam>
  /// <param name="inputList">list of items</param>
  /// <param name="minimumItems">minimum number of items wanted in the generated combinations, 
  ///                            if zero the empty combination is included,
  ///                            default is one</param>
  /// <param name="maximumItems">maximum number of items wanted in the generated combinations,
  ///                            default is no maximum limit</param>
  /// <returns>list of lists for possible combinations of the input items</returns>
  public static List<List<T>> ItemCombinations<T>(List<T> inputList, int minimumItems = 1, 
                                                  int maximumItems = int.MaxValue)
  {
     int nonEmptyCombinations = (int)Math.Pow(2, inputList.Count) - 1;
     List<List<T>> listOfLists = new List<List<T>>(nonEmptyCombinations + 1);

     // Optimize generation of empty combination, if empty combination is wanted
     if (minimumItems == 0)
        listOfLists.Add(new List<T>());

     if (minimumItems <= 1 && maximumItems >= inputList.Count)
     {
        // Simple case, generate all possible non-empty combinations
        for (int bitPattern = 1; bitPattern <= nonEmptyCombinations; bitPattern++)
           listOfLists.Add(GenerateCombination(inputList, bitPattern));
     }
     else
     {
        // Not-so-simple case, avoid generating the unwanted combinations
        for (int bitPattern = 1; bitPattern <= nonEmptyCombinations; bitPattern++)
        {
           int bitCount = CountBits(bitPattern);
           if (bitCount >= minimumItems && bitCount <= maximumItems)
              listOfLists.Add(GenerateCombination(inputList, bitPattern));
        }
     }

     return listOfLists;
  }

  /// <summary>
  /// Sub-method of ItemCombinations() method to generate a combination based on a bit pattern.
  /// </summary>
  private static List<T> GenerateCombination<T>(List<T> inputList, int bitPattern)
  {
     List<T> thisCombination = new List<T>(inputList.Count);
     for (int j = 0; j < inputList.Count; j++)
     {
        if ((bitPattern >> j & 1) == 1)
           thisCombination.Add(inputList[j]);
     }
     return thisCombination;
  }

  /// <summary>
  /// Sub-method of ItemCombinations() method to count the bits in a bit pattern. Based on this:
  /// https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
  /// </summary>
  private static int CountBits(int bitPattern)
  {
     int numberBits = 0;
     while (bitPattern != 0)
     {
        numberBits++;
        bitPattern &= bitPattern - 1;
     }
     return numberBits;
  }
7
RenniePet

Une autre solution utilisant Linq et la récursion ...

static void Main(string[] args)
    {
        List<List<long>> result = new List<List<long>>();

        List<long> set = new List<long>() { 1, 2, 3, 4 };

        GetCombination<long>(set, result);

        result.Add(set);

        IOrderedEnumerable<List<long>> sorted = result.OrderByDescending(s => s.Count);

        sorted.ToList().ForEach(l => { l.ForEach(l1 => Console.Write(l1 + " ")); Console.WriteLine(); });
    }

    private static void GetCombination<T>(List<T> set, List<List<T>> result)
    {
        for (int i = 0; i < set.Count; i++)
        {
            List<T> temp = new List<T>(set.Where((s, index) => index != i));

            if (temp.Count > 0 && !result.Where(l => l.Count == temp.Count).Any(l => l.SequenceEqual(temp)))
            {
                result.Add(temp);

                GetCombination<T>(temp, result);
            }
        }
    }
3
Roko Prč

Premièrement, pour un ensemble de n éléments, vous calculez toutes les combinaisons de k éléments (nCk). Vous devez modifier la valeur de k de 1 à n pour répondre à vos besoins.

Voir cet article codeproject pour le code C # permettant de générer des combinaisons.

Si vous souhaitez développer vous-même l'algorithme de combinaison, cochez cette question SO contenant de nombreux liens vers le matériel pertinent.

2
VinayC
public class CombinationGenerator{
    private readonly Dictionary<int, int> currentIndexesWithLevels = new Dictionary<int, int>();
    private readonly LinkedList<List<int>> _combinationsList = new LinkedList<List<int>>();
    private readonly int _combinationLength;

    public CombinationGenerator(int combinationLength)
    {
        _combinationLength = combinationLength;
    }

    private void InitializeLevelIndexes(List<int> list)
    {
        for (int i = 0; i < _combinationLength; i++)
        {
            currentIndexesWithLevels.Add(i+1, i);
        }
    }

    private void UpdateCurrentIndexesForLevels(int level)
    {
        int index;
        if (level == 1)
        {
            index = currentIndexesWithLevels[level];
            for (int i = level; i < _combinationLength + 1; i++)
            {
                index = index + 1;
                currentIndexesWithLevels[i] = index;
            }
        }
        else
        {
            int previousLevelIndex;
            for (int i = level; i < _combinationLength + 1; i++)
            {
                if (i > level)
                {
                    previousLevelIndex = currentIndexesWithLevels[i - 1];
                    currentIndexesWithLevels[i] = previousLevelIndex + 1;
                }
                else
                {
                    index = currentIndexesWithLevels[level];
                    currentIndexesWithLevels[i] = index + 1;
                }
            }
        }
    }

    public void FindCombinations(List<int> list, int level, Stack<int> stack)
    {
        int currentIndex;
        InitializeLevelIndexes(list);
        while (true)
        {
            currentIndex = currentIndexesWithLevels[level];
            bool levelUp = false;          
            for (int i = currentIndex; i < list.Count; i++)
            {
                if (level < _combinationLength)
                {
                    currentIndex = currentIndexesWithLevels[level];
                    MoveToUpperLevel(ref level, stack, list, currentIndex);
                    levelUp = true;
                    break;
                }
                levelUp = false;
                stack.Push(list[i]);
                if (stack.Count == _combinationLength)
                {
                    AddCombination(stack);
                    stack.Pop();
                }                                                                                 
            }

            if (!levelUp)
            {
                MoveToLowerLevel(ref level, stack, list, ref currentIndex);
                while (currentIndex >= list.Count - 1)
                {
                    if (level == 1)
                    {
                        AdjustStackCountToCurrentLevel(stack, level);
                        currentIndex = currentIndexesWithLevels[level];
                        if (currentIndex >= list.Count - 1)
                        {
                            return;
                        }
                        UpdateCurrentIndexesForLevels(level);
                    }
                    else
                    {
                        MoveToLowerLevel(ref level, stack, list, ref currentIndex);
                    }
              }
          }                               
       }
    }

    private void AddCombination(Stack<int> stack)
    {
        List<int> listNew = new List<int>();
        listNew.AddRange(stack);
        _combinationsList.AddLast(listNew);
    }

    private void MoveToUpperLevel(ref int level, Stack<int> stack, List<int> list, int index)
    {
        stack.Push(list[index]);
        level++;
    }

    private void MoveToLowerLevel(ref int level, Stack<int> stack, List<int> list, ref int currentIndex)
    {
        if (level != 1)
        {
            level--;
        }
        AdjustStackCountToCurrentLevel(stack, level);
        UpdateCurrentIndexesForLevels(level);
        currentIndex = currentIndexesWithLevels[level];
    }

    private void AdjustStackCountToCurrentLevel(Stack<int> stack, int currentLevel)
    {
        while (stack.Count >= currentLevel)
        {
            if (stack.Count != 0)
                stack.Pop();
        }
    }

    public void PrintPermutations()
    {
        int count = _combinationsList.Where(perm => perm.Count() == _combinationLength).Count();
        Console.WriteLine("The number of combinations is " + count);
    }

}
1
Bahruz Balabayov
protected List<List<T>> AllCombos<T>(Func<List<T>, List<T>, bool> comparer, params T[] items)
    {
        List<List<T>> results = new List<List<T>>();
        List<T> workingWith = items.ToList();
        results.Add(workingWith);
        items.ToList().ForEach((x) =>
        {
            results.Add(new List<T>() { x });
        });
        for (int i = 0; i < workingWith.Count(); i++)
        {
            T removed = workingWith[i];
            workingWith.RemoveAt(i);
            List<List<T>> nextResults = AllCombos2(comparer, workingWith.ToArray());
            results.AddRange(nextResults);
            workingWith.Insert(i, removed);
        }
        results = results.Where(x => x.Count > 0).ToList();
        for (int i = 0; i < results.Count; i++)
        {
            List<T> list = results[i];
            if (results.Where(x => comparer(x, list)).Count() > 1)
            {
                results.RemoveAt(i);
            }
        }

        return results;
    }

    protected List<List<T>> AllCombos2<T>(Func<List<T>, List<T>, bool> comparer, params T[] items)
    {
        List<List<T>> results = new List<List<T>>();
        List<T> workingWith = items.ToList();
        if (workingWith.Count > 1)
        {
            results.Add(workingWith);
        }
        for (int i = 0; i < workingWith.Count(); i++)
        {
            T removed = workingWith[i];
            workingWith.RemoveAt(i);
            List<List<T>> nextResults = AllCombos2(comparer, workingWith.ToArray());
            results.AddRange(nextResults);
            workingWith.Insert(i, removed);
        }
        results = results.Where(x => x.Count > 0).ToList();
        for (int i = 0; i < results.Count; i++)
        {
            List<T> list = results[i];
            if (results.Where(x => comparer(x, list)).Count() > 1)
            {
                results.RemoveAt(i);
            }
        }

        return results;
    }

Cela a fonctionné pour moi, il est légèrement plus complexe et prend en fait une fonction de rappel comparateur, et en fait deux fonctions, à la différence que AllCombos ajoute explicitement les listes d'éléments uniques. Il est très brut et peut certainement être réduit, mais il fait le travail. Toutes les suggestions de refactoring sont les bienvenues. Merci,

1
user1883961

Qu'en est-il de

static void Main(string[] args)
{
     Combos(new [] { 1, 2, 3 });
}

static void Combos(int[] arr)
{
    for (var i = 0; i <= Math.Pow(2, arr.Length); i++)
    {
        Console.WriteLine();
        var j = i;
        var idx = 0;
        do 
        {
            if ((j & 1) == 1) Console.Write($"{arr[idx]} ");
        } while ((j >>= 1) > 0 && ++idx < arr.Length);
    }
}
0
SDK

Nous pouvons utiliser la récursivité pour les problèmes de combinaison/permutation impliquant des chaînes ou des entiers.

public static void Main(string[] args)
{
    IntegerList = new List<int> { 1, 2, 3, 4 };

    PrintAllCombination(default(int), default(int));
}

public static List<int> IntegerList { get; set; }

public static int Length { get { return IntegerList.Count; } }

public static void PrintAllCombination(int position, int prefix)
{
    for (int i = position; i < Length; i++)
    {
        Console.WriteLine(prefix * 10 + IntegerList[i]);
        PrintAllCombination(i + 1, prefix * 10 + IntegerList[i]);
    }

}
0
Nans

Une version légèrement plus généralisée pour Linq utilisant C # 7. Voici le filtrage par éléments qui ont deux éléments.

static void Main(string[] args)
{
    foreach (var vals in Combos(new[] { "0", "1", "2", "3" }).Where(v => v.Skip(1).Any() && !v.Skip(2).Any()))
        Console.WriteLine(string.Join(", ", vals));
}

static IEnumerable<IEnumerable<T>> Combos<T>(T[] arr)
{
    IEnumerable<T> DoQuery(long j, long idx)
    {
        do
        {
            if ((j & 1) == 1) yield return arr[idx];
        } while ((j >>= 1) > 0 && ++idx < arr.Length);
    }
    for (var i = 0; i < Math.Pow(2, arr.Length); i++)
        yield return DoQuery(i, 0);
}
0
SDK