Comment utiliser itertools.combinations en Python

PythonBeginner
Pratiquer maintenant

Introduction

Le module itertools de Python fournit une collection d'outils rapides et économes en mémoire pour créer des itérateurs permettant des boucles efficaces. Une fonction particulièrement utile de ce module est combinations(), qui vous permet de générer toutes les combinaisons possibles d'une longueur spécifiée à partir d'une collection d'éléments.

Dans ce lab (atelier) LabEx, vous apprendrez à utiliser la fonction itertools.combinations() pour créer des combinaisons d'éléments, à comprendre ses paramètres et à explorer des applications pratiques. Cette connaissance enrichira votre boîte à outils de programmation Python et vous aidera à résoudre des problèmes complexes impliquant des opérations combinatoires.

Prise en main de itertools.combinations

Commençons par comprendre ce qu'est une combinaison et comment utiliser la fonction itertools.combinations() en Python.

Qu'est-ce qu'une combinaison ?

En mathématiques, une combinaison est une sélection d'éléments à partir d'une collection, où l'ordre n'a pas d'importance. Par exemple, lorsque vous sélectionnez 2 éléments à partir de l'ensemble {1, 2, 3}, les combinaisons possibles sont {1, 2}, {1, 3} et {2, 3}.

Installation des modules requis

Le module itertools de Python fait partie de la bibliothèque standard, vous n'avez donc pas besoin d'installer quoi que ce soit de supplémentaire. Créons un nouveau fichier Python pour expérimenter avec la fonction combinations.

  1. Dans l'éditeur WebIDE, créez un nouveau fichier en cliquant sur l'icône "Nouveau fichier" dans le panneau de l'explorateur ou en utilisant le raccourci clavier Ctrl+N.

  2. Enregistrez le fichier sous le nom combinations_intro.py dans le répertoire /home/labex/project.

  3. Écrivons maintenant un simple script Python pour démontrer l'utilisation de base de itertools.combinations :

## Import the combinations function from itertools module
import itertools

## Create a simple list of elements
fruits = ['apple', 'banana', 'orange']

## Generate all combinations of 2 fruits from the list
fruit_combinations = itertools.combinations(fruits, 2)

## Convert the iterator to a list to display all combinations
combinations_list = list(fruit_combinations)

## Print the result
print("All possible combinations of 2 fruits:")
print(combinations_list)

## Count the total number of combinations
print(f"Total number of combinations: {len(combinations_list)}")
  1. Exécutez le script en ouvrant un terminal (s'il n'est pas déjà ouvert) et en exécutant :
python3 combinations_intro.py
run script

Vous devriez voir une sortie similaire à ceci :

All possible combinations of 2 fruits:
[('apple', 'banana'), ('apple', 'orange'), ('banana', 'orange')]
Total number of combinations: 3

Compréhension de la sortie

La sortie montre toutes les combinaisons possibles de 2 éléments sélectionnés à partir de notre liste de fruits. Chaque combinaison est représentée sous forme de tuple :

  • ('apple', 'banana')
  • ('apple', 'orange')
  • ('banana', 'orange')

Notez qu'une combinaison comme ('banana', 'apple') n'est pas incluse car dans les combinaisons, l'ordre n'a pas d'importance. Ainsi, ('apple', 'banana') et ('banana', 'apple') sont considérées comme la même combinaison.

Compréhension des paramètres et de la syntaxe

Maintenant, approfondissons la fonction itertools.combinations(), en explorant ses paramètres et sa syntaxe plus en détail.

Signature de la fonction

La fonction itertools.combinations() a la syntaxe suivante :

itertools.combinations(iterable, r)

Où :

  • iterable : Une séquence, un itérateur ou tout autre objet qui prend en charge l'itération (comme une liste, un tuple ou une chaîne de caractères)
  • r : La longueur de chaque combinaison à générer

