Aller au contenu principal

La programmation linéaire

Ce que ce chapitre apporte 9 points
  • Reconnaître un modèle linéaire, et ce qui n'en est pas un : un produit de variables, un quotient, un coût fixe.
  • Écrire un programme linéaire sous forme canonique et sous forme standard, avec ses variables d'écart.
  • Lire le domaine réalisable comme un polygone, et trouver l'optimum à l'un de ses sommets.
  • Dérouler le simplexe à la main : variables de base et hors base, variable entrante, variable sortante au plus petit rapport, pivot, critère d'arrêt.
  • Dire ce que fait une méthode de points intérieurs, et ce que vaut le simplexe dans le pire cas et en pratique.
  • Écrire un modèle avec PuLP, et le même avec linprog, sous la forme de matrices qu'un solveur attend.
  • Interpréter une solution : contraintes saturées, écarts, prix d'une ressource et jusqu'où il tient, problème impossible ou non borné.
  • Modéliser et résoudre un problème de transport, et constater que sa solution est entière.
  • Refuser d'arrondir une solution continue pour obtenir une solution entière.
Le chapitre précédent a montré ce qui rend un problème difficile, et pourquoi tout essayer cesse vite d'être possible. Une famille de problèmes se résout pourtant bien, même grands : ceux dont l'objectif et les contraintes s'écrivent comme des sommes de termes proportionnels aux variables, et dont les variables peuvent prendre des valeurs fractionnaires. Ce sont les programmes linéaires. Ce chapitre en pose le modèle et ses formes, le résout sur un exemple de Valdrome Mécanique à deux variables, d'abord par le dessin, puis par le simplexe, la méthode qui va de sommet en sommet, et apprend à lire la solution : ce qui reste, ce que vaut une unité de plus, ce que signifie une réponse « impossible ». Il confie ensuite le calcul à un solveur, avec PuLP, sur le même exemple et sur un problème de transport. Il constate enfin ce qui change quand les variables doivent être entières : c'est de là que partent les bornes du chapitre suivant.

Le modèle linéaire

Un programme linéaire décrit une décision par trois choses. Des variables de décision, qui prennent des valeurs réelles. Un objectif, à maximiser ou à minimiser, qui est une somme de termes proportionnels aux variables : c1x1+c2x2+⋯+cnxnc_1 x_1 + c_2 x_2 + \dots + c_n x_n. Des contraintes, qui comparent chacune une somme du même type à une constante, par « ≤ », « ≥ » ou « = ». Les nombres cjc_j et les coefficients des contraintes sont des données ; seules les variables sont à choisir.

Valdrome fabrique aussi deux références en lots, sans rapport avec les dix commandes de la semaine : des poulies, qui rapportent 520 € le lot, et des bielles, 240 € le lot. Un lot de poulies demande 5 heures de tournage et 3 heures de fraisage ; un lot de bielles, 2 heures de tournage et 2 heures de fraisage. Le tournage dispose de 15 heures, le fraisage de 12. Les variables xx et yy sont les nombres de lots de poulies et de bielles.

max⁡  520x+240yavec5x+2y≤15  (tournage),3x+2y≤12  (fraisage),x≥0,  y≥0.\max\; 520x + 240y \qquad \text{avec} \qquad 5x + 2y \le 15 \;(\text{tournage}), \quad 3x + 2y \le 12 \;(\text{fraisage}), \quad x \ge 0,\; y \ge 0.

Ce modèle suppose trois choses, qu'il vaut mieux dire. La proportionnalité : deux lots consomment deux fois les heures d'un lot, et rapportent deux fois sa marge. L'additivité : les heures des poulies et des bielles s'ajoutent, sans gêne ni économie de l'une à l'autre. La divisibilité : un lot et demi a un sens. Quand l'une des trois tombe, le modèle n'est plus linéaire, ou plus continu.

Ce qui n'est pas linéaire

  • Un produit de variables, ou une puissance. Une remise qui grandit avec la quantité vendue fait de la marge d'un lot une fonction de xx, et la marge totale contient x2x^2.
  • Un quotient. « Au moins un tiers des lots sont des poulies » s'écrit d'abord xx+y≥13\frac{x}{x + y} \ge \frac{1}{3}. Le total x+yx + y est positif : multiplier les deux côtés par lui ne change pas le sens de l'inégalité, et la règle devient 3x≥x+y3x \ge x + y, soit 2x−y≥02x - y \ge 0, qui est linéaire. Cette linéarisation se fait à la main, aucun solveur ne la fait.
  • Un coût fixe. Un réglage de 300 € payé dès qu'au moins un lot de bielles est lancé ne dépend pas du nombre de lots : le coût saute de 0 à 300 quand yy quitte 0. Ni 300y300y ni aucune autre somme proportionnelle ne l'écrit.
  • Un maximum, une valeur absolue, une condition. « Le plus long des deux temps », « l'écart à la cible, quel que soit son sens », « si l'on fait des poulies, alors on fait aussi des bielles ».

Exiger des valeurs entières n'est pas une non-linéarité, mais cela sort aussi du cadre : c'est la programmation linéaire en nombres entiers, bien plus dure, qui occupe le chapitre 4. Le chapitre 5 traite des modèles qui sortent du cadre linéaire.

Vérification rapideon peut se reprendre

1.Laquelle de ces contraintes est linéaire ?

2.Le réglage de 300 €, payé dès qu'un lot de bielles est lancé, peut-il entrer dans l'objectif sous la forme −300y-300y ?

Forme canonique, forme standard

Un même problème s'écrit de plusieurs façons, et chaque méthode de résolution en attend une précise. Deux formes reviennent partout.

Les deux formes d'un programme linéaire

La forme canonique maximise, sous des contraintes toutes en « ≤ », avec des variables positives ou nulles. Les lots y sont déjà.

La forme standard n'a que des égalités, et des variables positives ou nulles. Chaque contrainte « ≤ » reçoit une variable d'écart, positive, qui est ce qui reste de la ressource :

5x+2y+e1=15,3x+2y+e2=12,x,y,e1,e2≥0.5x + 2y + e_1 = 15, \qquad 3x + 2y + e_2 = 12, \qquad x, y, e_1, e_2 \ge 0.

Au plan de 1 lot de poulies et 2 lots de bielles, il reste e1=15−(5+4)=6e_1 = 15 - (5 + 4) = 6 heures de tournage et e2=12−(3+4)=5e_2 = 12 - (3 + 4) = 5 heures de fraisage. Une contrainte est saturée quand son écart vaut 0 : la ressource est épuisée. Un écart ne peut pas être négatif, ce serait consommer plus qu'il n'y a. Une contrainte « ≥ » reçoit, elle, une variable d'excédent, retranchée : « au moins 2 lots en tout », x+y≥2x + y \ge 2, devient x+y−s=2x + y - s = 2 avec s≥0s \ge 0, ce qui dépasse le minimum.

