Problema dello zaino frazionario: algoritmo Greedy con esempio
Cos'รจ la strategia golosa?
Gli algoritmi greedy sono come algoritmi di programmazione dinamica che vengono spesso utilizzati per risolvere problemi ottimali (trovare le migliori soluzioni del problema secondo un criterio particolare).
Gli algoritmi greedy implementano selezioni locali ottimali nella speranza che tali selezioni portino a una soluzione globale ottimale per il problema da risolvere. Gli algoritmi greedy spesso non sono troppo difficili da impostare, sono veloci (la complessitร temporale รจ spesso una funzione lineare o molto una funzione di secondo ordine). Inoltre, questi programmi non sono difficili da debuggare e utilizzano meno memoria. Ma i risultati non sono sempre una soluzione ottimale.
Le strategie greedy vengono spesso utilizzate per risolvere il problema di ottimizzazione combinatoria costruendo un'opzione A. L'opzione A viene costruita selezionando ciascun componente Ai di A fino al completamento (abbastanza n componenti). Per ogni Ai, scegli Ai in modo ottimale. In questo modo รจ possibile che all'ultimo passaggio non si abbia altro da selezionare se non accettare l'ultimo valore rimasto.
Ci sono due componenti critici delle decisioni avide:
- Via della selezione golosa. ร possibile selezionare la soluzione migliore al momento e risolvere il sottoproblema derivante dall'ultima selezione. La selezione degli algoritmi greedy puรฒ dipendere dalle selezioni precedenti. Ma non puรฒ dipendere da alcuna selezione futura o dalla soluzione dei sottoproblemi. L'algoritmo si evolve in modo da effettuare selezioni in un ciclo, riducendo allo stesso tempo il problema dato in sottoproblemi piรน piccoli.
- Sottostruttura ottimale. Si esegue la sottostruttura ottimale per un problema se la soluzione ottima di questo problema contiene soluzioni ottimali ai suoi sottoproblemi.
Un algoritmo greedy ha cinque componenti:
- Un insieme di candidati, da cui creare soluzioni.
- Una funzione di selezione, per selezionare il miglior candidato da aggiungere alla soluzione.
- Una funzione fattibile viene utilizzata per decidere se un candidato puรฒ essere utilizzato per costruire una soluzione.
- Una funzione obiettivo, che fissa il valore di una soluzione o di una soluzione incompleta.
- Una funzione di valutazione, che indica quando trovi una soluzione completa.
L'idea dell'avido
Con la prima idea, hai i seguenti passaggi di Greedy One:
- Ordina in ordine di valori non crescente.
- Considerare a turno i colli ordinati, mettere nello zaino il pacco in esame se la capacitร residua dello zaino รจ sufficiente a contenerlo (il che significa che il peso totale dei colli messi nello zaino e il peso dei colli in esame non superano la capacitร dello zaino).
Tuttavia, questo algoritmo avido non fornisce sempre la soluzione ottimale. Ecco un controesempio:
- I parametri del problema sono: n = 3; M = 19.
- I pacchetti: {i = 1; W[i] = 14; V[i] = 20}; {io = 2; W[i] = 6; V[i] = 16}; {i = 3; W[i] = 10; V[i] = 8} -> Ottimo rapporto qualitร /prezzo ma anche ottimo peso.
- L'algoritmo selezionerร il pacchetto 1 con un valore totale di 20, mentre la soluzione ottima del problema verrร selezionata (pacchetto 2, pacchetto 3) con un valore totale di 24.
L'idea di Greedy Two
Con la seconda idea, hai i seguenti passaggi di Greedy Two:
- Ordina in ordine di peso non decrescente.
- Considerare a turno i colli ordinati, mettere nello zaino il pacco in esame se la capacitร residua dello zaino รจ sufficiente a contenerlo (il che significa che il peso totale dei colli messi nello zaino e il peso dei colli in esame non superano la capacitร dello zaino).
Tuttavia, questo algoritmo avido non fornisce sempre la soluzione ottimale. Ecco un controesempio:
- I parametri del problema sono: n = 3; M = 11.
- I pacchetti: {i = 1; W[i] = 5; V[i] = 10}; {io = 2; W[i] = 6; V[i] = 16}; {i = 3; W[i] = 10; V[i] = 28} -> Leggero ma anche il valore รจ molto leggero.
- L'algoritmo selezionerร (pacchetto 1, pacchetto 2) con un valore totale di 26, mentre la soluzione ottimale del problema รจ (pacchetto 3) con un valore totale di 28.
L'idea del Tre Avido
Con la terza idea, hai i seguenti passaggi di Greedy Three. Infatti, questo รจ l'algoritmo piรน ampiamente utilizzato.
- Ordinare i pacchetti in ordine non crescente del valore del costo unitario
. Hai:
- Considerare a turno i colli ordinati, mettere nello zaino il pacco in esame se la capacitร residua dello zaino รจ sufficiente a contenerlo (il che significa che il peso totale dei colli messi nello zaino e il peso dei colli in esame non superano la capacitร dello zaino).
L'idea: L'idea avida di questo problema รจ calcolare il rapporto di ciascuno
. Quindi ordina questi rapporti in ordine decrescente. Sceglierai il piรน alto
pacco e la capacitร dello zaino puรฒ contenere quel pacco (rimanere > wi). Ogni volta che un pacco viene inserito nello zaino, si ridurrร anche la capacitร dello zaino.
Modo per selezionare i pacchetti:
- Consideriamo la matrice dei costi unitari. Seleziona i pacchetti in base ai costi unitari decrescenti.
- Supponiamo di aver trovato una soluzione parziale: (x1, โฆ, Xi).
- Il valore dello zaino si ottiene:
- Corrispondente al peso dei pacchi messi nello zaino:
- Pertanto, il limite di peso rimanente dello zaino รจ:
Passi dell'algoritmo
Vedi, questo รจ un problema per trovare max. L'elenco dei pacchetti รจ ordinato in ordine decrescente di costi unitari per considerare la ramificazione.
- Fase 1: La radice del nodo rappresenta lo stato iniziale dello zaino, dove non รจ stato selezionato alcun pacchetto.
- Valore totale = 0.
- Il limite superiore del nodo radice UpperBound = M * Costo unitario massimo.
- Fase 2: La radice del nodo avrร nodi figlio corrispondenti alla possibilitร di selezionare il pacchetto con il costo unitario maggiore. Per ciascun nodo si ricalcolano i parametri:
- TotalValue = TotalValue (vecchio) + numero di pacchetti selezionati * valore di ciascun pacchetto.
- M = M (vecchio) โ numero di colli selezionati * peso di ciascun pacco.
- UpperBound = TotalValue + M (nuovo) * Il costo unitario dell'imballaggio da considerare successivamente.
- Fase 3: Nei nodi figlio, darai prioritร alla ramificazione per il nodo che ha il limite superiore piรน grande. I figli di questo nodo corrispondono alla capacitร di selezionare il pacchetto successivo avente un costo unitario elevato. Per ciascun nodo รจ necessario ricalcolare i parametri TotalValue, M, UpperBound secondo la formula menzionata al punto 2.
- Fase 4: Ripetere il passo 3 con la nota: per i nodi con limite superiore inferiore o uguale al costo massimo temporaneo di un'opzione trovata, non รจ piรน necessario ramificarsi per quel nodo.
- Fase 5: Se tutti i nodi sono ramificati o tagliati, l'opzione piรน costosa รจ quella da cercare.
Pseudo codice per l'algoritmo:
Fractional Knapsack (Array W, Array V, int M) 1. for i <- 1 to size (V) 2. calculate cost[i] <- V[i] / W[i] 3. Sort-Descending (cost) 4. i โ 1 5. while (i <= size(V)) 6. if W[i] <= M 7. M โ M โ W[i] 8. total โ total + V[i]; 9. if W[i] > M 10. i โ i+1
La complessitร dell'algoritmo:
- Se si utilizza un semplice algoritmo di ordinamento (selezione, bolla...), la complessitร dell'intero problema รจ O(n2).
- Se si utilizza l'ordinamento rapido o l'ordinamento per fusione, la complessitร dell'intero problema รจ O(nlogn).
Java codice per Tre Avidi
- Innanzitutto, definisci la classe KnapsackPackage. Questa classe ha proprietร : peso, valore e costo corrispondente di ciascun pacco. Il costo della proprietร di questa classe viene utilizzato per l'ordinamento dell'attivitร nell'algoritmo principale. Il valore di ciascun costo รจ il
rapporto di ciascun pacchetto.
public class KnapsackPackage {
private double weight;
private double value;
private Double cost;
public KnapsackPackage(double weight, double value) {
super();
this.weight = weight;
this.value = value;
this.cost = new Double(value / weight);
}
public double getWeight() {
return weight;
}
public double getValue() {
return value;
}
public Double getCost() {
return cost;
}
}
- Quindi crei una funzione per eseguire l'algoritmo Greedy Three.
public void knapsackGreProc(int W[], int V[], int M, int n) {
KnapsackPackage[] packs = new KnapsackPackage[n];
for (int i = 0; i < n; i ++) {
packs[i] = new KnapsackPackage(W[i], V[i]);
}
Arrays.sort(packs, new Comparator<KnapsackPackage>() {
@Override
public int compare(KnapsackPackage kPackA, KnapsackPackage kPackB) {
return kPackB.getCost().compareTo(kPackA.getCost());
}
});
int remain = M;
double result = 0d;
int i = 0;
boolean stopProc = false;
while (!stopProc) {
if (packs[i].getWeight() <= remain) {
remain -= packs[i].getWeight();
result += packs[i].getValue();
System.out.println("Pack " + i + " - Weight " + packs[i].getWeight() + " - Value " + packs[i].getValue());
}
if (packs[i].getWeight() > remain) {
i ++;
}
if (i == n) {
stopProc = true;
}
}
System.out.println("Max Value:\t" + result);
}