Créons un autre fichier Python pour explorer divers exemples.

  1. Créez un nouveau fichier nommé combinations_parameters.py dans le répertoire /home/labex/project.

  2. Ajoutez le code suivant au fichier :

import itertools

## Example 1: Combinations from a string
letters = "ABCD"
print("Example 1: Combinations from a string 'ABCD', r=2")
letter_combinations = list(itertools.combinations(letters, 2))
print(letter_combinations)
print(f"Number of combinations: {len(letter_combinations)}\n")

## Example 2: Different values of r
numbers = [1, 2, 3, 4]

## r=1 (individual elements)
print("Example 2a: Combinations with r=1")
combinations_r1 = list(itertools.combinations(numbers, 1))
print(combinations_r1)
print(f"Number of combinations: {len(combinations_r1)}\n")

## r=2 (pairs)
print("Example 2b: Combinations with r=2")
combinations_r2 = list(itertools.combinations(numbers, 2))
print(combinations_r2)
print(f"Number of combinations: {len(combinations_r2)}\n")

## r=3 (triplets)
print("Example 2c: Combinations with r=3")
combinations_r3 = list(itertools.combinations(numbers, 3))
print(combinations_r3)
print(f"Number of combinations: {len(combinations_r3)}\n")

## r=4 (all elements)
print("Example 2d: Combinations with r=4")
combinations_r4 = list(itertools.combinations(numbers, 4))
print(combinations_r4)
print(f"Number of combinations: {len(combinations_r4)}\n")

## Example 3: Empty result when r is larger than the iterable length
print("Example 3: Empty result when r > len(iterable)")
too_large_r = list(itertools.combinations(numbers, 5))
print(too_large_r)
print(f"Number of combinations: {len(too_large_r)}")
  1. Exécutez le script :
python3 combinations_parameters.py

Vous devriez voir une sortie similaire à ceci :

Example 1: Combinations from a string 'ABCD', r=2
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')]
Number of combinations: 6

Example 2a: Combinations with r=1
[(1,), (2,), (3,), (4,)]
Number of combinations: 4

Example 2b: Combinations with r=2
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
Number of combinations: 6

Example 2c: Combinations with r=3
[(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]
Number of combinations: 4

Example 2d: Combinations with r=4
[(1, 2, 3, 4)]
Number of combinations: 1

Example 3: Empty result when r > len(iterable)
[]
Number of combinations: 0

Points clés

À partir de ces exemples, vous pouvez observer plusieurs propriétés importantes de la fonction itertools.combinations() :

  1. Elle fonctionne avec différents types d'itérateurs (chaînes de caractères, listes, etc.)
  2. La valeur de r détermine la taille de chaque combinaison
  3. Le nombre de combinaisons suit la formule mathématique : n! / (r! * (n - r)!), où n est la longueur de l'itérateur
  4. Lorsque r est égal à la longueur de l'itérateur, il n'y a qu'une seule combinaison (l'itérateur entier)
  5. Lorsque r est supérieur à la longueur de l'itérateur, une liste vide est retournée

Cette compréhension des paramètres vous aidera à appliquer efficacement la fonction itertools.combinations() dans vos programmes Python.

Applications pratiques des combinaisons

Maintenant, explorons quelques applications pratiques de la fonction itertools.combinations(). Nous allons implémenter quelques exemples du monde réel pour démontrer comment cette fonction peut être utilisée pour résoudre des problèmes courants.

Exemple 1 : Formation d'équipes

Imaginez que vous deviez former des équipes d'une certaine taille à partir d'un groupe de personnes. Créons un programme qui aide à former toutes les équipes possibles.

  1. Créez un nouveau fichier nommé team_formation.py dans le répertoire /home/labex/project.

  2. Ajoutez le code suivant :

import itertools

def form_teams(members, team_size):
    """Generate all possible teams of the specified size from the list of members."""
    teams = list(itertools.combinations(members, team_size))
    return teams

