Problème du Sac à  Dos fraction

01 May 2019 01 May 2019 18633 vues ESSADDOUKI Mostafa 8 min de lecture
Problème : Sac à dos fractionnaire Étant donné les poids et les valeurs de n articles, nous devons mettre ces articles dans un sac à dos de capacité C pour obtenir la valeur totale maximale dans le sac à dos.

Particularité : Nous pouvons prendre des fractions d'articles (contrairement au sac à dos 0/1). Cela rend le problème adapté à une approche gloutonne.

  Exemples

Exemple 1
Entrée
Articles = [(60, 10), (100, 20), (100, 60), (40, 20)]
Capacité C = 50
Sortie
200.0
Explication : On prend les articles de poids 10 (valeur 60), 20 (valeur 100) et 20 (valeur 40) → 60 + 100 + 40 = 200
Exemple 2
Entrée
Articles = [(120, 30), (100, 20), (60, 10)]
Capacité C = 50
Sortie
280.0
Explication : Rapport valeur/poids : 4.0, 5.0, 6.0. Ordre décroissant : article 3 (6.0) → prend 10/10, article 2 (5.0) → prend 20/20, article 1 (4.0) → prend 20/30. Total = 60 + 100 + (20/30 × 120) = 60 + 100 + 80 = 240 + 40? Non : 60 + 100 + 80 = 240? Recalcul : 60 + 100 = 160, + 80 = 240. Attendez, vérifions : (60 + 100 + 80) = 240, mais la réponse donnée est 280. Il y a une erreur.
Exemple 2 (corrigé)
Entrée
Articles = [(60, 10), (100, 20), (120, 30)]
Capacité C = 50
Sortie
240.0
Explication : Rapports valeur/poids : article 1: 6.0, article 2: 5.0, article 3: 4.0. Ordre glouton : article 1 (poids 10, valeur 60), article 2 (poids 20, valeur 100), reste capacité = 20, on prend 20/30 de l'article 3 (valeur 120 × 2/3 = 80). Total = 60 + 100 + 80 = 240.
Exemple 3
Entrée
Articles = [(500, 30)]
Capacité C = 20
Sortie
333.3333333333333
Explication : Un seul article, on prend une fraction de 20/30 = 2/3 de sa valeur : 500 × 2/3 ≈ 333.33

Une solution naïve consisterait à essayer tous les sous-ensembles possibles avec des fractions différentes, mais cela prendrait trop de temps (complexité exponentielle).

Une solution efficace consiste à utiliser l'approche gloutonne. L'idée de l'approche gloutonne est de calculer le rapport valeur/poids pour chaque article et de trier les articles sur la base de ce rapport (du plus élevé au plus bas). Ensuite, on prend l'élément avec le rapport le plus élevé et on l'ajoute entièrement jusqu'à ce que nous ne puissions plus ajouter l'élément suivant dans son intégralité. À la fin, on ajoute une fraction du dernier article possible.

Pourquoi l'approche gloutonne est optimale ? Pour le sac à dos fractionnaire, l'approche gloutonne donne toujours la solution optimale car on peut toujours remplacer une partie d'un article moins rentable par une partie d'un article plus rentable sans violer la contrainte de capacité.
1. Calculer le rapport valeur/poids pour chaque article.
2. Trier les articles par ordre décroissant de ce rapport.
3. Initialiser la valeur totale à 0.
4. Pour chaque article dans l'ordre trié :
   a. Si l'article entier peut tenir dans la capacité restante :
        Ajouter l'article entier à la solution
        Réduire la capacité du poids de l'article
        Ajouter sa valeur totale au résultat
   b. Sinon :
        Ajouter une fraction de l'article (capacité restante / poids de l'article)
        Ajouter la valeur fractionnaire au résultat
        Terminer (capacité atteinte)
5. Retourner la valeur totale maximale.
#include <stdio.h>
#include <stdlib.h>

// Structure pour représenter un article
typedef struct {
    int valeur;
    int poids;
    float rapport;
} Article;

// Fonction de comparaison pour le tri décroissant selon le rapport
int comparer(const void* a, const void* b) {
    Article* articleA = (Article*)a;
    Article* articleB = (Article*)b;
    
    if (articleA->rapport < articleB->rapport)
        return 1;
    else if (articleA->rapport > articleB->rapport)
        return -1;
    else
        return 0;
}

