Logique et langage mathématique#

Soit les mathématiques sont trop grandes pour l’esprit humain, soit l’esprit humain est plus qu’une machine.

Kurt Gödel

La logique mathématique est le fondement de tout raisonnement rigoureux. Elle fournit un langage précis pour formuler des énoncés, des règles pour en déduire de nouveaux, et des outils pour vérifier la validité d’une démonstration.

Propositions et valeurs de vérité#

Définition 1 (Proposition — Principe de bivalence)

Une proposition logique est un énoncé qui est soit vrai, soit faux, mais pas les deux simultanément. On note 1 (ou V) pour vrai et 0 (ou F) pour faux.

Exemple 1

  • « 2 est pair » est une proposition vraie.

  • « 5 est pair » est une proposition fausse.

  • « \(x\) est pair » n’est pas une proposition mais un prédicat : sa valeur de vérité dépend de \(x\).

  • « Cette phrase est fausse » n’est pas une proposition (paradoxe du menteur).

Remarque 1

Le principe de bivalence est adopté en logique classique. D’autres logiques (intuitionniste, floue) relâchent cette contrainte. En mathématiques, on travaille dans le cadre classique.

Les connecteurs logiques#

Négation#

Définition 2 (Négation \(\lnot\))

Soit \(P\) une proposition. La négation de \(P\), notée \(\lnot P\) (lire « non \(P\) »), est vraie si et seulement si \(P\) est fausse.

\(P\)

\(\lnot P\)

1

0

0

1

Conjonction et disjonction#

Définition 3 (Disjonction \(\lor\) et conjonction \(\land\))

Soient \(P\) et \(Q\) deux propositions.

  • La disjonction \(P \lor Q\)\(P\) ou \(Q\) ») est vraie si au moins l’une des deux est vraie.

  • La conjonction \(P \land Q\)\(P\) et \(Q\) ») est vraie si et seulement si les deux sont vraies.

Remarque 2

Le « ou » logique est inclusif : \(P \lor Q\) est vraie même si \(P\) et \(Q\) sont toutes deux vraies. Le « ou exclusif » (XOR) s’écrit \(P \oplus Q = (P \lor Q) \land \lnot(P \land Q)\).

\(P\)

\(Q\)

\(P \lor Q\)

\(P \land Q\)

\(P \oplus Q\)

1

1

1

1

0

1

0

1

0

1

0

1

1

0

1

0

0

0

0

0

Implication et équivalence#

Définition 4 (Implication \(\implies\))

La proposition \(P \implies Q\)\(P\) implique \(Q\) », ou « si \(P\) alors \(Q\) ») est définie par

\[P \implies Q \iff \lnot P \lor Q\]

Elle est fausse uniquement lorsque \(P\) est vraie et \(Q\) est fausse.

Remarque 3

Une implication \(P \implies Q\) vraie signifie : « Chaque fois que \(P\) est satisfaite, \(Q\) l’est aussi. » Si \(P\) est fausse, l’implication est vraie vacuitement : on dit qu’une hypothèse fausse implique n’importe quoi (ex falso quodlibet).

Si \(P \implies Q\), on dit que \(P\) est une condition suffisante de \(Q\), et que \(Q\) est une condition nécessaire de \(P\).

Définition 5 (Équivalence \(\iff\))

La proposition \(P \iff Q\)\(P\) équivaut à \(Q\) ») est définie par

\[P \iff Q \iff (P \implies Q) \land (Q \implies P)\]

Elle est vraie lorsque \(P\) et \(Q\) ont la même valeur de vérité.

\(P\)

\(Q\)

\(P \implies Q\)

\(Q \implies P\)

\(P \iff Q\)

1

1

1

1

1

1

0

0

1

0

0

1

1

0

0

0

0

1

1

1

Tautologies et équivalences logiques#

Définition 6 (Tautologie et contradiction)

Une tautologie est une proposition toujours vraie, quelle que soit la valeur des variables. Une contradiction est une proposition toujours fausse.

Exemple 2

  • \(P \lor \lnot P\) est une tautologie (principe du tiers exclu).

  • \(P \land \lnot P\) est une contradiction (principe de non-contradiction).

