diff --git a/.idea/misc.xml b/.idea/misc.xml index a346fd7433def0c6493cf110cc4b5a801a98776c..e778be18ce10327d4e6028c48a4ca77537a3f1da 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> - <component name="ProjectRootManager" version="2" languageLevel="JDK_18" default="true" project-jdk-name="18" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_22" default="true" project-jdk-name="18" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/out" /> </component> </project> \ No newline at end of file diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000000000000000000000000000000000000..2684370cdd63221b730a9e8d0d7f53c66454eb7f --- /dev/null +++ b/Readme.md @@ -0,0 +1,54 @@ +# Solveur de Sac à Dos par Branch and Bound + +Programme Java implémentant l'algorithme Branch and Bound avec relaxation fractionnaire pour résoudre le problème du sac à dos 0/1 de manière optimale. + +## Fonctionnalités +- Algorithme d'exploration avec élagage par bornes +- Calcul de borne supérieure par méthode fractionnaire +- Lecture des données depuis un fichier texte +- Tri préalable des objets par ratio valeur/poids +- Statistiques d'exécution (nœuds explorés) +- Affichage de la solution optimale détaillée + +## Prérequis +- Java JDK 11 ou supérieur +- Fichier d'instance au format spécifié + +## Installation +1. Cloner/déposer les fichiers sources dans un dossier +2. Modifier le chemin du fichier dans `App.java` : +```java +// Ligne à modifier dans App.java +Backpack backpack = new InstanceReader().read("chemin/vers/votre_fichier.txt"); +``` + +## Compiler le projet : +```bash +javac -d bin src/*.java +``` + +## Utilisation +```bash +java -cp bin App +``` +## Exemple de sortie : + La valeur optimale est : 220 + Objets inclus dans la solution optimale : [true, false, true, true] + Nombre de nœuds explorés : 27 + +## Structure du code +| Classe | Description | +|-----------------|----------------------------------------------| +| Backpack | Implémente l'algorithme principal | +| Loot | Représente un objet avec ratio valeur/poids | +| InstanceReader | Lit et parse les fichiers d'instance | +| App | Point d'entrée du programme | + +## Membre(s) du projet + +- OUATTARA Sie +- GOUNOU Boubacar + +## Licence + +MIT License - Libre d'utilisation et modification \ No newline at end of file diff --git a/out/production/aroTP2/App.class b/out/production/aroTP2/App.class index f3dffbcc762d201aabffbb6ae573bd035da8db2d..1e4b3260fdd102ef289c8dbb8562734ddeac761f 100644 Binary files a/out/production/aroTP2/App.class and b/out/production/aroTP2/App.class differ diff --git a/out/production/aroTP2/InstanceReader.class b/out/production/aroTP2/InstanceReader.class index bcce837946a70025a3a851972fc22b8e964e1cc4..9a1d5b11a0914dac9a5b1a1a0bdda4d0958bdced 100644 Binary files a/out/production/aroTP2/InstanceReader.class and b/out/production/aroTP2/InstanceReader.class differ diff --git a/out/production/aroTP2/Loot.class b/out/production/aroTP2/Loot.class index 677c206f5b43a39187728543b3333c45abeee666..bc14dcc2b219d38772d8b2f664a991b6eba276ba 100644 Binary files a/out/production/aroTP2/Loot.class and b/out/production/aroTP2/Loot.class differ diff --git a/src/App.java b/src/App.java index 919c748040e30676e800217504bec9fc5dadd720..7e84a0faa4fbca152b00ff24c56ef4cd47fe5500 100644 --- a/src/App.java +++ b/src/App.java @@ -1,8 +1,9 @@ +import java.util.Arrays; import java.util.List; public class App { public static void main(String[] args) throws Exception { - Backpack backpack= new InstanceReader().read("src/sac0"); - System.out.println(backpack); + Backpack backpack= new InstanceReader().read("/home/aristidecod/Documents/branch-and-bound/src/sac0"); + backpack.solve(); } -} +} \ No newline at end of file diff --git a/src/Backpack.java b/src/Backpack.java index 121d485ed18cc2096aaf45eb08d3c699ff125a14..b5f71b1e8d06f70eccaa3e21c8345e825c6d7dab 100644 --- a/src/Backpack.java +++ b/src/Backpack.java @@ -1,15 +1,98 @@ +import java.util.Arrays; +import java.util.Comparator; import java.util.List; public class Backpack { - int capacity; - List<Loot> loots; + private final int capacity; + private final List<Loot> loots; + private int bestValue = 0; + private int currentWeight = 0; + private int currentValue = 0; + private final boolean[] currentSolution; + private final boolean[] bestSolution; + private int nodesExplored = 0; public Backpack(int capacity, List<Loot> loots) { this.capacity = capacity; this.loots = loots; + this.currentSolution = new boolean[loots.size()]; + this.bestSolution = new boolean[loots.size()]; + } + + public void solve() { + this.sortByRatio(); + explore_from(0); + System.out.println("La valeur optimale est : " + bestValue); + System.out.println("Objets inclus dans la solution optimale : " + Arrays.toString(bestSolution)); + System.out.println("Nombre de nœuds explorés : " + nodesExplored); } public String toString(){ return capacity +"\n"+loots; } + + /** + Méthode pour trier les objets par ordre décroissant selon leur ratio + */ + public void sortByRatio(){ + loots.sort(Comparator.comparing(Loot::getRatio).reversed()); + } + + public int solutionFractionnelle(int startIndex, int remainingCapacity) { + int sfValuee = 0; + int sfWeight = 0; + + // On commence à partir de startIndex + for(int i = startIndex; i < loots.size(); i++) { + Loot loot = loots.get(i); + if(sfWeight + loot.getWeight() <= remainingCapacity){ + // Si l'objet entier peut entrer dans le sac + sfValuee += loot.getValue(); + sfWeight += loot.getWeight(); + } + else { + // Si l'objet ne peut pas entrer entièrement dans le sac + int remainingWeight = remainingCapacity - sfWeight; + sfValuee += (int) (loot.getRatio() * remainingWeight); + break; + } + } + return sfValuee; + } + + private void explore_from(int index) { + nodesExplored++; + // Si on a exploré tous les objets + if (index >= loots.size()) { + if (currentValue > bestValue) { + bestValue = currentValue; + System.arraycopy(currentSolution, 0, bestSolution, 0, loots.size()); + } + return; + } + + // On calcule la borne supérieure pour ce nœud + int remainingCapacity = capacity - currentWeight; + int borneSuperieure = currentValue + solutionFractionnelle(index, remainingCapacity); + + // Si la borne supérieure est inférieure à la meilleure solution + if (borneSuperieure <= bestValue) { + return; + } + + // on essaye d'explorer avec l'objet + Loot currentLoot = loots.get(index); + if (currentWeight + currentLoot.getWeight() <= capacity) { + currentWeight += currentLoot.getWeight(); + currentValue += currentLoot.getValue(); + currentSolution[index] = true; + explore_from(index + 1); + currentWeight -= currentLoot.getWeight(); + currentValue -= currentLoot.getValue(); + currentSolution[index] = false; + } + + // on explore sans l'objet + explore_from(index + 1); + } } diff --git a/src/InstanceReader.java b/src/InstanceReader.java index 58e01aeeea4ff1d787bd9d70dba63e67f393f3c4..1724721c844fac11e361a9c3a65d0b14dc51da5d 100644 --- a/src/InstanceReader.java +++ b/src/InstanceReader.java @@ -23,5 +23,4 @@ public class InstanceReader { return new Backpack(capacity,loots); } - } diff --git a/src/Loot.java b/src/Loot.java index 46c20c07fc79c7d7b68cd9615108f3d48403fab1..ff7a6d54b361083055ec1a18d9c1f8ee601b9984 100644 --- a/src/Loot.java +++ b/src/Loot.java @@ -1,12 +1,26 @@ public class Loot { - int weight, value; + private final int weight,value; + private final float ratio; public Loot(int weight, int value) { this.weight = weight; this.value = value; + this.ratio = (float) value/weight; } public String toString(){ - return weight+" "+value; + return weight+" "+value+" "+ratio; + } + + public float getRatio(){ + return ratio; + } + + public int getWeight(){ + return weight; + } + + public int getValue(){ + return value; } } diff --git a/src/sacTest b/src/sacTest new file mode 100644 index 0000000000000000000000000000000000000000..25c99fbdaa22162309a1678f39c96731d7aedfa9 --- /dev/null +++ b/src/sacTest @@ -0,0 +1,4 @@ +15 +5 40 +8 48 +4 36 \ No newline at end of file