L'algèbre de Boole, du nom du mathématicien, logicien et philosophe britannique George Boole (1815-1864), est une partie des mathématiques qui s'intéresse à une approche algébrique de la logique, alors que la logique se fonde sur des systèmes de réécriture qui consistent à manipuler des symboles. La logique possède bien évidemment un volet sémantique et l'algèbre de Boole vient renforcer la sémantique logique en remplaçant le vrai et le faux par le 1 et le 0, le et et le ou par les opérateurs $+$ et $.$ (addition et multiplication).
Cette vision arithmétique de la logique a permis de mettre au point un système de calcul qui possède des applications dans la mise au point de circuits électroniques et autorise à aborder les problèmes de la logique sous un angle différent, ce qui peut, dans certains cas, donner la possibilité de résoudre un problème beaucoup plus simplement ou rapidement (cf. Chapitre 9, section 9.7 du livre Programmation Assembleur x86 32 et 64 bits sous Linux Ubuntu).
Soit un ensemble ${\sc A} = \{ 0, 1 \}$ pour lequel on a $0 ≤ 1$. On définit alors les opérations suivantes sur $\sc A$ :
Une variable complémentée $\ov{\table x;} \ $ est également dite signée ou négative.
Le résultat des opérations $+$ et $.$ apparaît dans la table ci-dessous d'après la sémantique que nous avons donnée.
$a$ | $b$ | $a + b$ | $a . b$ |
0 | 0 | 0 | 0 |
0 | 1 | 1 | 0 |
1 | 0 | 1 | 0 |
1 | 1 | 1 | 1 |
Le quadruplet $({\sc A},+,.,\ov{\table x;}\)$ est appelé algèbre de Boole s'il respecte les axiomes suivants :
Si l'on rapporte ces opérations à la logique, alors :
Ainsi, l'expression $a + \ov{\table a;}\ = 1$ peut s'interpréter : dire qu'une chose est vraie ou n'est pas vraie est toujours vrai. Je peux par exemple remplacer $a$ par l'énoncé il pleut, et donc, dans ce cas, il est vrai que : il pleut ou il ne pleut pas.
De la même manière $a . \ov{\table a;}\ = 0$ signifie qu'on ne peut pas dire une chose et son contraire. Je ne peux pas à la fois être grand et ne pas être grand.
On appelle fonction booléenne, une application de ${\sc A}^n$ dans $\sc A$, avec pour rappel ${\sc A} = \{ 0, 1 \}$ : $$ (x_1, x_2, ⋯, x_n) → f(x_1, x_2, ⋯, x_n) $$
La manière la plus simple de définir une fonction booléenne $f$ est de donner sa table de vérité, c'est à dire, l'ensemble des n-uplets:
$$ (\table (0, 0, ⋯, 0, f(0, 0, ⋯, 0) ) ; (0, 0, ⋯, 1, f(0, 0, ⋯, 1) ) ; ⋯ ; (1, 1, ⋯, 1, f(1, 1, ⋯, 1) ) ; ) $$Les variables $x_i$ prenant leurs valeurs dans ${\sc A} = \{ 0, 1 \}$, une fonction de $n$ variables possèdent donc $card({\sc A})^n = 2^n$ lignes, avec $card()$ qui donne la cardinalité d'un ensemble.
Prenons par exemple une fonction $f_1(x,y,z)$. Elle comporte 3 variables, elle est donc définie par une table de vérité composée de $2^3=8$ lignes numérotées de $0$ à $(8-1)$.
Ligne | $x$ | $y$ | $z$ | $f_1(x,y,z)$ |
0 | 0 | 0 | 0 | 1 |
1 | 0 | 0 | 1 | 0 |
2 | 0 | 1 | 0 | 0 |
3 | 0 | 1 | 1 | 1 |
4 | 1 | 0 | 0 | 0 |
5 | 1 | 0 | 1 | 0 |
6 | 1 | 1 | 0 | 1 |
7 | 1 | 1 | 1 | 1 |
Notation
Il existe un moyen plus simple et plus rapide de décrire la table de vérité d'une fonction booléenne en indiquant les lignes de la table de vérité qui comportent des $1$ et qui définissent la fonction. Ainsi, une fonction $f$ peut être est décrite par : $f(x,y,z,t) = (3,4,5,6,7,9,13,14,15)$ ou encore par $f(x,y,z,t)=(3-7,9,13-15)$, où l'expression $3-7$ signifie de $3$ à $7$.
Pour définir la table de vérité de $f_1$, on pourrait donc écrire :
$$ f_1(x,y,z) = (0,3,6,7) $$A partir de la table de vérité d'une fonction, on est en mesure de donner une expression de celle-ci sous forme algébrique en tant que somme de monômes, un monôme étant un produit de variables booléennes :
Avec l'exemple précédent, on obtient :
$$ f_1(x,y,z) = \ov{\table x;}\.\ov{\table y;}\.\ov{\table z;}\ + \ov{\table x;}\.y.z + x.y.\ov{\table z;}\ + x.y.z $$On remarque dans la table de vérité que les variables $x$, $y$ et $z$ suivent la notation binaire et que pour la ligne 6, on a bien $x=1, y=1, z=0$ qui correspond à $110_2 = 6$.
Dans le cas particulier des fonctions à 2 variables $f(x,y)$, on peut définir 16 fonctions différentes dont certaines ont été nommées. On retrouve notamment :
$x$ | $y$ | $or(x,y)$ | $and(x,y)$ | $xor(x,y)$ | $nor(x,y)$ | $nand(x,y)$ |
0 | 0 | 0 | 0 | 0 | 1 | 1 |
0 | 1 | 1 | 0 | 1 | 0 | 1 |
1 | 0 | 1 | 0 | 1 | 0 | 1 |
1 | 1 | 1 | 1 | 0 | 0 | 0 |
Notation | $or(x,y)$ | $and(x,y)$ | $xor(x,y)$ | $not(x)$ |
Algèbre de Boole | $x + y$ | $x . y$ | $x ⊕ y$ | $\ov{\table x;}$ |
Langage C | x || y | x && y | x ^ y | !x |
Langage C++ | x or y | x and y | x xor y | not(x) |
Logique | $x ∨ y$ | $x ∧ y$ | $(¬ x ∧ y) ∨ (x ∧ ¬ y) $ | $¬ x$ |
Le ET logique vaut 1 uniquement si ses deux opérandes $x$ et $y$ valent 1.
$x$ | $y$ | $and(x,y)$ |
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
En d'autres termes, pour que la condition if (x and y) soit vraie, il faut que les deux sous-conditions $x$ et $y$ soient vraies.
if ((0 < a) and (a < 11)) {
// a est compris entre 1 et 10
} else {
// a est en dehors de l'intervalle [1..10]
}
Le OU logique vaut 0 uniquement si ses deux opérandes $x$ et $y$ valent 0.
$x$ | $y$ | $or(x,y)$ |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
En d'autres termes, pour que la condition if (x or y) soit vraie, il faut que l'une des deux sous-conditions $x$ ou $y$ (ou les deux) soient vraies.
if ((a > 10) or (a < 1)) {
// a est en dehors de l'intervalle [1..10]
} else {
// a est compris entre 1 et 10
}
Le OU Exclusif logique vaut 0 uniquement si ses deux opérandes $x$ et $y$ ont la même valeur.
$x$ | $y$ | $xor(x,y)$ |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
En d'autres termes, pour que la condition if (x xor y) soit vraie, il faut que l'une des deux sous-conditions $x$ ou $y$ soit vraie, mais pas les deux en même temps.
if ((est_un_poisson(a) xor vole(a))) {
// élimine les poissons volants
// Traite les poissons ou les animaux qui volent
// mais pas les deux en même temps
}
Les lois de De Morgan (mathématicien et logicien britannique, 1806-1871) permettent d'exprimer la transformation de la négation d'un ET ou d'un OU logique.
Par exemple, soit la condition (0 < a) and (a < 11) vue précédemment dans l'exemple sur le ET. Il existe différentes manières d'en prendre la négation :
L'intérêt de l'algèbre de Boole est qu'elle permet de simplifier les fonctions booléennes comme on le ferait d'une expression algébrique sur les entiers ou les réels.
Deux fonctions booléennes sont dites identiques si elles possèdent la même table de vérité. Cette propriété nous permet d'établir un certain nombre d'identités et de règles de simplification :
Loi | Forme $+$ | Forme $.$ | ||
élément neutre | $(R1)$ | $x + 0 = x$ | $(R2)$ | $x . 1 = x$ |
d'idempotence | $(R3)$ | $x + x = x$ | $(R4)$ | $x . x = x$ |
d'inversion | $(R5)$ | $x + \ov{\table x;}\ = 1$ | $(R6)$ | $x . \ov{\table x;}\ = 0$ |
d'absorption | $(R7)$ | $x + x . y = x$ | $(R8)$ | $x . (x + y) = x$ |
de De Morgan | $(R9)$ | $\ov{\table x+y;}\ = \ov{\table x;}\ . \ov{\table y;}\ $ | $(R10)$ | $\ov{\table x . y;}\ = \ov{\table x;}\ + \ov{\table y;}\ $ |
de commutativité | $(R11)$ | $x + y = y + x$ | $(R12)$ | $x . y = y . x$ |
d'associativité | $(R13)$ | $x + (y + z) = (x + y) + z$ | $(R14)$ | $x . (y . z) = (x . y) . z$ |
de distributivité | $(R15)$ | $x . (y+z) = x . y + x . z$ | $(R16)$ | $x + y . z = (x+y) . (x+z)$ |
Essayons d'appliquer ces règles pour simplifier la fonction $f_2(x,y,z)$ définie par :
$$ f_2(x,y,z) = \ov{\table x;}\ . y . z + \ov{\table x;}\ . y . \ov{\table z;}\ + x . z $$Cette fonction peut être réécrite sous une forme simplifiée en :
$$ \ov{\table x;}\ . y . z + \ov{\table x;}\ . y . \ov{\table z;}\ + x . z $$La fonction ainsi réduite possède deux termes et est plus facile à concevoir sous forme de schéma électronique car elle utilise moins de symboles donc moins de portes logiques. Ces portes logiques sont implantées sous forme de transistors. Si on utilise moins de transistors on peut réduire la taille des circuits électroniques.
On utilise également d'autres formules de simplification parmi lesquelles:
$$ (R17) \;\;\;\; x + \ov{\table x;}\ . y = x + y $$Exercice 4.1
la fonction majorité de trois variables $maj(x,y,z)$ vaut $1$ si au moins deux de ses opérandes valent $1$.
Le ET logique et le OU logique peuvent être étendues pour traiter non plus des valeurs 0 et 1, mais des valeurs sur plusieurs bits. On parle alors de ET binaire et OU binaire.
Le ET et le OU binaires sont deux opérations très utiles qui permettent de faciliter certains traitements.
Par exemple, pour définir des personnages et des monstres dans un jeux ainsi que leurs caractéristiques, on peut utiliser des constantes :
Voici la définition de la structure EtreVivant qui permettra de modéliser des personnages ou des monstres :
typedef struct {
int force;
int agilite;
...
int points_de_vie;
int role;
} EtreVivant;
On peut alors définir les caractéristiques des personnages et monstres pour le role qu'ils occuperont :
const int PERSONNAGE = 1
const int ARMES = 2 // Maîtrise des armes
const int MAGIE = 3 // Maîtrise de la magie
const int MONSTRE = 4
const int PETIT = 5
const int GRAND = 6
const int ENORME = 7
Le système précédent permet de définir un personnage qui maitrise les armes, il suffit d'additionner les constantes PERSONNAGE et ARMES, ce qui donne 3, mais ce code correspond à MAGIE ! De la même manière un PETIT MONSRE (5 + 4 = 9) correspond également à ENORME ARMES (2 + 7). On a donc un problème pour identifier de manière unique une caractéristique d'un personnage ou d'un monstre.
Un système plus intelligent va se baser sur une représentation par puissances de 2, donc du binaire :
Voici donc comment il faut procéder :
const int PERSONNAGE = 1
const int ARMES = 2 // Maîtrise des armes
const int MAGIE = 4 // Maîtrise de la magie
const int MONSTRE = 8
const int PETIT = 16
const int GRAND = 32
const int ENORME = 64
A partir de cette modélisation, on peut alors définir différents êtres vivants pour le rôle qu'ils occuperont :
Guerrier = PERSONNAGE | ARMES | GRAND
Magicien = PERSONNAGE | MAGIE | GRAND
Elfe = PERSONNAGE | MAGIE | MAITRISE_ARMES | GRAND
Nain = PERSONNAGE | ARMES | PETIT
Gobelin = MONSTRE | PETIT
Troll = MONSTRE | GRAND
Dragon = MONTRE | ENORME | MAGIE
Par exemple un Guerrier est un personnage de grande taille qui possède une maîtrise des armes.
Le symbole | représente le OU binaire en langage C. Dans le cas présent, étant donné que l'on manipule des puissances de 2, on pourrait le remplacer par l'opérateur $+$ qui travaille sur les entiers.
Role | ENORME | GRAND | PETIT | MONSTRE | MAGIE | ARMES | PERSONNAGE | Valeur |
Bit N° | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ----- |
Puiss. de 2 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | ----- |
Guerrier | x | x | x | 35 | ||||
Magicien | x | x | x | 37 | ||||
Elfe | x | x | x | x | 39 | |||
Nain | x | x | x | 19 | ||||
Gobelin | x | x | 24 | |||||
Troll | x | x | 40 | |||||
Dragon | x | x | x | 76 |
On voit sur le tableau précédent que tout personnage ou monstre qui maitrise la magie comme les magiciens, les elfes ou les dragons, possèdera le bit 2 positionné à 1 ($2^2 = 4$) dans la valeur de son role.
On peut alors utiliser :
Pour vérifier si un être vivant (p) est un monstre, il faut vérifier que son champ role contient la valeur 8 (=MONSTRE). Si on écrit :
if (p.role == MONSTRE) {
...
}
cela ne fonctionne pas car un monstre possède d'autres caractéristiques. Les monstres (Gobelin, Troll et Dragon) ont des valeurs de role différentes de 8, mais possèdent le bit 3 positionné à 1 ($2^3 = 1$). On doit donc écrire :
if ((p.role & MONSTRE) != 0) {
...
}
ou encore :
if ((p.role & MONSTRE) == MONSTRE) {
...
}
Le symbole & représente le ET-binaire du langage C.
Pour un guerrier, on obtient :
Role | ENORME | GRAND | PETIT | MONSTRE | MAGIE | ARMES | PERSONNAGE | Valeur |
Bit | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ----- |
Puiss. de 2 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | ----- |
Guerrier | x | x | x | 35 | ||||
& MONSTRE | x | 8 | ||||||
Résultat | 0 |
Pour un dragon, on obtient :
Role | ENORME | GRAND | PETIT | MONSTRE | MAGIE | ARMES | PERSONNAGE | Valeur |
Bit | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ----- |
Puiss. de 2 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | ----- |
Dragon | x | x | x | 76 | ||||
& MONSTRE | x | 8 | ||||||
Résultat | x | 8 |
Pour vérifier si on a à faire à un personnage qui maitrise les armes, il faut choisir :
if ((p.role & (PERSONNAGE | ARMES)) == (PERSONNAGE | ARMES)) {
...
}
PERSONNAGE | ARMES représente un masque de sélection, tout comme MONSTRE dans l'exemple précédent.
Imaginons maintenant qu'un personnage découvre le pouvoir maitrise de la magie, il faut donc ajouter le bit 2 soit la valeur 4 ($=2^2$) à son rôle et on fait cela grâce au OU-binaire (OR) :
p.role = p.role | MAGIE;
Par exemple, avec un guerrier, on obtient :
Role | ENORME | GRAND | PETIT | MONSTRE | MAGIE | ARMES | PERSONNAGE | Valeur |
Bit | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ----- |
Puiss. de 2 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | ----- |
Guerrier | x | x | x | 35 | ||||
∣ MAGIE | x | 8 | ||||||
Résultat | x | x | x | x | 39 |
Exercice 4.2
Démontrez à l'aide de tables de vérité, les équivalences suivantes :
Exercice 4.3
Démontrez algébriquement les égalités suivantes :
Exercice 4.4
Simplifiez les expressions suivantes :
Définir les termes suivants :