Proposition 1 (Équivalences fondamentales)

Soient \(P\), \(Q\), \(R\) des propositions.

Double négation : \(\lnot\lnot P \iff P\)

Idempotence : \(P \lor P \iff P\) et \(P \land P \iff P\)

Commutativité : \(P \lor Q \iff Q \lor P\) et \(P \land Q \iff Q \land P\)

Associativité : \((P \lor Q) \lor R \iff P \lor (Q \lor R)\) et \((P \land Q) \land R \iff P \land (Q \land R)\)

Distributivité : \(P \land (Q \lor R) \iff (P \land Q) \lor (P \land R)\) et \(P \lor (Q \land R) \iff (P \lor Q) \land (P \lor R)\)

Lois de De Morgan : \(\lnot(P \lor Q) \iff \lnot P \land \lnot Q\) et \(\lnot(P \land Q) \iff \lnot P \lor \lnot Q\)

Contraposée : \((P \implies Q) \iff (\lnot Q \implies \lnot P)\)

Absorption : \(P \lor (P \land Q) \iff P\) et \(P \land (P \lor Q) \iff P\)

Proof. Chaque équivalence peut être vérifiée par table de vérité. Illustrons les lois de De Morgan.

\(P\)

\(Q\)

\(P \lor Q\)

\(\lnot(P \lor Q)\)

\(\lnot P\)

\(\lnot Q\)

\(\lnot P \land \lnot Q\)

1

1

1

0

0

0

0

1

0

1

0

0

1

0

0

1

1

0

1

0

0

0

0

0

1

1

1

1

Les colonnes 4 et 7 sont identiques, confirmant \(\lnot(P \lor Q) \iff \lnot P \land \lnot Q\).

Remarque 4

Les connecteurs \(\lnot\) et \(\lor\) forment un système complet : tout connecteur logique s’exprime avec ces deux-là. De même, \(\{\lnot, \land\}\) et NAND (non-et) sont complets. Cette complétude est importante en logique des circuits.

Hide code cell source

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.patches import FancyBboxPatch

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)

Hide code cell source

# Tables de vérité de tous les connecteurs binaires
fig, axes = plt.subplots(4, 2, figsize=(10, 14))
axes = axes.flatten()

vals = [(0, 0), (0, 1), (1, 0), (1, 1)]
P = np.array([v[0] for v in vals])
Q = np.array([v[1] for v in vals])

connectors = {
    r'$P \wedge Q$':     P & Q,
    r'$P \vee Q$':      P | Q,
    r'$P \Rightarrow Q$':  (~P.astype(bool) | Q.astype(bool)).astype(int),
    r'$P \Leftrightarrow Q$':      (P == Q).astype(int),
    r'$P \oplus Q$':    (P ^ Q),
    r'$\neg P$':       (~P.astype(bool)).astype(int),
    r'NAND':            (~(P.astype(bool) & Q.astype(bool))).astype(int),
    r'NOR':             (~(P.astype(bool) | Q.astype(bool))).astype(int),
}

labels = ['(0,0)', '(0,1)', '(1,0)', '(1,1)']
colors_true  = sns.color_palette("Blues")[4]
colors_false = sns.color_palette("Reds")[2]

for ax, (name, result) in zip(axes, connectors.items()):
    colors = [colors_true if r else colors_false for r in result]
    bars = ax.bar(labels, result, color=colors, edgecolor='black', linewidth=0.8)
    ax.set_title(name, fontsize=13)
    ax.set_ylim(-0.1, 1.3)
    ax.set_yticks([0, 1])
    ax.set_yticklabels(['Faux', 'Vrai'])
    ax.set_xlabel('$(P, Q)$', fontsize=9)
    for bar, val in zip(bars, result):
        ax.text(bar.get_x() + bar.get_width()/2, val + 0.05,
                'V' if val else 'F', ha='center', fontsize=11, fontweight='bold')