Tout programme linéaire se ramène à l'une ou l'autre forme, par des conversions qui ne changent pas l'ensemble des plans possibles, seulement leur écriture.

ÉcritSe ramène à
minimiser ffmaximiser −f-f : le même plan, la valeur au signe près
a1x1+⋯+anxn≥ba_1 x_1 + \dots + a_n x_n \ge b−a1x1−⋯−anxn≤−b-a_1 x_1 - \dots - a_n x_n \le -b
a1x1+⋯+anxn=ba_1 x_1 + \dots + a_n x_n = bdeux inégalités, « ≤ » et « ≥ » (en forme canonique)
une variable de signe quelconquela différence de deux variables positives, x=x+−x−x = x^+ - x^-
une borne, x≤10x \le 10une contrainte de plus, ou une borne que le solveur traite à part

Pourquoi un solveur n'accepte souvent qu'une forme. Son code ne travaille que sur l'écriture de son algorithme. Le simplexe, plus bas, raisonne sur la forme standard. La fonction linprog de scipy reçoit un problème à minimiser, des matrices de « ≤ » et d'« = », et des bornes : un problème à maximiser, ou une contrainte « ≥ », doit d'abord y être converti par celui qui l'écrit. Ces conversions sont faciles et propices aux erreurs de signe. Une bibliothèque de modélisation comme PuLP les fait elle-même, et c'est l'une de ses raisons d'être : la section Écrire un modèle avec PuLP montre les deux écritures du même modèle.

Les écarts d'un plan

  • 1.

    Au plan de 2 lots de poulies et 2 lots de bielles, combien d'heures de tournage restent-elles ?

  • 2.

    Et combien d'heures de fraisage ?

  • 3.

    Au plan de 3 lots de poulies et un demi-lot de bielles, que vaudrait l'écart de tournage ?

La résolution graphique

À deux variables, un programme linéaire se dessine. Le détail de la méthode graphique est traité dans le chapitre optimiser sous contraintes du parcours Mathématiques appliquées ; on ne le refait pas, on s'en sert.

Chaque contrainte est un demi-plan, et leur intersection est un polygone, le domaine réalisable. La marge 520x+240y520x + 240y est constante sur une droite, et cette droite glisse parallèlement à elle-même quand la marge change. La figure dessine le polygone et cette droite. Les lots entiers réalisables sont les points pleins de la grille. Avant toute explication, chercher à la main le lot le plus rentable : glisser la droite vers les grandes marges, choisir un point plein, puis demander à la figure si c'est le meilleur.

Polygone des contraintesmaximiser 520 x + 240 y
Droite
520 x + 240 y = 800 €
Point retenu
aucun

Glisser la droite orange, ou la régler au curseur. Zone grisée : interdite par une contrainte. Points pleins : points entiers réalisables. Au clavier : Tab jusqu'au tracé, flèches pour parcourir la grille, Entrée pour retenir un point, Page haut ou bas pour avancer la droite.

Chercher le point entier le plus rentable : faire glisser la droite vers les grandes marges, puis retenir un point.
Les lots de Valdrome. Zone grisée : interdite par une contrainte. Losanges : sommets du polygone.
À manipuler
Glisser la droite orange, ou la régler au curseur, et lire sa marge écrite le long du trait. Observer où elle quitte le polygone en montant : un coin, jamais le milieu. Retenir un point plein, vérifier, puis demander la suite : le sommet continu, l'arrondi de ce sommet, le meilleur point entier. Changer ensuite la marge d'un des deux produits pour incliner la droite : le sommet touché en dernier change parfois, et l'arrondi ne tombe pas toujours au même endroit. Chercher une pente pour laquelle le sommet continu est déjà entier, donc son arrondi optimal, et une pente pour laquelle il reste nettement en dessous.

L'optimum est atteint au dernier point du polygone que la droite touche en glissant vers les marges croissantes. C'est un cas d'une propriété générale, qui fonde tout ce chapitre.

L'optimum est en un sommet

Si un programme linéaire a un optimum, l'un au moins de ses points optimaux est un sommet du domaine réalisable, un point où se croisent autant de bords que de variables : deux droites, à deux variables.

La raison tient à la linéarité de l'objectif. Le long d'un segment, il varie en ligne droite : un point du milieu ne fait jamais mieux que le meilleur des deux bouts. Deux nuances. Quand la droite de l'objectif est parallèle à un côté du polygone, tout ce côté est optimal, ses deux sommets compris. Et un programme linéaire peut n'avoir aucun optimum, s'il est impossible ou non borné : la section Interpréter une solution y revient.

Il suffit donc d'examiner les sommets. Le bloc suivant les trouve comme on le ferait à la main : il croise les bords deux à deux, contraintes et axes, garde les points qui respectent toutes les contraintes, et calcule la marge de chacun. Il énumère aussi les plans en lots entiers, peu nombreux ici.

main.py
Sortie
>_ Prêt à exécuter…

Six paires de bords, quatre sommets : (0 ; 0), (3 ; 0), (1,5 ; 3,75) et (0 ; 6), de marges 0, 1 560, 1 680 et 1 440 €. Les deux autres croisements, où le tournage coupe l'axe des bielles et le fraisage celui des poulies, sortent du polygone. Cette méthode ne passe pas à l'échelle. À nn variables et mm contraintes, un sommet est le croisement de nn bords parmi m+nm + n, et il y a (m+nn)\binom{m+n}{n} choix à essayer : avec 20 variables et 20 contraintes, (4020)=137 846 528 820\binom{40}{20} = 137\,846\,528\,820, plus de cent milliards de systèmes à résoudre. Il faut une méthode qui n'essaie pas tout : c'est le simplexe, à la section suivante.

Le meilleur sommet est à x=1,5x = 1{,}5 lot de poulies et y=3,75y = 3{,}75 lots de bielles, pour 1 680 €. Un lot et demi, trois lots trois quarts : ce n'est pas une décision d'atelier. Si les lots sont indivisibles, l'optimum tombe à 1 560 €, avec 3 lots de poulies et aucun de bielles, le meilleur des 16 plans en lots entiers. Et le point entier obtenu en arrondissant le sommet continu vers le bas, 1 lot de poulies et 3 de bielles, ne rapporte que 1 240 € : arrondir n'est pas résoudre, l'optimum entier est ici dans un autre coin du polygone.

Deux leçons pour la suite. La première : le continu donne une borne (1 680 €), et le problème en nombres entiers est plus dur que son relâché continu. On sait résoudre le second pour des milliers de variables ; le premier reste difficile en général. La seconde : un solveur en nombres entiers traite directement le cas où l'on impose l'entier. PuLP le fait plus bas en un mot, et le chapitre 4 explique comment il s'y prend.

Vérification rapideon peut se reprendre

