0Pricing
DSA Interview Prep · Leçon

Masques binaires : définir, effacer, inverser et vérifier

Implémentez des fonctions auxiliaires pour définir, effacer, inverser et vérifier des bits individuels, puis appliquez des masques binaires pour représenter des sous-ensembles dans les problèmes d’énumération.

Masques binaires : définir, effacer, inverser et vérifier est une leçon DSA Interview Prep gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage DSA Interview Prep, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours DSA Interview Prep comprend 4 leçons au total.

Que sont les masques de bits ?

Un masque de bits est un entier utilisé pour sélectionner, modifier ou vérifier certains bits d’un autre entier. Le masque contient des 1 aux positions qui vous intéressent et des 0 ailleurs. Combinés aux opérateurs bit à bit, les masques permettent d’effectuer des opérations précises sur les bits sans modifier les autres.

Les quatre opérations fondamentales sur les masques sont : positionner (mettre un bit à 1), effacer (mettre un bit à 0), inverser (basculer un bit) et vérifier (déterminer si un bit vaut 1). Chacune utilise un opérateur différent — OR, ET-NON, XOR et AND, respectivement — avec le masque 1 << k.

# The four fundamental bit mask operations
def set_bit(n, k):    return n | (1 << k)       # OR to set
def clear_bit(n, k):  return n & ~(1 << k)      # AND-NOT to clear
def toggle_bit(n, k): return n ^ (1 << k)       # XOR to toggle
def check_bit(n, k):  return (n >> k) & 1       # shift+AND to check

n = 0b10110101  # 181
print(f'n = {bin(n)}')
print(f'set   bit 1: {bin(set_bit(n, 1))}')
print(f'clear bit 2: {bin(clear_bit(n, 2))}')
print(f'toggle bit 0: {bin(toggle_bit(n, 0))}')
print(f'check bit 4: {check_bit(n, 4)}')

Positionner un bit : mettre un bit à 1

Pour positionner le bit k (le forcer à 1 quelle que soit sa valeur actuelle), combinez le nombre par OR avec le masque 1 << k. Comme 0 OR 1 = 1 et 1 OR 1 = 1, le bit ciblé devient 1. Tous les autres bits sont combinés par OR avec 0, ce qui les laisse inchangés.

Positionner un bit est une opération idempotente : l’appeler plusieurs fois produit le même effet que de l’appeler une seule fois. Si le bit k vaut déjà 1, le résultat ne change pas. Cette propriété est importante pour la gestion des indicateurs, lorsque vous souhaitez activer une fonctionnalité sans vous soucier de son état actuel.

def set_bit(n, k):
    mask = 1 << k
    return n | mask

# Set various bits
n = 0b00001010  # 10
print(f'Original: {bin(n)} = {n}')
for k in [0, 3, 6, 7]:
    result = set_bit(n, k)
    print(f'Set bit {k}: {bin(result)} = {result}')

# Idempotence: setting already-set bit does nothing
n = 0b1111
print(f'\nAlready set: {bin(set_bit(n, 2))} = {bin(n)} (unchanged)')

# Setting multiple bits at once with a combined mask
mask = (1 << 0) | (1 << 2) | (1 << 4)  # bits 0, 2, 4
print(f'Set bits 0,2,4: {bin(0 | mask)} = {0 | mask}')

Effacer un bit : mettre un bit à 0

Pour effacer le bit k (le forcer à 0 quelle que soit sa valeur actuelle), combinez le nombre par AND avec le complément du masque : n & ~(1 << k). Le complément ~(1 << k) contient des 1 dans tous les bits, sauf dans le bit k qui vaut 0. La combinaison avec 0 force le bit ciblé à 0 ; la combinaison avec 1 conserve tous les autres bits.

Comme positionner un bit, l’effacer est une opération idempotente. Effacer un bit qui vaut déjà 0 ne modifie pas le nombre. En Python, ~(1 << k) fonctionne correctement pour toute valeur de k, car Python gère automatiquement l’extension du signe : le complément possède conceptuellement des 1 dans tous les bits de poids supérieur.

