Projet

Séance de code pour tester le problème du sac à dos en condition (presque) réelle.

Créez un projet vscode pour mettre toutes vos programmes.

Données

Créez un fichier données.py et placez y l'exemple du cours sous la forme d'une constante.

Algorithmes gloutons

Sac à dos fractionnel

Créez un fichier fractionnel.py où vous écrirez le code de l'algorithme glouton trouvant le sac à dos fractionnel optimal.

Vous testerez votre code dans le fichier test_fractionnel.py

N'oubliez pas qu'il ne faut jamais tester directement des réels. On utilise approx de pytest pour vérifier qu'ils sont à peu prêt égaux.

Une fois que vous êtes assuré du bon fonctionnement de votre algorithme :

Exécutez l'algorithme glouton fractionnel avec l'EXEMPLE du fichier données.py.

Pour vérifier que le profit est bien celui attendu, créons une fonction :

Ajoutez au fichier données.py une fonction profit(sac_à_dos, produits) qui rend le profit réalisé pour le sac à dos et les produits en entrée.

Vous testerez cette fonction dans le fichier test_données.py.

Enfin :

Créez un fichier main_fractionnel.py dont l'exécution rende les données, le sac à dos et le profit pour l'exemple. Vous devez avoir quelque chose du genre en sortie :

Données :
{'nom': 'poudre 1', 'kg': 15, 'prix': 135}
{'nom': 'poudre 2', 'kg': 2, 'prix': 30}
{'nom': 'poudre 3', 'kg': 4, 'prix': 32}
{'nom': 'poudre 4', 'kg': 1, 'prix': 6}
{'nom': 'poudre 5', 'kg': 6, 'prix': 18}
{'nom': 'poudre 6', 'kg': 80, 'prix': 800}

Sac à dos fractionnel optimal :
0 poudre 1
1 poudre 2
0 poudre 3
0 poudre 4
0 poudre 5
0.225 poudre 6

Profit : 210.0

Sac à dos glouton

Créez un fichier sac_a_dos.py où vous écrirez le code de l'algorithme glouton trouvant le sac à dos.

Vous testerez votre code dans le fichier test_sac_a_dos.py

Pour comparer les résultats par rapport au problème du sac à dos fractionnel :

Créez un fichier main_sac_a_dos.py dont l'exécution rende les données, le sac à dos et le profit pour l'exemple. Vous devez avoir quelque chose du genre en sortie :

Données :
{'nom': 'poudre 1', 'kg': 15, 'prix': 135}
{'nom': 'poudre 2', 'kg': 2, 'prix': 30}
{'nom': 'poudre 3', 'kg': 4, 'prix': 32}
{'nom': 'poudre 4', 'kg': 1, 'prix': 6}
{'nom': 'poudre 5', 'kg': 6, 'prix': 18}
{'nom': 'poudre 6', 'kg': 80, 'prix': 800}

Sac à dos glouton :
1 poudre 1
1 poudre 2
0 poudre 3
1 poudre 4
0 poudre 5
0 poudre 6

Profit : 171

Expérimentation aléatoire

Dans le fichier données.py créez une fonction sac_à_dos(n, prix, K) qui génère aléatoirement un jeu de $n$ données où chaque donnée a un prix inférieure au paramètre prix et un poids inférieur à K

Testez un peu vos algorithmes :

Dans un fichier aléatoire_glouton.py testez pour plusieurs jeux de donnés les différences entre :

  • la valeur de la solution fractionnelle optimale,
  • la valeur du glouton
  • les différences entre les deux sac à dos

Programmation dynamique

Dans un fichier efficace.py créez une fonction programmation_dynamique(produits, masse_totale) qui utilise la programmation dynamique pour trouver la valeur du sac à dos optimal.

On peut maintenant comparer les résultats :

Dans le fichier main_efficace.py, vérifiez que l'exemple donne bien la bonne valeur.

Dans un fichier aléatoire_valeur.py testez pour plusieurs jeux de donnés les différences entre :

  • la valeur de la solution fractionnelle optimale,
  • la valeur du glouton
  • la valeur de la solution optimale (utilisez des valeurs de $K$ pas trop grande pour que le calcul par programmation dynamique soit possible en temps raisonnable)

Énumération exhaustive

Tous les sac à dos

Dans le fichier sac_a_dos.py créez une fonction énumération(produits, masse_totale) qui énumère tous les sac à dos possibles et rend le sac à dos maximum.

Vous testerez votre code dans le fichier test_sac_a_dos.py

On peut maintenant comparer les résultats :

Dans le fichier main_sac_a_dos.py, vérifiez que l'exemple donne bien le bon résultat.

Branch and Bound

Dans le fichier énumération.py créez une fonction branch_and_bound(produits, masse_totale) qui utilise le branch and bound pour trouver le sac à dos optimal.

On peut maintenant comparer les résultats :

Dans le fichier main_efficace.py, vérifiez que l'exemple donne bien la même valeure optimale pour le branch and bound et l'énumération.

Améliorons la complexité :

  • Faites en sorte de commencer avec le résultat de l'algorithme glouton plutôt que le sac à dos vide
  • Faites le tri des produits une fois pour toute au début de la fonction branch_and_bound et pas à chaque appel du glouton fractionnel.

Expérimentation aléatoire

Dans un fichier aléatoire_temps.py, mesurez le temps pris par l'algorithme du branch and bound pour résoudre des problèmes avec :

  • de plus en plus d'éléments mais un sac de capacité petite
  • peux d'éléments mais un gros sac à dos (pour que la solution ne soit pas triviale, assurez vous que les poids des éléments soient grand...)

Vous pouvez bien sur comparer par rapport à l'énumération exhaustive, mais le temps va devir très vite prohibitif.

Pour aller plus loin

Implémentez l'algorithme à performance garantie et vérifiez que l'on est bien au pire deux fois moins bon que la solution optimale. Comparez son résultat à la solution optimale (calculée par branch and bound) et au résultat du glouton.

Vous pouvez aussi trouver le sac à dos optimal en remontant la programmation dynamique :

Implémentez un algorithme qui, à partir de la matrice rendue par l'algorithme de programmation dynamique, rend le sac à dos optimal associé.