Spiegazione del codice:
- Inizializza peso e valore per ogni pacco zaino.
- Ordina i pacchi zaino per costo con ordine decrescente.
- Se selezioni il pacchetto i.
- Se selezioni il numero del pacco i รจ sufficiente.
- Interrompi durante l'esplorazione di tutti i pacchetti.
In questo tutorial, hai due esempi. Ecco il codice Java per eseguire il programma sopra con due esempi:
public void run() {
/*
* Pack and Weight - Value
*/
//int W[] = new int[]{15, 10, 2, 4};
int W[] = new int[]{12, 2, 1, 1, 4};
//int V[] = new int[]{30, 25, 2, 6};
int V[] = new int[]{4, 2, 1, 2, 10};
/*
* Max Weight
*/
//int M = 37;
int M = 15;
int n = V.length;
/*
* Run the algorithm
*/
knapsackGreProc(W, V, M, n);
}
Hai l'output:
- Primo esempio:
Pack 0 - Weight 10.0 - Value 25.0 Pack 0 - Weight 10.0 - Value 25.0 Pack 0 - Weight 10.0 - Value 25.0 Pack 2 - Weight 4.0 - Value 6.0 Pack 3 - Weight 2.0 - Value 2.0 Max Value: 83.0
- Secondo esempio:
Pack 0 - Weight 4.0 - Value 10.0 Pack 0 - Weight 4.0 - Value 10.0 Pack 0 - Weight 4.0 - Value 10.0 Pack 1 - Weight 1.0 - Value 2.0 Pack 1 - Weight 1.0 - Value 2.0 Pack 1 - Weight 1.0 - Value 2.0 Max Value: 36.0
Analizziamo il primo esempio:
- I parametri del problema sono: n = 4; M = 37.
- I pacchetti: {i = 1; W[i] = 15; V[i] = 30; Costo = 2.0}; {io = 2; W[i] = 10; V[i] = 25; Costo = 2.5}; {i = 3; W[i] = 2; V[i] = 4; Costo = 1.0}; {i = 4; W[i] = 4; V[i] = 6; Costo = 1.5}.
- Ordina i pacchetti nell'ordine in cui non aumenta il valore dei costi unitari. Hai: {i = 2} -> {io = 1} -> {io = 4} -> {i = 3}.
Passaggi per applicare l'algoritmo per il primo esempio:
- Definire x1, x2, x3, x4 รจ il numero di ciascun pacchetto selezionato, corrispondente al pacchetto {i = 2} -> {io = 1} -> {io = 4} -> {i = 3}.
- La radice del nodo N rappresenta lo stato in cui non hai selezionato alcun pacchetto. Poi:
- Valore totale = 0.
- M = 37 (come proposto).
- Limite superiore = 37 * 2.5 = 92.5, di cui 37 รจ M e 2.5 รจ il costo unitario del pacchetto {i = 2}.
- Con il pacchetto {i = 2} hai 4 possibilitร : seleziona 3 pacchetti {i = 2} (x1 = 3); seleziona 2 pacchetti {i = 2} (x1 = 2); selezionare 1 pacchetto {i = 2} (x1 = 1) e non selezionare il pacchetto {i = 2} (x1 = 0). In accordo con queste 4 possibilitร , si ramifica il nodo radice N in 4 figli N[1], N[2], N[3] e N[4].
- Per il nodo figlio N1, hai:
- TotalValue = 0 + 3 * 25 = 75, dove 3 รจ il numero del pacchetto {i = 2} selezionato e 25 รจ il valore di ciascun pacchetto {i = 2}.
- M = 37 โ 3 * 10 = 7, dove 37 รจ la quantitร iniziale dello zaino, 3 รจ il numero dei colli {i = 2}, 10 รจ il peso di ciascun pacco {i = 2}.
- Limite Superiore = 75 + 7 * 2 = 89, dove 75 รจ TotalValue, 7 รจ il peso rimanente dello zaino e 2 รจ il costo unitario del pacco {i = 1}.
- Allo stesso modo, รจ possibile calcolare i parametri per i nodi N[2], N[3] e N[4], in cui l'UpperBound รจ rispettivamente 84, 79 e 74.
- Tra i nodi N[1], N[2], N[3] e N[4], il nodo N[1] ha il limite superiore piรน grande, quindi diramerai prima il nodo N[1] nella speranza che ci sia un buon piano da questa direzione.
- Dal nodo N[1], hai un solo nodo figlio N[1-1] corrispondente a x2 = 0 (poichรฉ il peso rimanente dello zaino รจ 7, mentre il peso di ciascun pacco {i = 1} รจ 15) . Dopo aver determinato i parametri per il pulsante N[1-1], il limite superiore di N[1-1] รจ 85.5.
- Si continua a ramificare il nodo N[1-1]. Il nodo N[1-1] ha 2 figli N[1-1-1] e N[1-1-2] corrispondenti a x3 = 1 e x3 = 0. Dopo aver determinato i parametri per questi due nodi, vedi che il Il limite superiore di N[1-1-1] รจ 84 e quello di N[1-1-2] รจ 82, quindi continui a ramificare il nodo N[1-1-1].
- Il nodo N[1-1-1] ha due figli, N[1-1-1-1] e N[1-1-1-2], corrispondenti a x4 = 1 e x4 = 0. Questi sono due nodi foglia (che rappresenta l'opzione) perchรฉ per ogni nodo รจ stato selezionato il numero di colli. In cui il nodo N[1-1-1-1] rappresenta l'opzione x1 = 3, x2 = 0, x3 = 1 e x4 = 1 per 83, mentre il nodo N[1-1-1-2] rappresenta l'opzione x1 = 3, x2 = 0, x3 = 1 e x4 = 01 su 81. Quindi il valore massimo temporaneo qui รจ 83.
- Tornando al nodo N[1-1-2], vedi che il limite superiore di N[1-1-2] รจ 82 < 83, quindi tagli il nodo N[1-1-2].
- Tornando al nodo N2, vedi che il limite superiore di N2 รจ 84 > 83, quindi continui a ramificare il nodo N2. Il nodo N2 ha due figli N[2-1] e N[2-2] corrispondenti a x2 = 1 e x2 = 0. Dopo aver calcolato i parametri per N[2-1] e N[2-2], vedi il limite superiore di N[2-1] รจ 83 e quello di N[2-2] รจ 75.25. Nessuno di questi valori รจ maggiore di 83, quindi entrambi i nodi vengono tagliati.
- Infine vengono tagliati anche i nodi N3 e N4.
- Quindi tutti i nodi dell'albero sono ramificati o tagliati, quindi la migliore soluzione temporanea รจ quella da cercare. Di conseguenza, รจ necessario selezionare 3 pacchi {i = 2}, 1 pacco {i = 4} e un pacco {i = 3} con un valore totale di 83, il peso totale รจ 36.
Con la stessa analisi del secondo esempio si ottiene il risultato: selezionare il pacchetto 4 (3 volte) e il pacchetto 5 (3 volte).
Python3 codice per Tre Avido
- Innanzitutto, definisci la classe KnapsackPackage.
class KnapsackPackage(object):
""" Knapsack Package Data Class """
def __init__(self, weight, value):
self.weight = weight
self.value = value
self.cost = value / weight
def __lt__(self, other):
return self.cost < other.cost
- Quindi crei una funzione per eseguire l'algoritmo Greedy Three.
class FractionalKnapsack(object):
def __init__(self):
def knapsackGreProc(self, W, V, M, n):
packs = []
for i in range(n):
packs.append(KnapsackPackage(W[i], V[i]))
packs.sort(reverse = True)
remain = M
result = 0
i = 0
stopProc = False
while (stopProc != True):
if (packs[i].weight <= remain):
remain -= packs[i].weight;
result += packs[i].value;
print("Pack ", i, " - Weight ", packs[i].weight, " - Value ", packs[i].value)
if (packs[i].weight > remain):
i += 1
if (i == n):
stopProc = True
print("Max Value:\t", result)

Spiegazione del codice:
- Inizializza peso e valore per ogni pacco zaino.
- Ordina i pacchi zaino per costo con ordine decrescente.
- Se selezioni il pacchetto i.
- Se selezioni il numero del pacco i รจ sufficiente.
- Interrompi durante l'esplorazione di tutti i pacchetti.
Qui รจ Python3 codice per eseguire il programma sopra con il primo esempio:
if __name__ == "__main__":
W = [15, 10, 2, 4]
V = [30, 25, 2, 6]
M = 37
n = 4
proc = FractionalKnapsack()
proc.knapsackGreProc(W, V, M, n)
Hai l'output:
Pack 0 - Weight 10 - Value 25 Pack 0 - Weight 10 - Value 25 Pack 0 - Weight 10 - Value 25 Pack 2 - Weight 4 - Value 6 Pack 3 - Weight 2 - Value 2 Max Value: 83
Codice C# per Greedy Three
- Innanzitutto, definisci la classe KnapsackPackage.
using System;
namespace KnapsackProblem
{
public class KnapsackPackage
{
private double weight;
private double value;
private double cost;
public KnapsackPackage(double weight, double value)
{
this.weight = weight;
this.value = value;
this.cost = (double) value / weight;
}
public double Weight
{
get { return weight; }
}
public double Value
{
get { return value; }
}
public double Cost
{
get { return cost; }
}
}
}
- Quindi crei una funzione per eseguire l'algoritmo Greedy Three.
using System;
namespace KnapsackProblem
{
public class FractionalKnapsack
{
public FractionalKnapsack()
{
}
public void KnapsackGreProc(int[] W, int[] V, int M, int n)
{
KnapsackPackage[] packs = new KnapsackPackage[n];
for (int k = 0; k < n; k++)
packs[k] = new KnapsackPackage(W[k], V[k]);
Array.Sort<KnapsackPackage>(packs, new Comparison<KnapsackPackage>(
(kPackA, kPackB) => kPackB.Cost.CompareTo(kPackA.Cost)));
double remain = M;
double result = 0d;
int i = 0;
bool stopProc = false;
while (!stopProc)
{
if (packs[i].Weight <= remain)
{
remain -= packs[i].Weight;
result += packs[i].Value;
Console.WriteLine("Pack " + i + " - Weight " + packs[i].Weight + " - Value " + packs[i].Value);
}
if (packs[i].Weight > remain)
{
i++;
}
if (i == n)
{
stopProc = true;
}
}
Console.WriteLine("Max Value:\t" + result);
}
}
}

Spiegazione del codice:
- Inizializza peso e valore per ogni pacco zaino.
- Ordina i pacchi zaino per costo con ordine decrescente.
- Se selezioni il pacchetto i.
- Se selezioni il numero del pacco i รจ sufficiente.
- Interrompi durante l'esplorazione di tutti i pacchetti.
Ecco il codice C# per eseguire il programma sopra con il primo esempio:
public void run()
{
/*
* Pack and Weight - Value
*/
int[] W = new int[]{15, 10, 2, 4};
//int[] W = new int[] { 12, 2, 1, 1, 4 };
int[] V = new int[]{30, 25, 2, 6};
//int[] V = new int[] { 4, 2, 1, 2, 10 };
/*
* Max Weight
*/
int M = 37;
//int M = 15;
int n = V.Length;
/*
* Run the algorithm
*/
KnapsackGreProc(W, V, M, n);
}
Hai l'output:
Pack 0 - Weight 10 - Value 25 Pack 0 - Weight 10 - Value 25 Pack 0 - Weight 10 - Value 25 Pack 2 - Weight 4 - Value 6 Pack 3 - Weight 2 - Value 2 Max Value: 83
Controesempio di Greedy Three
L'algoritmo di Greedy Three risolve rapidamente e in alcuni casi puรฒ anche essere ottimale. Tuttavia, in alcuni casi particolari, non fornisce la soluzione ottimale. Ecco un controesempio:
- I parametri del problema sono: n = 3; M = 10.
- I pacchetti: {i = 1; W[i] = 7; V[i] = 9; Costo = 9/7}; {io = 2; W[i] = 6; V[i] = 6; Costo = 1}; {i = 3; W[i] = 4; V[i] = 4; Costo = 1}.
- L'algoritmo selezionerร (pacchetto 1) con un valore totale pari a 9, mentre la soluzione ottimale del problema sarร (pacchetto 2, pacchetto 3) con un valore totale pari a 10.
Ecco il codice Java per eseguire il programma sopra con il controesempio:
public void run() {
/*
* Pack and Weight - Value
*/
int W[] = new int[]{7, 6, 4};
int V[] = new int[]{9, 6, 4};
/*
* Max Weight
*/
int M = 10;
int n = V.length;
/*
* Run the algorithm
*/
knapsackGreProc(W, V, M, n);
}
Ecco il risultato:
Pack 0 - Weight 7.0 - Value 9.0 Max Value: 9.0
Questo รจ tutto per il problema dello Zaino Frazionario.