def clear_bit(n, k):
    mask = ~(1 << k)     # all 1s except bit k
    return n & mask

n = 0b11111111  # 255: all bits set
print(f'Original: {bin(n)} = {n}')
for k in [0, 3, 6, 7]:
    result = clear_bit(n, k)
    print(f'Clear bit {k}: {bin(result)} = {result}')

# Clear multiple bits with combined mask complement
def clear_bits(n, positions):
    mask = 0
    for k in positions:
        mask |= (1 << k)
    return n & ~mask

result = clear_bits(0b11111111, [1, 3, 5, 7])
print(f'Clear bits 1,3,5,7: {bin(result)} = {result}')  # 0b01010101 = 85

Inverser un bit : basculer sa valeur

Pour inverser le bit k (le faire passer de 0 à 1 ou de 1 à 0), combinez le nombre par XOR avec le masque 1 << k. Combiner un bit avec 1 par XOR l’inverse ; le combiner avec 0 le laisse inchangé. Il s’agit de la propriété fondamentale de XOR appliquée à un seul bit.

L’inversion est la seule des quatre opérations qui n’est pas idempotente : l’appeler deux fois rétablit la valeur initiale. Elle convient donc parfaitement aux fonctionnalités qui alternent entre deux états, comme un interrupteur marche/arrêt ou un indicateur booléen dans une représentation entière compacte.

def toggle_bit(n, k):
    return n ^ (1 << k)

n = 0b10101010  # 170
print(f'Original:    {bin(n)}')
print(f'Toggle bit 0: {bin(toggle_bit(n, 0))}')  # off->on: 10101011
print(f'Toggle bit 1: {bin(toggle_bit(n, 1))}')  # on->off: 10101000
print(f'Toggle bit 7: {bin(toggle_bit(n, 7))}')  # on->off: 00101010

# Toggle is its own inverse: two toggles = no change
result = toggle_bit(toggle_bit(n, 3), 3)
print(f'Double toggle bit 3: {bin(result)} == original {bin(n)}? {result == n}')

# Toggle all lower k bits
def toggle_lower_k(n, k):
    mask = (1 << k) - 1   # k ones in the lowest positions
    return n ^ mask

print(f'Toggle lower 4 bits of {bin(n)}: {bin(toggle_lower_k(n, 4))}')

Vérifier un bit : déterminer s’il vaut 1

Pour vérifier si le bit k est positionné, décalez n vers la droite de k positions, puis effectuez un AND avec 1 : (n >> k) & 1. Cela amène le bit k à la position 0 et masque tous les bits de poids supérieur, en laissant 0 (le bit k valait 0) ou 1 (le bit k valait 1). Vous pouvez aussi utiliser bool(n & (1 << k)) pour obtenir un résultat True/False.

Vérifier un bit est une opération non destructive : elle ne modifie pas n. Vous pouvez vérifier plusieurs bits en décalant et en masquant chaque position indépendamment. C’est la base du parcours de la représentation binaire d’un nombre, utilisé pour l’énumération de sous-ensembles et la programmation dynamique avec des états représentés par des masques de bits.

def check_bit(n, k):
    return (n >> k) & 1

def is_bit_set(n, k):
    return bool(n & (1 << k))

n = 0b10110101  # 181
print(f'n = {bin(n)} = {n}')
for k in range(8):
    print(f'Bit {k}: {check_bit(n, k)} ({"set" if check_bit(n, k) else "clear"})')

# Count set bits using check_bit
def count_set_bits(n):
    return sum(check_bit(n, k) for k in range(n.bit_length()))

print(f'\nSet bits in {n}: {count_set_bits(n)}')

# Get bit representation as list (LSB first)
def to_bit_list(n, width=8):
    return [check_bit(n, k) for k in range(width)]

print(f'Bit list (LSB first): {to_bit_list(n)}')