float sacADos(Article articles[], int n, int capacite) {
    // Calculer les rapports valeur/poids
    for (int i = 0; i < n; i++) {
        articles[i].rapport = (float)articles[i].valeur / articles[i].poids;
    }
    
    // Trier les articles par rapport décroissant
    qsort(articles, n, sizeof(Article), comparer);
    
    float valeurTotale = 0.0;
    int poidsRestant = capacite;
    
    printf("Articles sélectionnés (dans l'ordre) :\n");
    
    for (int i = 0; i < n; i++) {
        if (articles[i].poids <= poidsRestant) {
            // Prendre l'article entier
            printf("  - Article entier (valeur=%d, poids=%d, rapport=%.2f)\n", 
                   articles[i].valeur, articles[i].poids, articles[i].rapport);
            poidsRestant -= articles[i].poids;
            valeurTotale += articles[i].valeur;
        } else {
            // Prendre une fraction de l'article
            float fraction = (float)poidsRestant / articles[i].poids;
            printf("  - Fraction de l'article (%.2f × %d = %.2f, rapport=%.2f)\n", 
                   fraction, articles[i].valeur, fraction * articles[i].valeur, articles[i].rapport);
            valeurTotale += articles[i].valeur * fraction;
            break; // Sac plein
        }
    }
    
    return valeurTotale;
}

int main() {
    // Exemple 1
    Article articles1[] = {
        {60, 10, 0},
        {100, 20, 0},
        {100, 60, 0},
        {40, 20, 0}
    };
    int n1 = sizeof(articles1) / sizeof(articles1[0]);
    int capacite1 = 50;
    
    printf("=== Exemple 1 ===\n");
    printf("Articles : (60,10), (100,20), (100,60), (40,20)\n");
    printf("Capacité : %d\n\n", capacite1);
    
    float resultat1 = sacADos(articles1, n1, capacite1);
    printf("\nValeur maximale dans le sac à dos = %.2f\n\n", resultat1);
    
    // Exemple 2
    Article articles2[] = {
        {60, 10, 0},
        {100, 20, 0},
        {120, 30, 0}
    };
    int n2 = sizeof(articles2) / sizeof(articles2[0]);
    int capacite2 = 50;
    
    printf("=== Exemple 2 ===\n");
    printf("Articles : (60,10), (100,20), (120,30)\n");
    printf("Capacité : %d\n\n", capacite2);
    
    float resultat2 = sacADos(articles2, n2, capacite2);
    printf("\nValeur maximale dans le sac à dos = %.2f\n\n", resultat2);
    
    // Exemple 3 (cas d'un seul article)
    Article articles3[] = {
        {500, 30, 0}
    };
    int n3 = sizeof(articles3) / sizeof(articles3[0]);
    int capacite3 = 20;
    
    printf("=== Exemple 3 ===\n");
    printf("Articles : (500,30)\n");
    printf("Capacité : %d\n\n", capacite3);
    
    float resultat3 = sacADos(articles3, n3, capacite3);
    printf("\nValeur maximale dans le sac à dos = %.2f\n", resultat3);
    
    return 0;
}
def sac_dos_fractionnaire(articles, capacite):
    """
    Résout le problème du sac à dos fractionnaire.
    
    Paramètres:
    articles: liste de tuples (valeur, poids)
    capacite: capacité maximale du sac
    
    Retourne:
    float: valeur totale maximale
    """
    # Créer une liste d'articles avec leur rapport valeur/poids
    articles_avec_rapport = []
    for i, (valeur, poids) in enumerate(articles):
        rapport = valeur / poids
        articles_avec_rapport.append((valeur, poids, rapport, i))
    
    # Trier par rapport décroissant
    articles_avec_rapport.sort(key=lambda x: x[2], reverse=True)
    
    valeur_totale = 0.0
    poids_restant = capacite
    selection = []  # Pour stocker les articles sélectionnés
    
    for valeur, poids, rapport, index in articles_avec_rapport:
        if poids <= poids_restant:
            # Prendre l'article entier
            selection.append((index, 1.0, valeur, poids))
            poids_restant -= poids
            valeur_totale += valeur
        else:
            # Prendre une fraction de l'article
            fraction = poids_restant / poids
            selection.append((index, fraction, valeur * fraction, poids_restant))
            valeur_totale += valeur * fraction
            break  # Sac plein
    
    # Afficher les détails de la sélection
    print("Articles sélectionnés :")
    for index, fraction, valeur_contrib, poids_contrib in selection:
        article_original = articles[index]
        if fraction == 1.0:
            print(f"  - Article {index+1}: entier (valeur={article_original[0]}, poids={article_original[1]})")
        else:
            print(f"  - Article {index+1}: fraction {fraction:.2f} (valeur={article_original[0]} × {fraction:.2f} = {valeur_contrib:.2f}, poids={poids_contrib:.1f})")
    
    return valeur_totale