La solution de la relaxation continue d'un problème de maximisation vaut 1 680, et la meilleure solution en nombres entiers vaut 1 560. Laquelle de ces phrases est fausse ?

Le simplexe, de sommet en sommet

Le simplexe, mis au point par George Dantzig en 1947, part d'un sommet, ici l'origine, et passe à un sommet voisin, relié au premier par un côté du polygone, à condition que la marge y soit meilleure. Il recommence jusqu'à un sommet dont aucun voisin ne fait mieux. Ce sommet est l'optimum, parce que le polygone n'a pas de creux : il est convexe, le segment qui joint deux de ses points y reste tout entier, si bien qu'un sommet qu'aucun voisin n'améliore est le meilleur de tous. C'est ce qui sépare un programme linéaire des problèmes de la recherche locale, où un optimum local n'est que local.

La figure déroule le simplexe sur les lots. Chaque clic sur « Pivot suivant » fait un pas, et le tableau sous le dessin est celui que la suite de cette section apprend à lire.

Le simplexe, de sommet en sommetmaximiser 520 x + 240 y
Sommet courant
(0 ; 0), après 0 pivot
Marge
0 €

Zone grisée : interdite par une contrainte. Point plein : le sommet courant ; flèches : les pivots déjà faits ; tirets orange : la droite de la marge du sommet courant. Chaque pivot fait entrer une variable de coefficient positif dans la ligne de la marge, et sortir la variable de base du plus petit rapport.

Tableau du sommet (0 ; 0)
basexye₁e₂valeur
e₁521015
e₂320112
marge520240000

Écarts : e₁ Tournage, e₂ Fraisage. Hors base, une variable vaut 0. Dans la ligne de la marge, un coefficient positif (en couleur) est ce que rapporte une unité de cette variable.

ou faire entrer :
À l'origine, x et y sont hors base. Prévoir l'entrante et la sortante, puis cliquer sur « Pivot suivant ».
Le simplexe sur les lots de Valdrome, depuis l'origine. Point plein : le sommet courant ; flèches : les pivots faits.
À manipuler
Cliquer sur « Pivot suivant » et suivre le point plein : il passe d'un sommet à un voisin, le long d'un côté du polygone, et la droite orange de la marge monte à chaque pivot. Lire sous le dessin le message du pivot (la variable qui entre, les rapports, la variable qui sort) et le tableau du nouveau sommet. Le bouton se grise quand la figure peut prouver qu'elle est arrivée : lire pourquoi. Recommencer, puis faire entrer y au premier pas au lieu de x : le chemin longe l'autre côté du polygone, par (0 ; 6), et arrive au même sommet, en autant de pivots.

Le tableau : variables de base et hors base

Le simplexe travaille sur la forme standard des lots, quatre variables et deux égalités :

5x+2y+e1=15,3x+2y+e2=12.5x + 2y + e_1 = 15, \qquad 3x + 2y + e_2 = 12.

Deux équations fixent deux inconnues : si deux des quatre variables sont mises à 0, les deux autres se calculent. À chaque sommet, les variables se partagent ainsi en deux groupes. Les variables hors base, autant que de variables de décision, valent 0. Les variables de base, une par contrainte, se calculent par les égalités. Choisir les variables hors base, c'est choisir les deux bords qui se croisent au sommet : x=0x = 0 et y=0y = 0 à l'origine ; e1=0e_1 = 0, le tournage épuisé, et y=0y = 0 au sommet (3 ; 0).

À l'origine, xx et yy sont hors base, et chaque écart vaut toutes les heures de son atelier. Écrire chaque variable de base en fonction des variables hors base donne un dictionnaire, et la marge s'y écrit de même, notée zz :

e1=15−5x−2y,e2=12−3x−2y,z=0+520x+240y.e_1 = 15 - 5x - 2y, \qquad e_2 = 12 - 3x - 2y, \qquad z = 0 + 520x + 240y.

Le tableau range les mêmes nombres : une ligne par égalité, une ligne pour la marge, une colonne par variable, et la valeur de chaque variable de base à droite.

basexxyye1e_1e2e_2valeur
e1e_1521015
e2e_2320112
marge520240000

La ligne de la marge porte, sous chaque variable, ce que rapporterait une unité de cette variable à partir du sommet courant. On l'appelle aussi la ligne des coûts réduits.

Un pivot, pas à pas

Le pas du simplexe, le pivot
  1. La variable entrante. Une variable hors base dont le coefficient dans la ligne de la marge est positif : la faire monter améliore la marge. La règle de Dantzig prend le plus grand coefficient.
  2. La variable sortante. Faire monter l'entrante fait baisser des variables de base. Chacune s'annule à un rapport : sa valeur, divisée par son coefficient dans la colonne de l'entrante, quand ce coefficient est positif. La sortante est celle du plus petit rapport, la première à atteindre 0 : monter plus haut la rendrait négative, et le plan sortirait du polygone. Une ligne où l'entrante a un coefficient nul ou négatif ne limite rien.
  3. Le pivot. L'entrante prend la place de la sortante dans la base : sa ligne se résout en elle, puis on la remplace partout ailleurs, ligne de la marge comprise. Le plan est passé au sommet voisin.
  4. Le critère d'arrêt. Quand plus aucun coefficient de la ligne de la marge n'est positif, le sommet courant est l'optimum.

Premier pivot. À l'origine, xx entre, avec le plus grand coefficient, 520 € par lot. yy reste à 0, et les écarts valent e1=15−5xe_1 = 15 - 5x et e2=12−3xe_2 = 12 - 3x. Le premier s'annule à x=15/5=3x = 15 / 5 = 3, le second à x=12/3=4x = 12 / 3 = 4 : les rapports sont 3 et 4, et e1e_1 sort, au plus petit. La ligne du tournage se résout en xx, x=3−25y−15e1x = 3 - \frac{2}{5}y - \frac{1}{5}e_1, et remplace xx dans les deux autres :

e2=3−45y+35e1,z=1 560+32 y−104 e1.e_2 = 3 - \tfrac{4}{5}y + \tfrac{3}{5}e_1, \qquad z = 1\,560 + 32\,y - 104\,e_1.

Le plan est en (3 ; 0), pour 1 560 €. Le coefficient de yy n'est plus 240, mais 32 : au sommet (3 ; 0), le tournage est épuisé, et un lot de bielles de plus prend 2 heures de tournage qu'il faut retirer aux poulies, soit deux cinquièmes de lot. Il rapporte 240−25×520=32240 - \frac{2}{5} \times 520 = 32 €. C'est encore positif : un pivot de plus s'impose.

Second pivot. yy entre, seul candidat. e1e_1 reste à 0, si bien que x=3−25yx = 3 - \frac{2}{5}y s'annulerait à y=7,5y = 7{,}5, et e2=3−45ye_2 = 3 - \frac{4}{5}y à y=3,75y = 3{,}75 : e2e_2 sort. Après réécriture :