## List of team members
team_members = ["Alice", "Bob", "Charlie", "David", "Eva", "Frank"]

## Form teams of different sizes
pairs = form_teams(team_members, 2)
trios = form_teams(team_members, 3)

## Display the results
print(f"Total members: {len(team_members)}")
print(f"Members: {team_members}\n")

print(f"Possible pairs (teams of 2): {len(pairs)}")
for i, pair in enumerate(pairs, 1):
    print(f"Team {i}: {' and '.join(pair)}")

print(f"\nPossible trios (teams of 3): {len(trios)}")
for i, trio in enumerate(trios, 1):
    print(f"Team {i}: {', '.join(trio)}")
  1. Exécutez le script :
python3 team_formation.py

Vous devriez voir une sortie qui liste toutes les paires et tous les trios possibles qui peuvent être formés à partir des six membres de l'équipe.

Exemple 2 : Recherche de tous les sous-ensembles

Une autre application courante consiste à générer tous les sous-ensembles possibles d'un ensemble donné (également connu sous le nom d'ensemble des parties). Implémentons cela :

  1. Créez un nouveau fichier nommé generate_subsets.py dans le répertoire /home/labex/project.

  2. Ajoutez le code suivant :

import itertools

def generate_all_subsets(items):
    """Generate all possible subsets of the given items."""
    all_subsets = []

    ## Empty set is always a subset
    all_subsets.append(())

    ## Generate subsets of all possible lengths
    for r in range(1, len(items) + 1):
        subsets_of_length_r = list(itertools.combinations(items, r))
        all_subsets.extend(subsets_of_length_r)

    return all_subsets

## Sample set of items
items = ['A', 'B', 'C']

## Generate all subsets
subsets = generate_all_subsets(items)

## Display the results
print(f"Original set: {items}")
print(f"Total number of subsets: {len(subsets)}")
print("All subsets (including the empty set):")

for i, subset in enumerate(subsets):
    if len(subset) == 0:
        print(f"{i+1}. Empty set {{}}")
    else:
        print(f"{i+1}. {set(subset)}")
  1. Exécutez le script :
python3 generate_subsets.py

La sortie montrera tous les sous-ensembles possibles de l'ensemble {A, B, C}, y compris l'ensemble vide.

Créons une application pratique qui aide un restaurant à générer toutes les combinaisons de repas possibles à partir de ses éléments de menu :

  1. Créez un nouveau fichier nommé menu_combinations.py dans le répertoire /home/labex/project.

  2. Ajoutez le code suivant :

import itertools

def generate_meal_combinations(appetizers, main_courses, desserts):
    """Generate all possible meal combinations with one item from each category."""
    meal_combinations = []

    for app in appetizers:
        for main in main_courses:
            for dessert in desserts:
                meal_combinations.append((app, main, dessert))

    return meal_combinations

def generate_combo_deals(menu_items, combo_size):
    """Generate all possible combo deals of the specified size."""
    return list(itertools.combinations(menu_items, combo_size))

## Menu categories
appetizers = ["Salad", "Soup", "Bruschetta"]
main_courses = ["Pasta", "Steak", "Fish"]
desserts = ["Ice Cream", "Cake", "Fruit"]

## All menu items
all_items = appetizers + main_courses + desserts

## Generate all possible three-course meals
meals = generate_meal_combinations(appetizers, main_courses, desserts)

## Generate all possible 2-item combo deals from the entire menu
combos = generate_combo_deals(all_items, 2)

## Display the results
print("Restaurant Menu Planner\n")

print("Menu Items:")
print(f"Appetizers: {appetizers}")
print(f"Main Courses: {main_courses}")
print(f"Desserts: {desserts}\n")

print(f"Total possible three-course meals: {len(meals)}")
print("Sample meals:")
for i in range(min(5, len(meals))):
    app, main, dessert = meals[i]
    print(f"Meal option {i+1}: {app} + {main} + {dessert}")