Masques de bits pour représenter des sous-ensembles

Un entier de n bits peut représenter un sous-ensemble d’un ensemble à n éléments : le bit k vaut 1 si l’élément k appartient au sous-ensemble, et 0 sinon. Cela compresse un sous-ensemble en un seul entier et permet des opérations en O(1) : test d’appartenance (mask & (1 << k)), ajout d’un élément (mask | (1 << k)), suppression d’un élément (mask & ~(1 << k)) et union/intersection d’ensembles (mask1 | mask2 et mask1 & mask2).

Avec n éléments, il existe 2^n sous-ensembles possibles, chacun représenté de manière unique par un entier de n bits compris entre 0 et 2^n - 1. Parcourir tous les entiers de 0 à 2^n - 1 permet donc d’énumérer tous les sous-ensembles.

# Subset representation with bitmasks
elements = ['A', 'B', 'C', 'D']
n = len(elements)

def subset_from_mask(mask):
    return [elements[k] for k in range(n) if (mask >> k) & 1]

# Enumerate all 2^n subsets
print('All subsets:')
for mask in range(1 << n):   # 0 to 15 for n=4
    print(f'  {mask:04b}: {subset_from_mask(mask)}')

# Set operations
mask_ab = 0b0011   # {A, B}
mask_bc = 0b0110   # {B, C}
print(f'\nUnion:        {subset_from_mask(mask_ab | mask_bc)}')
print(f'Intersection: {subset_from_mask(mask_ab & mask_bc)}')
print(f'Difference A\\B: {subset_from_mask(mask_ab & ~mask_bc & 0b1111)}')

Parcourir tous les sous-ensembles d’un masque

En programmation dynamique par masques de bits, vous devez souvent parcourir tous les sous-ensembles d’un masque donné. Une astuce courante consiste à commencer par sub = mask, puis à répéter sub = (sub - 1) & mask jusqu’à atteindre 0. Chaque itération produit un sous-masque différent. La complexité totale est O(3^n) pour tous les masques, car chaque élément peut appartenir uniquement au masque externe, uniquement au sous-masque, aux deux, ou à aucun des deux.

Cette technique intervient dans des problèmes tels que « partitionner un tableau en sous-ensembles dont le XOR est égal » ou « trouver le AND maximal d’un sous-ensemble ». La capacité à énumérer efficacement les sous-masques est caractéristique de la programmation dynamique avancée par masques de bits.

def all_submasks(mask):
    submasks = []
    sub = mask
    while sub > 0:
        submasks.append(sub)
        sub = (sub - 1) & mask
    submasks.append(0)  # empty subset
    return submasks

mask = 0b1011   # {0, 1, 3}
elements = ['A', 'B', 'C', 'D']
def show(m): return '{' + ','.join(elements[k] for k in range(4) if (m>>k)&1) + '}'

print(f'All submasks of {bin(mask)} = {show(mask)}:')
for sub in all_submasks(mask):
    print(f'  {bin(sub):6s}: {show(sub)}')
print(f'Total: {len(all_submasks(mask))} submasks (should be 2^{bin(mask).count("1")} = {2**bin(mask).count("1")})')

DP par masque de bits : aperçu du problème du voyageur de commerce

La DP par masque de bits résout les problèmes dont the état inclut un sous-ensemble d'éléments visités. L'exemple classique est le problème du voyageur de commerce (TSP) : trouvez la tournée de coût minimal visitant n villes. L'état est dp[mask][city] = coût minimal pour visiter les villes de mask, en terminant dans city. Avec n villes, il y a 2^n × n états, ce qui donne un temps d'exécution de O(n^2 × 2^n), réalisable pour n ≤ 20.

Le masque sert d'ensemble visité compact. Positionner, effacer et vérifier des bits correspond respectivement à visiter, quitter et interroger des villes. C'est le principe central de la DP par masque de bits : utiliser les bits comme ensemble compact pour représenter l'état.