y=3,75+34e1−54e2,x=1,5−12e1+12e2,z=1 680−80 e1−40 e2.y = 3{,}75 + \tfrac{3}{4}e_1 - \tfrac{5}{4}e_2, \qquad x = 1{,}5 - \tfrac{1}{2}e_1 + \tfrac{1}{2}e_2, \qquad z = 1\,680 - 80\,e_1 - 40\,e_2.
basexxyye1e_1e2e_2valeur
xx1012\tfrac{1}{2}−12-\tfrac{1}{2}1,5
yy01−34-\tfrac{3}{4}54\tfrac{5}{4}3,75
marge00−80−401 680

Plus aucun coefficient positif : faire monter e1e_1 ou e2e_2, c'est laisser des heures inutilisées, et la marge ne peut que baisser. Le simplexe s'arrête en (1,5 ; 3,75), pour 1 680 €, après deux pivots et trois sommets visités ; le sommet (0 ; 6) ne l'a jamais été. Les deux nombres −80 et −40 resservent plus bas : ce sont, au signe près, ce que vaut une heure de plus de chaque atelier.

La règle d'entrée choisit le chemin, pas l'arrivée. Faire entrer yy d'abord, comme la figure le permet, mène par (0 ; 6), pour 1 440 €, puis au même sommet (1,5 ; 3,75). Quand trois bords ou plus passent par un même sommet, un rapport peut être nul : le pivot change la base sans déplacer le plan. Un tel sommet est dit dégénéré ; l'atelier en rencontre un dès son premier pivot. Les solveurs ajoutent des règles qui empêchent alors de tourner en rond.

Le bloc suivant fait les pivots en Python, sans bibliothèque, en fractions exactes : 25\frac{2}{5} s'y affiche 2/5. Il prend le problème sous forme canonique, des marges c, une matrice A et des seconds membres b, et construit lui-même la forme standard. Il affiche chaque tableau, l'entrante, les rapports, la sortante, et le sommet atteint.

main.py
Sortie
>_ Prêt à exécuter…

Le bloc retrouve les deux pivots et les trois tableaux de la section. Il ne dépend pas du nombre de variables. Lui donner un troisième produit, à 660 € le lot pour 5 heures de tournage et 8 de fraisage, demande une colonne de plus : c=[520, 240, 660], A=[[5, 2, 5], [3, 2, 8]] et noms=["x", "y", "w"]. Le simplexe le fait entrer d'abord, avec le plus grand coefficient, passe par deux sommets où il est fabriqué, puis l'abandonne au troisième pivot, et finit au même plan, pour la même marge. Le dernier tableau porte −60 sous lui : aux prix de 80 € l'heure de tournage et 40 € l'heure de fraisage, ses 5 et 8 heures valent 80×5+40×8=72080 \times 5 + 40 \times 8 = 720 €, plus que les 660 € qu'il rapporte.

Vérification rapideon peut se reprendre

1.Au sommet (3 ; 0), le coefficient de yy dans la ligne de la marge vaut 32, et non plus 240. Pourquoi ?

2.Au second pivot, yy entre, et les rapports valent 7,5 pour xx et 3,75 pour e2e_2. Que se passerait-il si yy montait jusqu'à 7,5 ?

3.Au dernier tableau, la ligne de la marge vaut 0, 0, −80 et −40. Que conclure ?

La ligne de la marge qui ne bouge pas

Une seule étape est fausse. Désigner laquelle.

Un camarade mène le simplexe sur les lots, depuis l'origine, avec la règle du plus grand coefficient.

Les points intérieurs

Le simplexe longe le bord du polygone. Une méthode de points intérieurs prend l'autre chemin : elle part d'un point à l'intérieur du domaine, et avance vers l'optimum à travers lui, en se tenant d'abord à distance des bords, puis en s'en approchant à mesure qu'elle relâche cette distance. Elle ne visite aucun sommet en route. Son nombre d'étapes croît au plus comme un polynôme de la taille du problème, depuis les travaux de Narendra Karmarkar, en 1984. Elle finit tout près du sommet optimal, et une dernière étape l'y amène exactement.

Dans la page, PuLP confie le calcul à HiGHS, un solveur libre, par la fonction linprog de scipy et sa méthode "highs". D'après la documentation de scipy, cette méthode choisit d'elle-même entre deux algorithmes de HiGHS : "highs-ds", un simplexe dual révisé, une variante du simplexe de cette section, et "highs-ipm", une méthode de points intérieurs, munie de cette étape finale qui la rend aussi précise qu'un simplexe. Le plus rapide des deux dépend du problème.

Le pire cas, et la pratique

Le simplexe est rapide sur les problèmes de la pratique, et pourtant son pire cas est exponentiel. En 1972, Victor Klee et George Minty ont construit des problèmes à nn variables, des cubes déformés, sur lesquels la règle du plus grand coefficient visite tous les sommets, 2n2^n, en 2n−12^n - 1 pivots. Des exemples du même genre ont été construits depuis pour les autres règles d'entrée courantes, et l'on ne connaît aucune règle dont on ait prouvé qu'elle reste polynomiale dans tous les cas. Les problèmes de la pratique se résolvent pourtant en un nombre de pivots modeste, qui croît à peu près comme leur nombre de contraintes. Le bloc suivant compte les pivots sur les deux sortes de problèmes ; il ne démontre rien, il montre.

main.py
Sortie
>_ Prêt à exécuter…

Sur le cube de Klee et Minty, chaque variable de plus double le nombre de pivots : 3 à deux variables, 1 023 à dix, 4 095 à douze. Sur vingt problèmes tirés au hasard à chaque taille (graine 1), le plus long n'en demande jamais plus de 7. Le pire cas existe, mais il ne se rencontre guère hors des exemples construits pour lui.

La programmation linéaire est pourtant un problème facile, au sens du chapitre 2 : sa version de décision est dans P, et l'on sait la résoudre en un temps polynomial depuis 1979, par la méthode de l'ellipsoïde de Leonid Khachiyan, lente en pratique, puis par les points intérieurs. Sa version en nombres entiers, elle, est NP-difficile. Le simplexe illustre la différence entre la complexité dans le pire cas et le comportement observé : un algorithme exponentiel dans le pire cas peut rester le plus employé.

Écrire un modèle avec PuLP

Un solveur résout ; une bibliothèque de modélisation écrit. PuLP, une bibliothèque Python, laisse écrire le modèle presque comme sur le papier, maximiser, « ≥ », une expression de chaque côté, et le traduit dans la forme que le solveur attend.