def afficher_resultat(articles, capacite):
    """Affiche le résultat de façon détaillée"""
    print(f"Articles : {articles}")
    print(f"Capacité : {capacite}")
    print()
    
    resultat = sac_dos_fractionnaire(articles, capacite)
    print(f"\nValeur maximale dans le sac à dos = {resultat:.2f}")
    print("-" * 50)

# Tester la fonction avec les exemples
print("=== Exemple 1 ===")
articles1 = [(60, 10), (100, 20), (100, 60), (40, 20)]
afficher_resultat(articles1, 50)

print("\n=== Exemple 2 ===")
articles2 = [(60, 10), (100, 20), (120, 30)]
afficher_resultat(articles2, 50)

print("\n=== Exemple 3 ===")
articles3 = [(500, 30)]
afficher_resultat(articles3, 20)

print("\n=== Exemple 4 (test interactif) ===")
# Test avec des valeurs personnalisées
articles_test = [(90, 20), (120, 30), (60, 10), (200, 40)]
capacite_test = 60
afficher_resultat(articles_test, capacite_test)
Sortie
=== Exemple 1 ===
Articles : [(60, 10), (100, 20), (100, 60), (40, 20)]
Capacité : 50

Articles sélectionnés :
  - Article 1: entier (valeur=60, poids=10)
  - Article 2: entier (valeur=100, poids=20)
  - Article 4: entier (valeur=40, poids=20)

Valeur maximale dans le sac à dos = 200.00
--------------------------------------------------

=== Exemple 2 ===
Articles : [(60, 10), (100, 20), (120, 30)]
Capacité : 50

Articles sélectionnés :
  - Article 1: entier (valeur=60, poids=10)
  - Article 2: entier (valeur=100, poids=20)
  - Article 3: fraction 0.67 (valeur=120 × 0.67 = 80.00, poids=20.0)

Valeur maximale dans le sac à dos = 240.00
--------------------------------------------------

=== Exemple 3 ===
Articles : [(500, 30)]
Capacité : 20

Articles sélectionnés :
  - Article 1: fraction 0.67 (valeur=500 × 0.67 = 333.33, poids=20.0)

Valeur maximale dans le sac à dos = 333.33
--------------------------------------------------
Analyse de complexité
  • Tri des articles : O(n log n) où n est le nombre d'articles
  • Parcours des articles : O(n)
  • Complexité totale : O(n log n)
  • Complexité spatiale : O(n) pour stocker les articles avec leurs rapports

Comparaison des approches

ApprocheComplexitéOptimalitéQuand l'utiliser
Force bruteO(2ⁿ)OptimaleUniquement pour très petits ensembles (n ≤ 20)
Glouton (fractionnaire)O(n log n)Optimale pour version fractionnaireVersion fractionnaire du problème
Programmation dynamiqueO(n × C)Optimale pour version 0/1Version 0/1 avec capacité modérée
Cas particuliers
  • Tous les articles ont le même rapport : L'ordre de sélection n'a pas d'importance
  • Capacité nulle : La valeur maximale est 0
  • Articles avec poids nul : Peuvent être ajoutés sans limite (cas rare)
  • Capacité très grande : Tous les articles peuvent être pris entièrement
Variantes du problème
  • Sac à dos 0/1 : Chaque article est soit pris entièrement, soit pas du tout (pas de fraction)
  • Sac à dos fractionnaire : Notre problème actuel
  • Sac à dos avec contraintes multiples : Plusieurs dimensions (poids, volume, etc.)
Sortie
// La sortie apparaîtra ici…
Prêt · Ctrl+Entrée pour exécuter

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.