# TSP with bitmask DP
import sys

def tsp(dist):
    n = len(dist)
    INF = float('inf')
    # dp[mask][v] = min cost to reach v having visited cities in mask
    dp = [[INF] * n for _ in range(1 << n)]
    dp[1][0] = 0   # start at city 0, only city 0 visited (mask=1=0b0001)

    for mask in range(1 << n):
        for v in range(n):
            if dp[mask][v] == INF: continue
            if not (mask >> v) & 1: continue  # v must be in mask
            for u in range(n):
                if (mask >> u) & 1: continue  # u must not be visited
                new_mask = mask | (1 << u)
                dp[new_mask][u] = min(dp[new_mask][u], dp[mask][v] + dist[v][u])

    full_mask = (1 << n) - 1
    return min(dp[full_mask][v] + dist[v][0] for v in range(1, n))

dist = [[0,10,15,20],[10,0,35,25],[15,35,0,30],[20,25,30,0]]
print('TSP minimum tour cost:', tsp(dist))  # should be 80

Masques de plusieurs bits : extraction d'un champ

Vous devez parfois extraire non pas un seul bit, mais un champ de plusieurs bits — une plage contiguë de bits. Pour extraire les bits de la position start à start+length-1, créez un masque de length bits à 1 consécutifs : mask = (1 << length) - 1, puis utilisez (n >> start) & mask.

Cette technique est utilisée pour analyser des formats d'entiers compactés, comme les adresses IP, les données de pixels ou les registres matériels, dans lesquels plusieurs petites valeurs sont stockées dans un seul entier. Par exemple, un pixel RGB565 de 16 bits stocke le rouge dans les bits 15-11, le vert dans les bits 10-5 et le bleu dans les bits 4-0.

def extract_field(n, start, length):
    mask = (1 << length) - 1   # e.g., length=3 => mask=0b111
    return (n >> start) & mask

# RGB565 pixel format: RRRRRGGGGGGBBBBB
pixel = 0b1111100111001000  # 63432
red   = extract_field(pixel, 11, 5)   # bits 15-11
green = extract_field(pixel, 5, 6)    # bits 10-5
blue  = extract_field(pixel, 0, 5)    # bits 4-0
print(f'Pixel: {hex(pixel)}')
print(f'Red:   {red}   ({bin(red)})')
print(f'Green: {green} ({bin(green)})')
print(f'Blue:  {blue}  ({bin(blue)})')

# Packing values back
def pack_rgb565(r, g, b):
    return (r << 11) | (g << 5) | b

packe = pack_rgb565(red, green, blue)
print(f'Repacked: {hex(packed) if (packed := pack_rgb565(red,green,blue)) else 0}')

Masques de bits dans les problèmes d'entretien

Les masques de bits apparaissent couramment dans les types de problèmes d'entretien suivants :

  • Énumération de sous-ensembles : parcourir les 2^n sous-ensembles à l'aide de masques allant de 0 à 2^n-1
  • DP avec compression d'état : encoder un ensemble de nœuds ou d'éléments visités sous forme de masque de bits dans l'état de la DP
  • Systèmes d'autorisations : combiner les indicateurs READ/WRITE/EXECUTE avec OR et les vérifier avec AND
  • Suivi des cases visitées dans une grille : pour les petites grilles, regrouper les cases visitées dans un seul entier

Un indicateur important que les masques de bits sont utiles : le problème concerne un petit ensemble (n ≤ 20 éléments) et vous devez suivre des combinaisons d'appartenance. Les ensembles plus grands nécessitent d'autres représentations.

# Subset sum with bitmask enumeration
def subset_sum_exists(nums, target):
    n = len(nums)
    for mask in range(1 << n):
        total = sum(nums[k] for k in range(n) if (mask >> k) & 1)
        if total == target:
            subset = [nums[k] for k in range(n) if (mask >> k) & 1]
            print(f'Found subset {subset} summing to {target}')
            return True
    return False