BesoinÉcriture
Un problème à maximiser (ou à minimiser)prob = LpProblem("lots", LpMaximize) (ou LpMinimize, le défaut)
Une variable positive, continuex = LpVariable("poulies", lowBound=0)
Une variable bornée, ou entièreLpVariable("nuit", lowBound=5, upBound=30), cat="Integer"
L'objectifprob += 520 * x + 240 * y : une expression seule
Une contrainte nomméeprob += 5 * x + 2 * y <= 15, "tournage" : une comparaison, <=, >= ou ==
Une somme sur un ensemblelpSum(cout[i] * q[i] for i in routes)
Résoudreprob.solve()
Le statut de la résolutionLpStatus[prob.status] : "Optimal", "Infeasible", "Unbounded"
La valeur d'une variable, de l'objectifx.varValue, value(prob.objective)
L'écart, le prix d'une contrainteprob.constraints["tournage"].slack, .pi

Le bloc suivant écrit les lots. Le premier bloc de la page qui importe pulp met environ deux secondes à le charger ; les suivants vont vite.

main.py
Sortie
>_ Prêt à exécuter…

Le solveur rend le sommet (1,5 ; 3,75) et ses 1 680 €, le statut "Optimal", et pour chaque contrainte son écart et son prix, que la section suivante interprète. Avec ENTIERS = True, le même modèle exige des lots entiers, et rend (3 ; 0), pour 1 560 € : le meilleur point entier de la figure. Les prix, eux, n'ont plus de sens en nombres entiers, et se lisent sur le problème continu.

Le même modèle, hors de cette page
PuLP s'installe aussi sur un ordinateur, où il appelle par défaut un autre programme de résolution, CBC. Le modèle s'écrit de la même façon, et le statut et la valeur optimale sont les mêmes. Quand plusieurs plans sont optimaux à égalité, CBC peut en rendre un autre que celui de la page, pour la même valeur.

La forme brute. La fonction linprog de scipy reçoit le même problème sous la forme qu'attend le solveur : des matrices. Il faut alors faire soi-même les conversions de la forme canonique : linprog minimise, on lui passe la marge changée de signe, et l'on change le signe de ce qu'il rend.

main.py
Sortie
>_ Prêt à exécuter…

linprog n'accepte qu'une forme : minimiser c⋅xc \cdot x sous Aub x≤bubA_{ub}\,x \le b_{ub}, Aeq x=beqA_{eq}\,x = b_{eq}, et des bornes. Une contrainte « ≥ » s'y écrit en changeant tous ses signes, et une égalité passe par A_eq et b_eq. C'est exactement ce que PuLP fabrique à partir du modèle écrit tel qu'on le dit : une colonne par variable, une ligne par contrainte, les signes remis dans le bon sens. Les deux écritures rendent le même plan, les mêmes écarts et les mêmes prix ; la seconde montre ce que voit le solveur, la première se relit et se modifie sans erreur de signe.

Interpréter une solution

Le plan, (1,5 ; 3,75) pour 1 680 €, n'est que la moitié de la réponse. La sortie d'un solveur dit aussi ce qui le fixe, ce qui reste, et ce que vaudrait une unité de plus de chaque ressource.

Contraintes saturées, et ce qui reste

Au plan (1,5 ; 3,75), le tournage consomme 5×1,5+2×3,75=155 \times 1{,}5 + 2 \times 3{,}75 = 15 heures et le fraisage 3×1,5+2×3,75=123 \times 1{,}5 + 2 \times 3{,}75 = 12 : les deux contraintes sont saturées, et leurs écarts, que PuLP donne par slack, sont nuls. Ce sont elles qui fixent le plan : un sommet, c'est un choix de contraintes saturées, celles dont l'écart a quitté la base au fil des pivots. Une contrainte non saturée laisse un reste, et ne limite rien. PuLP écrit l'écart comme le second membre moins la valeur de l'expression : il est positif pour une « ≤ » non saturée, et négatif pour une « ≥ » non saturée. Avec une commande d'au moins 5 lots en tout, x+y≥5x + y \ge 5, le plan (1,5 ; 3,75) en fait 5,25, et PuLP donne à cette contrainte un écart de −0,25.

Ce que vaut une unité de plus

Le prix d'une ressource

Le prix d'une ressource, ou prix dual, est ce que gagnerait l'objectif optimal si la ressource augmentait d'une unité, le plan étant recalculé. Une ressource qui n'est pas saturée a un prix nul : il en reste déjà, une unité de plus ne sert à rien.

Trois lectures donnent le même nombre : refaire le calcul avec une unité de plus ; lire contrainte.pi dans PuLP ; lire le dernier tableau du simplexe, où il figure au signe près, dans la ligne de la marge, sous l'écart de la contrainte.

Avec 16 heures de tournage, le tournage et le fraisage restent saturés, et le sommet glisse le long de la droite du fraisage, jusqu'en (2 ; 3), pour 1 760 € : 80 € de plus. C'est le −80 du dernier tableau, et le pi du tournage que PuLP affiche. Une heure de fraisage vaut de même 40 €. Si un sous-traitant propose une heure de tournage à 60 €, l'acheter laisse 20 € de gain ; à 100 €, elle coûte plus qu'elle ne rapporte. Le prix d'une ressource est le prix au-delà duquel une unité de plus ne vaut plus la peine d'être achetée.

Au signe près, le prix dit aussi ce que coûte une unité de moins. Le dernier dictionnaire le montre : z=1 680−80 e1−40 e2z = 1\,680 - 80\,e_1 - 40\,e_2, et une heure de tournage laissée libre, e1=1e_1 = 1, coûte 80 €.

Jusqu'où le prix tient

Le prix ne vaut que tant que les mêmes contraintes restent saturées au sommet optimal. Avec TT heures de tournage, ce sommet est le croisement des droites du tournage et du fraisage : x=T−122x = \frac{T - 12}{2} et y=60−3T4y = \frac{60 - 3T}{4}. Il reste dans le polygone tant que x≥0x \ge 0 et y≥0y \ge 0, c'est-à-dire pour TT entre 12 et 20 heures. Au-delà de 20 heures, plus aucune bielle n'est fabriquée : quatre lots de poulies épuisent le fraisage, il reste du tournage, et une heure de plus ne rapporte plus rien. C'est l'idée de l'analyse de sensibilité : chaque prix a sa plage de validité, et l'étudier dit jusqu'où une décision prise sur un prix reste bonne. Beaucoup de solveurs calculent ces plages ; à deux variables, elles se trouvent à la main, comme ici.

Le prix d'une heure de fraisage

  • 1.

    Avec 13 heures de fraisage au lieu de 12, le tournage et le fraisage restent saturés. Combien de lots de poulies le nouveau sommet compte-t-il ?

  • 2.

    Et combien de lots de bielles ?

  • 3.

    Quelle marge ce sommet rapporte-t-il, en euros ?

  • 4.

    Que rapporte donc la treizième heure de fraisage, en euros ?

  • 5.

    Jusqu'à combien d'heures de fraisage ce prix tient-il ? Chercher où le sommet quitte le polygone en atteignant l'axe des bielles.