print(f"\nTotal possible 2-item combo deals: {len(combos)}")
print("Sample combo deals:")
for i in range(min(5, len(combos))):
    print(f"Combo {i+1}: {' + '.join(combos[i])}")
  1. Exécutez le script :
python3 menu_combinations.py

La sortie montrera diverses combinaisons de repas et offres d'assortiment qui peuvent être créées à partir des éléments du menu.

Ces exemples démontrent comment la fonction itertools.combinations() peut être appliquée pour résoudre des problèmes du monde réel impliquant des combinaisons d'éléments. En comprenant comment utiliser efficacement cette fonction, vous pouvez développer des solutions plus efficaces pour les problèmes qui impliquent la sélection de groupes d'éléments à partir d'un ensemble plus large.

Résolution d'un problème avec des combinaisons

Dans cette étape, nous allons appliquer nos connaissances sur la fonction itertools.combinations() pour résoudre un problème de programmation courant. Nous allons implémenter une solution pour trouver toutes les manières possibles de sélectionner des éléments dont la somme est égale à une valeur donnée, souvent appelée le problème de la "somme de sous-ensemble" (Subset Sum).

Problème : Trouver les sous-ensembles dont la somme est égale à une valeur cible

Imaginez que vous avez un ensemble de nombres et que vous souhaitez trouver tous les sous-ensembles dont la somme est égale à une valeur cible spécifique. C'est un problème classique qui peut être résolu en utilisant des combinaisons.

Implémentons une solution :

  1. Créez un nouveau fichier nommé subset_sum.py dans le répertoire /home/labex/project.

  2. Ajoutez le code suivant :

import itertools

def find_subsets_with_sum(numbers, target_sum):
    """Find all subsets of numbers that add up to the target sum."""
    results = []

    ## Try combinations of different lengths
    for r in range(1, len(numbers) + 1):
        ## Generate all combinations of length r
        combinations = itertools.combinations(numbers, r)

        ## Check each combination
        for combo in combinations:
            if sum(combo) == target_sum:
                results.append(combo)

    return results

## Example usage
numbers = [3, 5, 2, 7, 4, 9, 1, 8]
target_sum = 10

## Find subsets with sum equal to target_sum
matching_subsets = find_subsets_with_sum(numbers, target_sum)

## Display results
print(f"Numbers: {numbers}")
print(f"Target sum: {target_sum}")
print(f"Number of matching subsets: {len(matching_subsets)}")

if matching_subsets:
    print("Subsets that add up to the target sum:")
    for i, subset in enumerate(matching_subsets, 1):
        print(f"Subset {i}: {subset} (Sum: {sum(subset)})")
else:
    print("No subsets found that add up to the target sum.")

## Let's find subsets for another target sum
second_target = 15
matching_subsets_2 = find_subsets_with_sum(numbers, second_target)

print(f"\nTarget sum: {second_target}")
print(f"Number of matching subsets: {len(matching_subsets_2)}")

if matching_subsets_2:
    print("Subsets that add up to the target sum:")
    for i, subset in enumerate(matching_subsets_2, 1):
        print(f"Subset {i}: {subset} (Sum: {sum(subset)})")
else:
    print("No subsets found that add up to the target sum.")
  1. Exécutez le script :
python3 subset_sum.py

La sortie affichera toutes les combinaisons de nombres de notre liste dont la somme est égale à 10, puis à 15.

Extension : Version interactive

Améliorons notre solution en la rendant interactive, permettant à l'utilisateur d'entrer sa propre liste de nombres et la somme cible :

  1. Créez un nouveau fichier nommé interactive_subset_sum.py dans le répertoire /home/labex/project.

  2. Ajoutez le code suivant :

import itertools

def find_subsets_with_sum(numbers, target_sum):
    """Find all subsets of numbers that add up to the target sum."""
    results = []

    ## Try combinations of different lengths
    for r in range(1, len(numbers) + 1):
        ## Generate all combinations of length r
        combinations = itertools.combinations(numbers, r)

        ## Check each combination
        for combo in combinations:
            if sum(combo) == target_sum:
                results.append(combo)

    return results