fig.suptitle('Tables de vérité des connecteurs logiques binaires', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
_images/5328edff299712f1a4670cf618c17105c4d5ced0e6a5c6cf2c3beeb2f4892880.png

Prédicats et quantificateurs#

Définition 7 (Prédicat)

Soit \(E\) un ensemble. Un prédicat \(P(x)\) est une proposition paramétrée par une variable \(x \in E\). Sa valeur de vérité dépend de \(x\).

Définition 8 (Quantificateurs)

Soit \(E\) un ensemble et \(P(x)\) un prédicat.

  • Quantificateur universel : \(\forall x \in E,\ P(x)\) signifie « pour tout \(x\) dans \(E\), \(P(x)\) est vrai ».

  • Quantificateur existentiel : \(\exists x \in E,\ P(x)\) signifie « il existe au moins un \(x\) dans \(E\) tel que \(P(x)\) est vrai ».

  • Existence et unicité : \(\exists! x \in E,\ P(x)\) signifie « il existe un unique \(x\) dans \(E\) tel que \(P(x)\) est vrai ».

Proposition 2 (Négation des quantificateurs — Lois de De Morgan généralisées)

\[\lnot(\forall x \in E,\ P(x)) \iff \exists x \in E,\ \lnot P(x)\]
\[\lnot(\exists x \in E,\ P(x)) \iff \forall x \in E,\ \lnot P(x)\]

Exemple 3

Nions la définition de la limite \(\lim_{x \to a} f(x) = \ell\) :

\[\forall \varepsilon > 0,\ \exists \delta > 0,\ \forall x \in E,\ |x - a| < \delta \implies |f(x) - \ell| < \varepsilon\]

Sa négation (= \(f\) n’a pas \(\ell\) pour limite) est :

\[\exists \varepsilon > 0,\ \forall \delta > 0,\ \exists x \in E,\ |x - a| < \delta \land |f(x) - \ell| \geq \varepsilon\]

La négation inverse l’ordre des quantificateurs et nie la conclusion.

Remarque 5

L’ordre des quantificateurs est crucial. En général :

\[\forall x \in E,\ \exists y \in F,\ P(x, y) \not\iff \exists y \in F,\ \forall x \in E,\ P(x, y)\]

La première affirme : pour chaque \(x\), il existe un \(y\) (qui peut dépendre de \(x\)). La seconde affirme : il existe un \(y\) universel qui convient pour tous les \(x\) simultanément.

Exemple : avec \(E = F = \mathbb{R}\) et \(P(x,y) = (x < y)\) :

  • \(\forall x \in \mathbb{R},\ \exists y \in \mathbb{R},\ x < y\) est vraie (prendre \(y = x + 1\)).

  • \(\exists y \in \mathbb{R},\ \forall x \in \mathbb{R},\ x < y\) est fausse (\(\mathbb{R}\) n’est pas majoré).

Hide code cell source

# Visualisation : quantificateurs et ordre
fig, axes = plt.subplots(2, 1, figsize=(9, 9))

x_vals = np.linspace(-3, 3, 200)

# Pour tout x, il existe y = x+1 > x
ax = axes[0]
for x0 in [-2, -1, 0, 1, 2]:
    y0 = x0 + 1
    ax.annotate('', xy=(x0 + 0.05, y0), xytext=(x0, x0),
                arrowprops=dict(arrowstyle='->', color='steelblue', lw=2))
    ax.scatter([x0], [x0], s=60, color='steelblue', zorder=5)
    ax.scatter([x0], [y0], s=60, color='coral', marker='D', zorder=5)

ax.plot(x_vals, x_vals, 'k--', alpha=0.3, label='$y = x$')
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
ax.set_title(r'$\forall x,\ \exists y > x$' '\n(ici $y = x+1$, vrai)',
             fontsize=12)
ax.legend()
ax.set_aspect('equal')
ax.grid(True, alpha=0.3)

# Il n'existe pas de y supérieur à tout x
ax2 = axes[1]
y_candidates = [1, 2, 3]
x_test = np.linspace(-1, 5, 100)
for y_c in y_candidates:
    ax2.axhline(y=y_c, linestyle='--', alpha=0.7,
                label=f'$y = {y_c}$ (battu par $x = {y_c+0.5:.1f}$)')
    ax2.scatter([y_c + 0.5], [y_c + 0.5], s=80, color='red', zorder=5)
    ax2.annotate('×', xy=(y_c + 0.5, y_c + 0.5),
                 fontsize=14, color='red', ha='center')

ax2.plot(x_test, x_test, 'k-', alpha=0.5, label='$y = x$')
ax2.set_xlim(-0.5, 5)
ax2.set_ylim(-0.5, 5)
ax2.set_xlabel('$x$')
ax2.set_ylabel('$y$ candidat')
ax2.set_title(r'$\nexists y,\ \forall x,\ x < y$' '\n(tout candidat est dépassé, faux)',
              fontsize=12)
ax2.legend(fontsize=9)
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()
_images/9456da1bcd566e91d12adeb18d3cbeacac41d39c304e23112122e46f49aff011.png

Modes de raisonnement#

Raisonnement direct (modus ponens)#

Proposition 3 (Modus ponens)

\[\bigl(P \land (P \implies Q)\bigr) \implies Q\]

Proof. Table de vérité : la dernière colonne est identiquement 1 (tautologie).

\(P\)

\(Q\)

\(P \implies Q\)

\(P \land (P \implies Q)\)

Conclusion \(Q\)

1

1

1

1

1

1

0

0

0

— (hyp. fausse)

0

1

1

0

— (hyp. fausse)

0

0

1

0

— (hyp. fausse)

C’est le raisonnement déductif direct : si \(P\) est vraie et \(P \implies Q\) est prouvée, alors \(Q\) est établie.

Raisonnement par contraposée#

Proposition 4 (Contraposée)

\((P \implies Q) \iff (\lnot Q \implies \lnot P)\)

Proof. \(P \implies Q \iff \lnot P \lor Q \iff Q \lor \lnot P \iff \lnot(\lnot Q) \lor \lnot P \iff \lnot Q \implies \lnot P\).

Exemple 4

Montrons : si \(n^2\) est pair, alors \(n\) est pair.

Contraposée : si \(n\) est impair, alors \(n^2\) est impair.

Preuve de la contraposée : si \(n = 2k + 1\), alors \(n^2 = 4k^2 + 4k + 1 = 2(2k^2 + 2k) + 1\) est impair. \(\square\)

Raisonnement par l’absurde#

Proposition 5 (Preuve par l’absurde)

Pour montrer \(P\), on suppose \(\lnot P\) et on dérive une contradiction (une proposition fausse connue).

\[(\lnot P \implies \bot) \implies P\]

\(\bot\) désigne la contradiction.

Exemple 5

Montrons : \(\sqrt{2}\) est irrationnel.

Supposons par l’absurde \(\sqrt{2} = \frac{p}{q} \in \mathbb{Q}\) avec \(\text{pgcd}(p, q) = 1\). Alors \(2q^2 = p^2\), donc \(p^2\) est pair, donc \(p\) est pair (par la contraposée ci-dessus), soit \(p = 2k\). Substitution : \(2q^2 = 4k^2\), d’où \(q^2 = 2k^2\), donc \(q\) est pair. Contradiction : \(p\) et \(q\) sont tous deux pairs, contredisant \(\text{pgcd}(p, q) = 1\). \(\square\)

Raisonnement par récurrence#

Proposition 6 (Récurrence simple)

Soit \(P(n)\) un prédicat sur \(\mathbb{N}\) et \(k \in \mathbb{N}\). Si

  • Initialisation : \(P(k)\) est vraie,

  • Hérédité : \(\forall n \geq k,\ P(n) \implies P(n+1)\),

alors \(P(n)\) est vraie pour tout \(n \geq k\).

Proposition 7 (Récurrence forte (ou complète))

Soit \(P(n)\) un prédicat sur \(\mathbb{N}\). Si \(\forall n \in \mathbb{N},\ \bigl(\forall k < n,\ P(k)\bigr) \implies P(n)\), alors \(P(n)\) est vraie pour tout \(n \in \mathbb{N}\).

Remarque 6

La récurrence forte est utile quand la preuve de \(P(n)\) fait appel non seulement à \(P(n-1)\) mais à plusieurs rangs précédents (ex : Fibonacci, décomposition en facteurs premiers).

Exemple 6

Récurrence simple : \(\displaystyle\sum_{k=0}^{n} k = \frac{n(n+1)}{2}\).

  • Init (\(n=0\)) : \(0 = 0\). ✓

  • Hérédité : \(\sum_{k=0}^{n+1} k = \frac{n(n+1)}{2} + (n+1) = \frac{(n+1)(n+2)}{2}\). ✓

Récurrence forte : tout entier \(n \geq 2\) admet un diviseur premier.

  • Si \(n\) est premier : c’est lui-même.

  • Sinon : \(n = ab\) avec \(2 \leq a < n\), donc par hypothèse de récurrence forte appliquée à \(a\), \(a\) admet un diviseur premier, qui divise aussi \(n\).

Raisonnement par disjonction des cas#

Proposition 8 (Disjonction des cas)

Si \(P \lor Q\) est vraie, et si \((P \implies R)\) et \((Q \implies R)\), alors \(R\) est vraie.

Exemple 7

Montrons que \(n^2 + n\) est pair pour tout \(n \in \mathbb{N}\).

  • Cas 1 : \(n\) pair, \(n = 2k\). Alors \(n^2 + n = 4k^2 + 2k = 2(2k^2 + k)\) est pair.

  • Cas 2 : \(n\) impair, \(n = 2k+1\). Alors \(n^2 + n = (2k+1)(2k+2) = 2(2k+1)(k+1)\) est pair.

Dans les deux cas, \(n^2 + n\) est pair. \(\square\)

Syllogisme et transitivité#

Proposition 9 (Syllogisme)

\((P \implies Q) \land (Q \implies R) \implies (P \implies R)\)

Ce résultat (l’implication est transitive) permet d’enchaîner les implications dans une démonstration.

Analyse d’un énoncé mathématique#

Remarque 7

La pratique de la logique en mathématiques consiste à :

  1. Identifier les hypothèses (les \(P\) de l’implication \(P \implies Q\)).

  2. Identifier la conclusion (le \(Q\)).

  3. Choisir la stratégie : directe, contraposée, absurde, récurrence.

  4. Ne jamais utiliser la conclusion comme hypothèse (raisonnement circulaire).

  5. Vérifier chaque étape : chaque assertion doit découler logiquement de la précédente.

Hide code cell source

# Visualisation : stratégies de démonstration
fig, axes = plt.subplots(3, 1, figsize=(9, 14))

# --- Démonstration directe ---
ax = axes[0]
steps = ['Hypothèses\n$P$', 'Étape 1', 'Étape 2', 'Conclusion\n$Q$']
for i, step in enumerate(steps):
    color = '#2ecc71' if i == 0 else ('#e74c3c' if i == len(steps)-1 else '#3498db')
    ax.add_patch(FancyBboxPatch((0.1, 0.7 - 0.22*i), 0.8, 0.18,
                  boxstyle='round,pad=0.02', facecolor=color, alpha=0.8,
                  edgecolor='black'))
    ax.text(0.5, 0.79 - 0.22*i, step, ha='center', va='center',
            fontsize=10, color='white', fontweight='bold')
    if i < len(steps) - 1:
        ax.annotate('', xy=(0.5, 0.7 - 0.22*i), xytext=(0.5, 0.72 - 0.22*(i-1) if i > 0 else 0.69),
                   arrowprops=dict(arrowstyle='->', color='black', lw=2))

ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.axis('off')
ax.set_title('Démonstration directe\n$P \\Rightarrow Q$', fontsize=12, fontweight='bold')

# --- Raisonnement par l'absurde ---
ax2 = axes[1]
absurde_steps = ['But : montrer $P$', 'Supposer $\\neg P$', 'Dériver...', 'Contradiction !', 'Donc $P$ ✓']
absurde_colors = ['#2ecc71', '#e67e22', '#3498db', '#e74c3c', '#2ecc71']
for i, (step, col) in enumerate(zip(absurde_steps, absurde_colors)):
    ax2.add_patch(FancyBboxPatch((0.1, 0.78 - 0.17*i), 0.8, 0.14,
                   boxstyle='round,pad=0.02', facecolor=col, alpha=0.8,
                   edgecolor='black'))
    ax2.text(0.5, 0.85 - 0.17*i, step, ha='center', va='center',
             fontsize=10, color='white', fontweight='bold')

ax2.set_xlim(0, 1)
ax2.set_ylim(0, 1)
ax2.axis('off')
ax2.set_title('Raisonnement par l\'absurde', fontsize=12, fontweight='bold')

# --- Récurrence ---
ax3 = axes[2]
recur_steps = ['Initialisation\n$P(0)$ vraie', 'Hérédité\n$P(n) \\Rightarrow P(n+1)$',
               '$P(0), P(1), P(2), \\ldots$', '$P(n)$ vraie\n$\\forall n \\in \\mathbb{N}$']
recur_colors = ['#2ecc71', '#3498db', '#9b59b6', '#e74c3c']
for i, (step, col) in enumerate(zip(recur_steps, recur_colors)):
    ax3.add_patch(FancyBboxPatch((0.1, 0.7 - 0.22*i), 0.8, 0.18,
                   boxstyle='round,pad=0.02', facecolor=col, alpha=0.8,
                   edgecolor='black'))
    ax3.text(0.5, 0.79 - 0.22*i, step, ha='center', va='center',
             fontsize=10, color='white', fontweight='bold')

ax3.set_xlim(0, 1)
ax3.set_ylim(0, 1)
ax3.axis('off')
ax3.set_title('Raisonnement par récurrence', fontsize=12, fontweight='bold')

fig.suptitle('Principales stratégies de démonstration', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
_images/4bbbb70d735597ea23a90dd6dc0e816e0c50fbdf5bc9875ae5f288289770b83b.png

Implication, condition nécessaire, condition suffisante#

Définition 9 (Conditions nécessaire et suffisante)

Soit \(P \implies Q\) vraie.

  • On dit que \(P\) est une condition suffisante de \(Q\) : il suffit que \(P\) soit réalisée pour que \(Q\) le soit.

  • On dit que \(Q\) est une condition nécessaire de \(P\) : si \(Q\) est fausse, alors \(P\) ne peut pas être vraie.

  • Si \(P \iff Q\), on dit que \(P\) est une condition nécessaire et suffisante (CNS) de \(Q\).

Exemple 8

Dans \(\mathbb{N}\) :

  • « \(n\) est divisible par 4 » est une condition suffisante mais non nécessaire pour « \(n\) est pair » (contre-exemple : \(n = 2\)).

  • « \(n\) est pair » est une condition nécessaire mais non suffisante pour « \(n\) est divisible par 4 » (contre-exemple : \(n = 2\)).

  • « \(n\) est pair » \(\iff\) « \(n^2\) est pair » : c’est une CNS.

Hide code cell source

# Visualisation : CNS via diagrammes d'inclusion
fig, axes = plt.subplots(3, 1, figsize=(9, 14))

import matplotlib.patches as mpatches

def draw_implication(ax, title, A_label, B_label, A_subset_of_B, B_subset_of_A):
    """Dessine un diagramme d'Euler illustrant l'implication."""
    ax.set_xlim(-3, 3)
    ax.set_ylim(-2, 2)
    ax.set_aspect('equal')
    ax.axis('off')
    ax.set_title(title, fontsize=11, fontweight='bold')

    if A_subset_of_B and not B_subset_of_A:
        # A ⊂ B strict : A ⟹ B mais pas B ⟹ A
        circle_B = plt.Circle((0.3, 0), 1.6, color='#3498db', alpha=0.3, label=B_label)
        circle_A = plt.Circle((-0.3, 0), 0.8, color='#e74c3c', alpha=0.5, label=A_label)
        ax.add_patch(circle_B)
        ax.add_patch(circle_A)
        ax.text(-0.3, 0, A_label, ha='center', va='center', fontsize=12, fontweight='bold')
        ax.text(1.5, 0.8, B_label, ha='center', va='center', fontsize=12, fontweight='bold')
        ax.text(0, -1.8, f'{A_label}{B_label}\n(suffisant, non nécessaire)', ha='center', fontsize=10)
    elif A_subset_of_B and B_subset_of_A:
        # A = B : équivalence
        circle = plt.Circle((0, 0), 1.2, color='#2ecc71', alpha=0.5)
        ax.add_patch(circle)
        ax.text(0, 0.3, A_label, ha='center', va='center', fontsize=12, fontweight='bold')
        ax.text(0, -0.3, f'= {B_label}', ha='center', va='center', fontsize=12, fontweight='bold')
        ax.text(0, -1.8, f'{A_label}{B_label}\n(CNS)', ha='center', fontsize=10)
    else:
        # Ensembles quelconques
        circle_A = plt.Circle((-0.8, 0), 1.0, color='#e74c3c', alpha=0.4)
        circle_B = plt.Circle((0.8, 0), 1.0, color='#3498db', alpha=0.4)
        ax.add_patch(circle_A)
        ax.add_patch(circle_B)
        ax.text(-1.2, 0, A_label, ha='center', va='center', fontsize=12, fontweight='bold')
        ax.text(1.2, 0, B_label, ha='center', va='center', fontsize=12, fontweight='bold')
        ax.text(0, -1.8, 'Ni nécessaire, ni suffisante', ha='center', fontsize=10)

draw_implication(axes[0], 'Condition suffisante :\n« div. par 4 » ⟹ « pair »',
                 'div. 4', 'pair', True, False)
draw_implication(axes[1], 'CNS :\n« pair » ⟺ « $n^2$ pair »',
                 'pair', '$n^2$ pair', True, True)
draw_implication(axes[2], 'Indépendantes :\n« premier » et « pair »',
                 'premier', 'pair', False, False)

fig.suptitle('Conditions nécessaires, suffisantes et CNS', fontsize=13, fontweight='bold')
plt.tight_layout()
plt.show()
_images/55b21468b5deae82511da89530312ba2a853d97edde12534202cb298c33461ae.png

Récapitulatif : connecteurs et tables de vérité#

Hide code cell source

# Heatmap récapitulative de toutes les tables de vérité
fig, ax = plt.subplots(figsize=(12, 5))

P_vals = [1, 1, 0, 0]
Q_vals = [1, 0, 1, 0]

table_data = {
    r'$P$':             P_vals,
    r'$Q$':             Q_vals,
    r'$\neg P$':       [1-p for p in P_vals],
    r'$P \wedge Q$':     [p & q for p, q in zip(P_vals, Q_vals)],
    r'$P \vee Q$':      [p | q for p, q in zip(P_vals, Q_vals)],
    r'$P \oplus Q$':    [p ^ q for p, q in zip(P_vals, Q_vals)],
    r'$P \Rightarrow Q$': [int(not p or q) for p, q in zip(P_vals, Q_vals)],
    r'$P \Leftrightarrow Q$': [int(p == q) for p, q in zip(P_vals, Q_vals)],
    r'NAND':            [int(not (p and q)) for p, q in zip(P_vals, Q_vals)],
    r'NOR':             [int(not (p or q)) for p, q in zip(P_vals, Q_vals)],
}

import numpy as np
matrix = np.array(list(table_data.values())).T  # shape (4, 10)

im = ax.imshow(matrix, cmap='RdYlGn', vmin=0, vmax=1, aspect='auto')

ax.set_xticks(range(len(table_data)))
ax.set_xticklabels(list(table_data.keys()), fontsize=11)
rows = ['$P=1, Q=1$', '$P=1, Q=0$', '$P=0, Q=1$', '$P=0, Q=0$']
ax.set_yticks(range(4))
ax.set_yticklabels(rows, fontsize=11)

for i in range(4):
    for j in range(len(table_data)):
        val = matrix[i, j]
        ax.text(j, i, 'V' if val else 'F', ha='center', va='center',
                fontsize=12, fontweight='bold',
                color='black' if 0.3 < val < 0.7 else 'white' if val > 0.5 else 'black')

ax.set_title('Table de vérité récapitulative — tous les connecteurs', fontsize=13, fontweight='bold')
plt.tight_layout()
plt.show()
_images/cd34fb68f366a59fc03a9124d5647ce76660a507abe88074e7a176c3f13efd99.png