Les cas sans réponse

Un programme linéaire n'a pas toujours d'optimum, et le solveur le dit par son statut.

  • Impossible. Aucun plan ne respecte toutes les contraintes : elles se contredisent. Un client qui exige au moins 7 lots en tout, x+y≥7x + y \ge 7, demande l'impossible : le polygone ne contient aucun plan de plus de 6 lots, au sommet (0 ; 6). PuLP répond "Infeasible", et varValue vaut None. C'est une réponse, pas une panne : la demande, telle qu'elle est écrite, ne peut pas être tenue, et c'est au métier de décider quelle contrainte desserrer.
  • Non borné. L'objectif peut croître sans fin. Dans un problème réel, c'est presque toujours une contrainte oubliée, ou écrite à l'envers : les deux contraintes des lots écrites en « ≥ » laissent produire autant de lots qu'on veut. PuLP répond "Unbounded". Le simplexe le voit quand une entrante n'a aucun rapport : aucune ligne ne l'arrête, et le bloc du simplexe l'écrit.
main.py
Sortie
>_ Prêt à exécuter…

Le statut se lit avant la solution : sur un problème impossible ou non borné, il n'y a pas de plan à lire, et un programme qui affiche x.varValue sans regarder le statut affiche None, ou plante sur un calcul qui en dépend.

Le problème de transport

Trois carrières, au nord, au centre et au sud d'un département, livrent du gravier à quatre chantiers : un pont, une gare, un lycée et un stade. Chaque carrière peut fournir au plus son tonnage de la semaine ; chaque chantier doit recevoir exactement le sien ; chaque tonne transportée coûte selon la route. Où prendre le gravier de chaque chantier pour payer le moins de transport ?

Coût (€ par tonne)PontGareLycéeStadeOffre (t)
Nord4697120
Centre5346110
Sud853490
Demande (t)70906080

Une variable par route : qijq_{ij}, les tonnes livrées de la carrière ii au chantier jj, douze variables positives. Le modèle s'écrit

min⁡∑i,jcij qijavec∑jqij≤oi pour chaque carrieˋre,∑iqij=dj pour chaque chantier,\min \sum_{i,j} c_{ij}\, q_{ij} \qquad \text{avec} \qquad \sum_j q_{ij} \le o_i \ \text{pour chaque carrière}, \qquad \sum_i q_{ij} = d_j \ \text{pour chaque chantier},

où cijc_{ij} est le coût d'une tonne sur la route, oio_i l'offre de la carrière et djd_j la demande du chantier. Les offres totalisent 320 tonnes pour 300 demandées : une carrière au moins gardera du gravier, et la contrainte d'offre est une « ≤ ». Si l'offre totale était inférieure à la demande, le problème serait impossible.

Le modèle a la même forme quelle que soit la taille : une contrainte par carrière, une par chantier, écrites en parcourant les dictionnaires, et lpSum pour les sommes. Le bloc le résout, puis le résout encore sur trente problèmes tirés au hasard (graine 1), et compte ceux dont le plan est entier.

main.py
Sortie
>_ Prêt à exécuter…

Le plan coûte 1 160 € et emprunte six routes : le Nord livre le pont (70 t) et une partie du stade (30 t), le Centre la gare (90 t) et une partie du lycée (20 t), le Sud le reste du lycée (40 t) et du stade (50 t). Le Nord garde 20 tonnes : son offre n'est pas saturée, et son prix est nul. Les prix se lisent comme ceux des lots. Une tonne de plus livrée au stade coûterait 7 € : elle viendrait du Nord, la seule carrière qui ait du reste, par la route à 7 €. Une tonne de plus à la carrière du Centre ferait économiser 2 € : elle irait au lycée à la place d'une tonne du Sud, qui irait au stade à la place d'une tonne du Nord, soit 4−3+4−7=−24 - 3 + 4 - 7 = -2 €.

Une solution entière, sans l'avoir exigé. Les variables sont continues, et le plan est pourtant fait de tonnes entières ; sur les trente problèmes tirés au hasard, les trente plans le sont aussi. Ce n'est pas un hasard : quand les offres et les demandes sont entières, tous les sommets du polygone d'un problème de transport ont des coordonnées entières. La raison tient à la forme des contraintes, où chaque variable figure exactement deux fois, avec le coefficient 1, une fois dans l'offre de sa carrière et une fois dans la demande de son chantier ; la démonstration sort du cadre de ce chapitre. Le simplexe, qui va de sommet en sommet, rend donc un plan entier. Les lots de poulies et de bielles n'ont pas cette forme, et leur sommet optimal est (1,5 ; 3,75).

La propriété vaut aussi pour l'affectation, un transport où chaque offre et chaque demande vaut 1 : affecter des opérateurs à des postes se résout sans exiger l'entier, et le chapitre 4 le constate sur les opérateurs de Valdrome. Elle ne vaut plus dès qu'une contrainte d'une autre forme s'ajoute au modèle, une capacité de camion par exemple : il faut alors exiger l'entier.

Prendre en main le solveur

Six exercices courts, dans l'ordre : chacun ajoute une écriture de PuLP à la précédente. Les vérifications disent quel cas échoue, ce qui était attendu, et l'erreur probable quand elle est connue. Le premier exercice vérifié charge PuLP, en deux secondes environ ; les suivants vont vite.

Exercice : écrire puis vérifier

Un premier modèle à maximiser

Écrire marge_optimale(marge_poulie, marge_bielle), qui renvoie, arrondie au centime, la meilleure marge des lots quand un lot de poulies rapporte marge_poulie euros et un lot de bielles marge_bielle euros. Les heures ne changent pas : 5 de tournage et 3 de fraisage par lot de poulies, 2 et 2 par lot de bielles, 15 heures de tournage et 12 de fraisage en tout.

Le problème à maximiser est créé ; il reste les deux variables, positives, l'objectif et les deux contraintes.

À compléter

Écrire la fonction, puis « Vérifier » : 4 vérifications sur des cas qu'on sait faire de tête.

Exercice : écrire puis vérifier

Une contrainte « au moins »

Deux machines fabriquent deux pièces. Une heure de la machine M1 coûte 50 € et donne 3 pièces A et 1 pièce B ; une heure de la machine M2 coûte 40 € et donne 1 pièce A et 2 pièces B. Les heures se fractionnent.

Écrire cout_minimal(pieces_a, pieces_b), qui renvoie, arrondi au centime, le coût le plus bas pour obtenir au moins pieces_a pièces A et au moins pieces_b pièces B.

À compléter

Écrire la fonction, puis « Vérifier » : 4 vérifications sur des cas qu'on sait faire de tête.

Exercice : écrire puis vérifier