def main():
    ## Get user input
    try:
        input_str = input("Enter a list of numbers separated by spaces: ")
        numbers = [int(num) for num in input_str.split()]

        target_sum = int(input("Enter the target sum: "))

        ## Find matching subsets
        matching_subsets = find_subsets_with_sum(numbers, target_sum)

        ## Display results
        print(f"\nNumbers: {numbers}")
        print(f"Target sum: {target_sum}")
        print(f"Number of matching subsets: {len(matching_subsets)}")

        if matching_subsets:
            print("Subsets that add up to the target sum:")
            for i, subset in enumerate(matching_subsets, 1):
                print(f"Subset {i}: {subset} (Sum: {sum(subset)})")
        else:
            print("No subsets found that add up to the target sum.")

    except ValueError:
        print("Invalid input. Please enter integers only.")

if __name__ == "__main__":
    main()
  1. Exécutez le script interactif :
python3 interactive_subset_sum.py
  1. Lorsque vous y êtes invité, entrez une liste de nombres (par exemple, 3 5 2 7 4 9 1 8) et une somme cible (par exemple, 10).

Le script trouvera et affichera toutes les combinaisons des nombres entrés dont la somme est égale à la somme cible spécifiée.

Compréhension de la solution

Notre solution utilise itertools.combinations() pour générer des sous-ensembles de différentes tailles à partir de la liste de nombres d'entrée. Pour chaque sous-ensemble, nous vérifions si la somme est égale à la valeur cible, et si c'est le cas, nous l'ajoutons à nos résultats.

Cette approche démontre une application puissante des combinaisons dans la résolution d'un problème algorithmique courant. L'efficacité de itertools.combinations() permet de résoudre efficacement le problème de la somme de sous-ensemble pour des entrées de petite à moyenne taille.

En pratique, pour des listes très grandes, des algorithmes plus optimisés pourraient être nécessaires, mais pour de nombreux scénarios du monde réel, cette approche basée sur les combinaisons offre une solution claire et compréhensible.

Résumé

Dans ce laboratoire, vous avez appris à utiliser la fonction itertools.combinations() de Python pour générer des combinaisons à partir d'une collection d'éléments. Voici les points clés à retenir :

  1. Utilisation de base : La fonction itertools.combinations(iterable, r) génère toutes les combinaisons possibles de longueur r à partir de l'iterable d'entrée.

  2. Paramètres de la fonction : La fonction prend deux paramètres :

    • iterable : Une séquence, un itérateur ou tout autre objet qui prend en charge l'itération
    • r : La longueur de chaque combinaison à générer
  3. Propriétés clés :

    • L'ordre n'a pas d'importance dans les combinaisons
    • Aucun élément ne peut apparaître plus d'une fois dans une combinaison
    • La fonction renvoie un itérateur qui génère les combinaisons une par une
  4. Applications pratiques : Vous avez appris à appliquer la fonction combinations() pour résoudre divers problèmes :

    • Formation d'équipes à partir d'un groupe de personnes
    • Génération de tous les sous-ensembles d'un ensemble donné
    • Création de combinaisons de repas et d'offres d'assortiment
    • Recherche de sous-ensembles dont la somme est égale à une valeur cible

La fonction itertools.combinations() est un outil puissant pour résoudre des problèmes qui impliquent la sélection de groupes d'éléments à partir d'une collection plus large. En utilisant cette fonction, vous pouvez écrire un code plus propre et plus efficace pour gérer les opérations combinatoires en Python.

Dans vos futurs projets Python, n'oubliez pas que le module itertools propose de nombreuses autres fonctions utiles pour travailler avec les itérateurs, ce qui peut vous aider à écrire un code plus élégant et plus efficace.