subset_sum_exists([3, 1, 4, 1, 5], 10)  # finds a subset summing to 10

# Check if permutation covers all required elements (bitmask approach)
required = 0b11111  # need all 5 elements
visited  = 0b01101  # visited elements 0, 2, 3
all_visited = (visited & required) == required
print(f'All required visited: {all_visited}')  # False: missing bits 1 and 4

Astuces pour énumérer efficacement les bits

Pour parcourir les bits positionnés d'un masque, deux techniques courantes sont utilisées. La méthode du décalage et de la vérification consiste à décaler vers la droite et à vérifier le LSB. La méthode d'isolement du bit positionné de poids faible consiste à isoler le bit positionné de poids faible avec n & -n, à le traiter, puis à l'effacer avec n &= n - 1. La seconde méthode ne parcourt que les bits positionnés et est plus rapide lorsque le masque est peu dense.

En Python, vous pouvez également utiliser bin(n).count('1') ou n.bit_count() (3.10 ou version ultérieure) pour compter les bits à 1. Pour obtenir la position de chaque bit positionné, utilisez n.bit_length() - 1 pour le bit positionné le plus élevé.

# Iterate over set bit positions
def set_bit_positions(n):
    positions = []
    k = 0
    while n:
        if n & 1:
            positions.append(k)
        n >>= 1
        k += 1
    return positions

# Faster: use lowest-set-bit isolation
def set_bit_positions_fast(n):
    positions = []
    while n:
        lsb = n & -n           # isolate lowest set bit
        k = lsb.bit_length() - 1  # position of that bit
        positions.append(k)
        n &= n - 1             # clear lowest set bit
    return positions

mask = 0b10110101
print(f'Set positions (naive): {set_bit_positions(mask)}')
print(f'Set positions (fast):  {set_bit_positions_fast(mask)}')
print(f'Bit count: {bin(mask).count("1")}')
print(f'Highest set bit: {mask.bit_length() - 1}')

Vérification rapide

Évaluez votre compréhension des concepts de structures de données et d'algorithmes — préparation aux entretiens de programmation — abordés dans cette leçon.

Récapitulatif de la leçon

Dans cette leçon, vous avez appris que the quatre opérations fondamentales sur les masques de bits sont le positionnement (OR), l'effacement (AND-NOT), l'inversion (XOR) et la vérification (décalage-AND), que les entiers peuvent représenter des sous-ensembles, chaque bit indiquant l'appartenance d'un élément, ce qui permet d'énumérer les 2^n sous-ensembles, et que l'extraction de champs de plusieurs bits et la DP par masque de bits utilisent les mêmes principes de masquage pour encoder des états plus complexes. Nous allons ensuite étudier le comptage des bits, les nombres manquants et l'inversion des bits à l'aide des techniques de cette leçon et de la précédente.

Questions Fréquemment Posées

La leçon « Masques binaires : définir, effacer, inverser et vérifier » est-elle gratuite ?

Oui — le texte complet de « Masques binaires : définir, effacer, inverser et vérifier » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours DSA Interview Prep, passe à CoddyKit PRO. Le cours DSA Interview Prep comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Masques binaires : définir, effacer, inverser et vérifier » ?

Implémentez des fonctions auxiliaires pour définir, effacer, inverser et vérifier des bits individuels, puis appliquez des masques binaires pour représenter des sous-ensembles dans les problèmes d’én… Tu pratiques DSA Interview Prep avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer DSA Interview Prep ?

Aucune expérience préalable n'est requise. DSA Interview Prep sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Masques binaires : définir, effacer, inverser et vérifier » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon DSA Interview Prep ?

Oui. Chaque leçon DSA Interview Prep inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Opérateurs bit à bit : AND, OR, XOR, NOT et décalages
  2. Nombre unique et propriétés de XOR
  3. Masques binaires : définir, effacer, inverser et vérifier
  4. Compter les bits, nombre manquant et inversion des bits
← Retour à DSA Interview Prep