Une égalité et des bornes

Trois équipes se partagent exactement heures heures de presse dans la semaine. Une heure coûte 40 € avec l'équipe du matin, 30 € avec celle de l'après-midi, 50 € avec celle de nuit. Le matin dispose d'au plus 20 heures, l'après-midi d'au plus 15 ; l'équipe de nuit, qui doit garder la main, en fait au moins 5, et au plus 30.

Écrire cout_de_la_semaine(heures), qui renvoie, arrondi au centime, le coût le plus bas. Les bornes d'une variable se donnent à sa création : LpVariable("nuit", lowBound=5, upBound=30).

À compléter

Écrire la fonction, puis « Vérifier » : 4 vérifications sur des cas qu'on sait faire de tête.

Exercice : écrire puis vérifier

Lire le statut

Écrire statut_des_lots(contraintes), qui maximise la marge des lots, 520 x + 240 y avec x et y positifs, sous les contraintes données, et renvoie le statut de la résolution en clair : "Optimal", "Infeasible" (impossible) ou "Unbounded" (non borné).

Chaque contrainte est une liste [a, b, sens, c], qui se lit a x + b y ≤ c quand sens vaut "<=", et a x + b y ≥ c quand il vaut ">=".

À compléter

Écrire la fonction, puis « Vérifier » : 5 vérifications sur des cas qu'on sait faire de tête.

Exercice : écrire puis vérifier

Lire le prix d'une ressource

Écrire prix_des_heures(tournage, fraisage), qui résout les lots (520 € et 240 € de marge) avec tournage heures de tournage et fraisage heures de fraisage, et renvoie la liste [prix du tournage, prix du fraisage] : ce que rapporterait une heure de plus de chaque atelier, arrondi au centime.

Une contrainte nommée se retrouve, après la résolution, dans prob.constraints["tournage"], et son prix dans .pi.

À compléter

Écrire la fonction, puis « Vérifier » : 4 vérifications sur des cas qu'on sait faire de tête.

Exercice : écrire puis vérifier

Le transport

Écrire cout_du_transport(offres, demandes, couts), qui renvoie, arrondi au centime, le coût minimal d'un plan de transport : la carrière i livre au plus offres[i] tonnes, le chantier j reçoit exactement demandes[j] tonnes, et une tonne de i à j coûte couts[i][j] euros.

Une variable par route, dans un dictionnaire q[i, j] construit en compréhension, pour i dans carrieres et j dans chantiers.

À compléter

Écrire la fonction, puis « Vérifier » : 4 vérifications sur des cas qu'on sait faire de tête.

Les erreurs d'un débutant

Arrondir la relaxation en croyant résoudre
Les lots de poulies et de bielles l'ont montré : l'arrondi vers le bas du sommet continu rapporte 1 240 € quand l'optimum entier en rapporte 1 560, à l'autre bout du polygone. Le point continu donne une borne ; l'arrondi ne donne pas un optimum, parfois même pas une solution réalisable.
Oublier dans quel sens le solveur travaille
La fonction linprog minimise toujours : lui passer la marge sans en changer le signe fait chercher le plan qui rapporte le moins, souvent ne rien produire. PuLP minimise aussi quand on ne lui dit rien, car LpMinimize est son sens par défaut : un problème de marge créé sans LpMaximize rend un plan vide, avec le statut Optimal.
Écrire une contrainte à l'envers
Une ressource limitée s'écrit en « ≤ », un besoin à couvrir en « ≥ ». Inverser la première dans une maximisation laisse l'objectif croître sans fin : le problème devient non borné. Inverser la seconde dans une minimisation fait gagner le plan vide, à 0 €. Les deux erreurs ont une signature, qu'il vaut mieux reconnaître que corriger au hasard.
Lire un prix hors de sa plage
L'heure de tournage vaut 80 € entre 12 et 20 heures, et rien au-delà. Acheter 10 heures à 60 € en comptant 20 € de gain sur chacune se trompe : seules les 5 premières rapportent, et les 5 autres sont payées pour rien. Un prix dual est une pente, pas un tarif : il vaut tant que les mêmes contraintes restent saturées.
Lire la solution avant le statut
Sur un problème impossible ou non borné, il n'y a pas de plan : varValue vaut None, et la valeur de l'objectif aussi. Un programme qui calcule avec ces valeurs sans regarder le statut plante, ou affiche un nombre qui ne veut rien dire. « Impossible » est une réponse : elle dit que la demande se contredit.
Glisser un produit de variables dans le modèle
PuLP refuse x * y : il n'accepte que des expressions linéaires, et lève une erreur. Un quotient se linéarise en multipliant par le total, s'il est positif ; un coût fixe ou une condition demandent une variable binaire, donc la programmation en nombres entiers du chapitre 4.

Exercices type

Écrire sous forme standard le problème : minimiser 3x+2y3x + 2y sous x+y≥4x + y \ge 4, x−y≤2x - y \le 2, x,y≥0x, y \ge 0. Puis l'écrire pour linprog.

La forme standard veut des égalités. La contrainte « ≥ » reçoit une variable d'excédent, retranchée : x+y−s=4x + y - s = 4 ; la contrainte « ≤ » une variable d'écart, ajoutée : x−y+e=2x - y + e = 2 ; toutes les variables sont positives, x,y,s,e≥0x, y, s, e \ge 0. Pour la forme canonique, qui maximise, l'objectif devient max⁡ −3x−2y\max\, -3x - 2y.

linprog minimise déjà, mais n'accepte que des « ≤ » : la première contrainte s'y écrit en changeant tous ses signes, −x−y≤−4-x - y \le -4. D'où c = [3, 2], A_ub = [[-1, -1], [1, -1]] et b_ub = [-4, 2]. L'optimum est en (0 ; 4), pour 8.

Le simplexe a fait entrer yy d'abord, et il est au sommet (0 ; 6), pour 1 440 €. La ligne de la marge vaut 160 sous xx et −120 sous e2e_2. Que fait-il ensuite, et où arrive-t-il ?

Le coefficient 160 sous xx est positif : xx entre. Au sommet (0 ; 6), le fraisage est épuisé : un lot de poulies prend 3 heures de fraisage, qu'il faut retirer aux bielles, un lot et demi, et rapporte 520−32×240=160520 - \frac{3}{2} \times 240 = 160 €. Les deux variables de base baissent quand xx monte : y=6−32xy = 6 - \frac{3}{2}x s'annule à x=4x = 4, et le reste de tournage, e1=15−2×6−5x+3x=3−2xe_1 = 15 - 2 \times 6 - 5x + 3x = 3 - 2x, à x=1,5x = 1{,}5. Le plus petit rapport est 1,5 : e1e_1 sort, et le plan arrive en (1,5 ; 3,75), pour 1 680 €, le même sommet que par l'autre chemin. Plus aucun coefficient n'y est positif.

Un fournisseur propose 6 heures de fraisage de plus, à 35 € l'heure. Que répondre ?

Une heure de fraisage vaut 40 €, mais seulement de 9 à 15 heures. Au-delà de 15, le plan ne fabrique plus que des bielles, 7,5 lots, que le tournage arrête ; il reste du fraisage, et une heure de plus ne rapporte rien. Les 3 premières heures font monter la marge de 120 €, pour 105 € payés : 15 € de gain. Les 3 suivantes coûtent 105 € et ne rapportent rien. Acheter les 6 heures ferait perdre 90 € ; en acheter 3 en fait gagner 15. Le prix dual dit à quel tarif acheter, et sa plage dit combien.

Pourquoi le plan de transport est-il fait de tonnes entières, sans qu'on l'ait exigé, alors que les lots s'arrêtent à 3,75 lots de bielles ?

Parce que le simplexe rend un sommet, et que les sommets d'un problème de transport sont entiers quand les offres et les demandes le sont. C'est une propriété de la forme de ses contraintes : chaque variable y figure deux fois, avec le coefficient 1, dans l'offre de sa carrière et dans la demande de son chantier. Les contraintes des lots, avec leurs coefficients 5, 2, 3 et 2, n'ont pas cette forme, et leur sommet optimal a une coordonnée fractionnaire. La propriété se perd dès qu'une contrainte d'une autre forme s'ajoute au transport : il faut alors exiger l'entier.

Le simplexe doit-il visiter tous les sommets pour prouver que le sien est optimal ?

Non. Il s'arrête quand aucun coefficient de la ligne de la marge n'est positif, c'est-à-dire quand aucun sommet voisin ne fait mieux, et la convexité du polygone garantit qu'alors aucun sommet, voisin ou non, ne fait mieux. Sur les lots, il prouve l'optimum de (1,5 ; 3,75) sans avoir visité (0 ; 6). Le pire cas, celui de Klee et Minty, l'oblige à les visiter tous, mais par le chemin qu'il emprunte, pas pour vérifier.

La méthode

  1. Nommer les variables de décision, et dire ce que chacune compte, avec son unité.
  2. Écrire l'objectif et chaque contrainte comme des sommes de termes proportionnels aux variables : une ressource limitée en « ≤ », un besoin en « ≥ », une quantité imposée en « = ». Linéariser un quotient en multipliant par le total, s'il est positif ; refuser un produit de variables.
  3. Donner au solveur la forme qu'il attend : la forme standard, avec une variable d'écart par contrainte, pour le simplexe à la main ; des matrices à minimiser pour linprog ; le modèle tel qu'on le dit pour PuLP, qui fait les conversions.
  4. À deux variables, chercher l'optimum parmi les sommets du polygone ; au-delà, confier le calcul au solveur, qui va de sommet en sommet ou traverse le polygone par l'intérieur.
  5. Lire le statut avant la solution : « impossible » dit que la demande se contredit, « non borné » trahit presque toujours une contrainte oubliée ou écrite à l'envers.
  6. Lire la solution : les contraintes saturées fixent le plan, une ressource non saturée a un prix nul, une unité de plus d'une ressource saturée vaut son prix, dans une plage qu'il faut connaître avant d'acheter.
  7. Si les variables doivent être entières, ne pas arrondir : résoudre en nombres entiers, avec cat="Integer", et garder l'optimum continu comme borne. Un problème de transport aux données entières fait exception : son optimum continu est déjà entier.

Synthèse

  • Un programme linéaire maximise ou minimise une somme de termes proportionnels aux variables, sous des contraintes du même type, avec des variables réelles. Un produit de variables, un quotient, un coût fixe ou une condition n'en sont pas ; un quotient d'un total positif se linéarise.
  • La forme canonique maximise sous des « ≤ » ; la forme standard n'a que des égalités, avec une variable d'écart par contrainte, ce qui reste de la ressource. Une contrainte est saturée quand son écart est nul. Les conversions ne changent que l'écriture : linprog en exige une, PuLP les fait seul.
  • Le domaine réalisable est un polygone, convexe, et l'optimum, s'il existe, est atteint en un sommet. Énumérer les sommets marche à deux variables, et devient impossible quand le nombre de variables grandit.
  • Le simplexe va de sommet en sommet : l'entrante a un coefficient positif dans la ligne de la marge, la sortante le plus petit rapport, le pivot réécrit le tableau, et l'arrêt vient quand plus aucun coefficient n'est positif. Sur les lots : (0 ; 0), (3 ; 0), puis (1,5 ; 3,75), pour 1 680 €. La règle d'entrée change le chemin, pas l'arrivée.
  • Les points intérieurs traversent le polygone et sont polynomiaux ; HiGHS choisit entre eux et un simplexe selon le problème. Le simplexe est exponentiel dans le pire cas, 2n−12^n - 1 pivots sur le cube de Klee et Minty, et rapide en pratique.
  • Interpréter une solution, c'est lire le statut, les contraintes saturées, les écarts, et les prix duaux : 80 € l'heure de tournage, valable de 12 à 20 heures, 40 € l'heure de fraisage, de 9 à 15 heures. Un problème peut être impossible ou non borné, et le solveur le dit.
  • Le problème de transport se modélise par une variable par route, une contrainte par offre et une par demande ; aux données entières, son optimum continu est entier : 1 160 € pour les carrières, en tonnes entières.
  • L'optimum continu est une borne de l'optimum entier : 1 680 € pour les lots, contre 1 560 € en lots entiers. Arrondir le sommet continu ne donne pas l'optimum entier.

Et ensuite

Le chapitre suivant fait un outil de la borne que donne le continu : elle dit ce qu'une solution entière peut encore espérer, et un solveur en nombres entiers s'en sert pour résoudre exactement les petits cas, par séparation et évaluation. Le chapitre 5 sort du cadre linéaire, pour les contraintes et les coûts qui ne s'y écrivent pas.

L'atelier de programmation linéaire met ce chapitre en pratique chez Da Lambda, une pizzeria : un modèle à poser depuis une demande écrite, une proportion à linéariser, un simplexe qui commence par un sommet dégénéré, un mélange à composer, des commandes en tout ou rien. Le thème d'exercices La programmation linéaire reprend la lecture d'une solution, le calcul d'un prix, un pivot à la main, un transport et un modèle à corriger, sur d'autres situations. La méthode graphique est détaillée dans le chapitre optimiser sous contraintes du parcours Mathématiques appliquées.

Chapitre révisé en octobre 2026 · Une erreur dans ce chapitre ?

Mettre en pratique

Lire la sortie d'un solveur, retrouver le prix d'une ressource, dérouler un pivot du simplexe, livrer au moindre coût malgré des routes coupées, et corriger un modèle qui ne produit rien.

Tous les exercices sur la programmation linéaire