Problema da mochila fracionada: algoritmo ganancioso com exemplo
O que รฉ estratรฉgia gananciosa?
Algoritmos gananciosos sรฃo como algoritmos de programaรงรฃo dinรขmica que sรฃo frequentemente usados โโpara resolver problemas รณtimos (encontrar as melhores soluรงรตes do problema de acordo com um critรฉrio especรญfico).
Algoritmos gananciosos implementam seleรงรตes locais รณtimas na esperanรงa de que essas seleรงรตes levem a uma soluรงรฃo global รณtima para o problema a ser resolvido. Algoritmos gananciosos geralmente nรฃo sรฃo muito difรญceis de configurar e sรฃo rรกpidos (a complexidade do tempo costuma ser uma funรงรฃo linear ou, em grande parte, uma funรงรฃo de segunda ordem). Alรฉm disso, esses programas nรฃo sรฃo difรญceis de depurar e usam menos memรณria. Mas os resultados nem sempre sรฃo uma soluรงรฃo ideal.
Estratรฉgias gananciosas sรฃo frequentemente usadas para resolver o problema de otimizaรงรฃo combinatรณria construindo uma opรงรฃo A. A opรงรฃo A รฉ construรญda selecionando cada componente Ai de A atรฉ completar (n componentes suficientes). Para cada Ai, vocรช escolhe Ai de forma otimizada. Desta forma, รฉ possรญvel que na รบltima etapa vocรช nรฃo tenha nada para selecionar a nรฃo ser aceitar o รบltimo valor restante.
Existem dois componentes crรญticos de decisรตes gananciosas:
- Forma de seleรงรฃo gananciosa. Vocรช pode selecionar qual soluรงรฃo รฉ a melhor no momento e entรฃo resolver o subproblema resultante da รบltima seleรงรฃo. A seleรงรฃo de algoritmos gananciosos pode depender de seleรงรตes anteriores. Mas nรฃo pode depender de nenhuma seleรงรฃo futura ou das soluรงรตes de subproblemas. O algoritmo evolui de forma a fazer seleรงรตes em loop, ao mesmo tempo que reduz o problema em questรฃo a subproblemas menores.
- Subestrutura ideal. Vocรช executa a subestrutura รณtima para um problema se a soluรงรฃo รณtima desse problema contiver soluรงรตes รณtimas para seus subproblemas.
Um algoritmo ganancioso tem cinco componentes:
- Um conjunto de candidatos a partir dos quais criar soluรงรตes.
- Uma funรงรฃo de seleรงรฃo, para selecionar o melhor candidato para adicionar ร soluรงรฃo.
- Uma funรงรฃo viรกvel รฉ usada para decidir se um candidato pode ser usado para construir uma soluรงรฃo.
- Uma funรงรฃo objetivo, fixando o valor de uma soluรงรฃo ou de uma soluรงรฃo incompleta.
- Uma funรงรฃo de avaliaรงรฃo, indicando quando vocรช encontra uma soluรงรฃo completa.
A ideia do ganancioso
Com a primeira ideia, vocรช tem os seguintes passos do Greedy One:
- Classifique em ordem nรฃo crescente de valores.
- Por sua vez, considere os pacotes encomendados, coloque o pacote considerado na mochila se a capacidade restante da mochila for suficiente para contรช-lo (o que significa que o peso total dos pacotes que foram colocados na mochila e o peso dos pacotes considerados nรฃo excedem a capacidade da mochila).
No entanto, este algoritmo ganancioso nem sempre fornece a soluรงรฃo ideal. Aqui vocรช tem um contra-exemplo:
- Os parรขmetros do problema sรฃo: n = 3; M = 19.
- Os pacotes: {i = 1; W[i] = 14; V[i] = 20}; {eu = 2; W[i] = 6; V[i] = 16}; {eu = 3; W[i] = 10; V[eu] = 8} -> รtimo valor, mas tambรฉm รณtimo peso.
- O algoritmo selecionarรก o pacote 1 com valor total de 20, enquanto a soluรงรฃo รณtima do problema serรก selecionada (pacote 2, pacote 3) com valor total de 24.
A ideia dos dois gananciosos
Com a segunda ideia, vocรช tem as seguintes etapas do Greedy Two:
- Classifique em ordem nรฃo decrescente de pesos.
- Por sua vez, considere os pacotes encomendados, coloque o pacote considerado na mochila se a capacidade restante da mochila for suficiente para contรช-lo (o que significa que o peso total dos pacotes que foram colocados na mochila e o peso dos pacotes considerados nรฃo excedem a capacidade da mochila).
No entanto, este algoritmo ganancioso nem sempre fornece a soluรงรฃo ideal. Aqui vocรช tem um contra-exemplo:
- Os parรขmetros do problema sรฃo: n = 3; M = 11.
- Os pacotes: {i = 1; W[i] = 5; V[i] = 10}; {eu = 2; W[i] = 6; V[i] = 16}; {eu = 3; W[i] = 10; V[eu] = 28} -> Peso leve, mas o valor tambรฉm รฉ muito leve.
- O algoritmo selecionarรก (pacote 1, pacote 2) com valor total de 26, enquanto a soluรงรฃo รณtima do problema รฉ (pacote 3) com valor total de 28.
A ideia dos trรชs gananciosos
Com a terceira ideia, vocรช tem as seguintes etapas do Greedy Three. Na verdade, este รฉ o algoritmo mais utilizado.
- Classifique os pacotes em ordem nรฃo crescente do valor do custo unitรกrio
. Vocรช tem:
- Por sua vez, considere os pacotes encomendados, coloque o pacote considerado na mochila se a capacidade restante da mochila for suficiente para contรช-lo (o que significa que o peso total dos pacotes que foram colocados na mochila e o peso dos pacotes considerados nรฃo excedem a capacidade da mochila).
idรฉia: A ideia gananciosa desse problema รฉ calcular o proporรงรฃo de cada
. Em seguida, classifique essas proporรงรตes em ordem decrescente. Vocรช escolherรก o mais alto
pacote e a capacidade da mochila pode conter esse pacote (permanece > wi). Cada vez que um pacote รฉ colocado na mochila, isso tambรฉm reduz a capacidade da mochila.
Forma de selecionar os pacotes:
- Considere a matriz de custos unitรกrios. Vocรช seleciona pacotes de acordo com custos unitรกrios decrescentes.
- Suponha que vocรช encontrou uma soluรงรฃo parcial: (x1,โฆ, Xi).
- O valor da mochila รฉ obtido:
- Correspondente ao peso dos pacotes colocados na mochila:
- Portanto, o limite de peso restante da mochila รฉ:
Etapas do Algoritmo
Vocรช vรช que este รฉ um problema de encontrar o mรกximo. A lista de pacotes รฉ classificada em ordem decrescente de custos unitรกrios para considerar a ramificaรงรฃo.
- Etapa 1: O nรณ raiz representa o estado inicial da mochila, onde vocรช nรฃo selecionou nenhum pacote.
- ValorTotal = 0.
- O limite superior do nรณ raiz UpperBound = M * Custo unitรกrio mรกximo.
- Etapa 2: O nรณ raiz terรก nรณs filhos correspondentes ร capacidade de selecionar o pacote com o maior custo unitรกrio. Para cada nรณ, vocรช recalcula os parรขmetros:
- TotalValue = TotalValue (antigo) + nรบmero de pacotes selecionados * valor de cada pacote.
- M = M (antigo) โ quantidade de embalagens selecionadas * peso de cada embalagem.
- UpperBound = TotalValue + M (novo) * O custo unitรกrio do pacote a ser considerado a seguir.
- Etapa 3: em nรณs filhos, vocรช priorizarรก a ramificaรงรฃo para o nรณ que tiver o limite superior maior. Os filhos deste nรณ correspondem ร capacidade de selecionar o prรณximo pacote de grande custo unitรกrio. Para cada nรณ, deve-se recalcular os parรขmetros TotalValue, M, UpperBound conforme fรณrmula citada no passo 2.
- Etapa 4: Repita a Etapa 3 com a observaรงรฃo: para nรณs com limite superior com valores inferiores ou iguais ao custo mรกximo temporรกrio de uma opรงรฃo encontrada, vocรช nรฃo precisa mais ramificar para esse nรณ.
- Etapa 5: Se todos os nรณs estiverem ramificados ou cortados, a opรงรฃo mais cara รฉ a que deve ser procurada.
Pseudocรณdigo para o 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
A complexidade do algoritmo:
- Se estiver usando um algoritmo de classificaรงรฃo simples (seleรงรฃo, bolhaโฆ), entรฃo a complexidade de todo o problema รฉ O(n2).
- Se estiver usando classificaรงรฃo rรกpida ou classificaรงรฃo por mesclagem, a complexidade de todo o problema รฉ O (nlogn).
Java cรณdigo para trรชs gananciosos
- Primeiramente, vocรช define a classe KnapsackPackage. Esta classe possui propriedades que sรฃo: peso, valor e custo correspondente de cada pacote. O custo da propriedade desta classe รฉ usado para classificar tarefas no algoritmo principal. O valor de cada custo รฉ o
proporรงรฃo de cada pacote.
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;
}
}
- Vocรช entรฃo cria uma funรงรฃo para executar o 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);
}

Explicaรงรฃo do cรณdigo:
- Inicialize o peso e o valor de cada pacote de mochila.
- Classifique os pacotes de mochila por custo em ordem decrescente.
- Se selecionar o pacote i.
- Se selecionar o nรบmero do pacote i รฉ suficiente.
- Pare ao navegar em todos os pacotes.
Neste tutorial, vocรช tem dois exemplos. Aqui estรก o cรณdigo java para executar o programa acima com dois exemplos:
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);
}
Vocรช tem a saรญda:
- Primeiro exemplo:
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
- Segundo exemplo:
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
Analise o primeiro exemplo:
- Os parรขmetros do problema sรฃo: n = 4; M = 37.
- Os pacotes: {i = 1; W[i] = 15; V[i] = 30; Custo = 2.0}; {eu = 2; W[i] = 10; V[i] = 25; Custo = 2.5}; {eu = 3; W[i] = 2; V[i] = 4; Custo = 1.0}; {eu = 4; W[i] = 4; V[i] = 6; Custo = 1.5}.
- Vocรช classifica os pacotes em ordem sem aumento do valor dos custos unitรกrios. Vocรช tem: {i = 2} -> {eu = 1} -> {eu = 4} -> {eu = 3}.
Etapas para aplicar o algoritmo para o primeiro exemplo:
- Defina x1, x2, x3, x4 รฉ o nรบmero de cada pacote selecionado, correspondente ao pacote {i = 2} -> {eu = 1} -> {eu = 4} -> {eu = 3}.
- A raiz do nรณ N representa o estado em que vocรช nรฃo selecionou nenhum pacote. Entรฃo:
- ValorTotal = 0.
- M = 37 (conforme proposto).
- UpperBound = 37 * 2.5 = 92.5, dos quais 37 รฉ M e 2.5 รฉ o custo unitรกrio do pacote {i = 2}.
- Com o pacote {i = 2}, vocรช tem 4 possibilidades: selecione 3 pacotes {i = 2} (x1 = 3); selecione 2 pacotes {i = 2} (x1 = 2); selecione 1 pacote {i = 2} (x1 = 1) e nรฃo selecione o pacote {i = 2} (x1 = 0). De acordo com essas 4 possibilidades, vocรช ramifica o nรณ raiz N para 4 filhos N[1], N[2], N[3] e N[4].
- Para o nรณ filho N1, vocรช tem:
- TotalValue = 0 + 3 * 25 = 75, onde 3 รฉ o nรบmero de pacotes {i = 2} selecionados e 25 รฉ o valor de cada pacote {i = 2}.
- M = 37 โ 3 * 10 = 7, onde 37 รฉ a quantidade inicial da mochila, 3 รฉ o nรบmero do pacote {i = 2}, 10 รฉ o peso de cada pacote {i = 2}.
- UpperBound = 75 + 7 * 2 = 89, onde 75 รฉ TotalValue, 7 รฉ o peso restante da mochila e 2 รฉ o custo unitรกrio do pacote {i = 1}.
- Da mesma forma, vocรช pode calcular os parรขmetros para os nรณs N[2], N[3] e N[4], nos quais o UpperBound รฉ 84, 79 e 74 respectivamente.
- Entre os nรณs N[1], N[2], N[3] e N[4], o nรณ N[1] tem o maior UpperBound, entรฃo vocรช ramificarรก o nรณ N[1] primeiro na esperanรงa de que haja um bom plano desta direรงรฃo.
- Do nรณ N[1], vocรช tem apenas um nรณ filho N[1-1] correspondente a x2 = 0 (devido ao peso restante da mochila ser 7, enquanto o peso de cada pacote {i = 1} รฉ 15) . Depois de determinar os parรขmetros para o botรฃo N[1-1], vocรช terรก que o UpperBound de N[1-1] รฉ 85.5.
- Vocรช continua ramificando o nรณ N[1-1]. O nรณ N[1-1] tem 2 filhos N[1-1-1] e N[1-1-2] correspondentes a x3 = 1 e x3 = 0. Depois de determinar os parรขmetros para esses dois nรณs, vocรช vรช que o UpperBoundary de N[1-1-1] รฉ 84 e o de N[1-1-2] รฉ 82, entรฃo vocรช continua ramificando o nรณ N[1-1-1].
- O nรณ N[1-1-1] tem dois filhos, N[1-1-1-1] e N[1-1-1-2], correspondendo a x4 = 1 e x4 = 0. Estes sรฃo dois nรณs folha (representando a opรงรฃo) porque para cada nรณ foi selecionado o nรบmero de pacotes. Em que o nรณ N[1-1-1-1] representa a opรงรฃo x1 = 3, x2 = 0, x3 = 1 e x4 = 1 para 83, enquanto o nรณ N[1-1-1-2] representa a opรงรฃo x1 = 3, x2 = 0, x3 = 1 e x4 = 01 em 81. Portanto, o valor mรกximo temporรกrio aqui รฉ 83.
- Voltando ao nรณ N[1-1-2], vocรช vรช que o UpperBound de N[1-1-2] รฉ 82 <83, entรฃo vocรช corta o nรณ N[1-1-2].
- Voltando ao nรณ N2, vocรช vรช que o UpperBound de N2 รฉ 84> 83, entรฃo continua ramificando o nรณ N2. O nรณ N2 tem dois filhos N[2-1] e N[2-2] correspondentes a x2 = 1 e x2 = 0. Depois de calcular os parรขmetros para N[2-1] e N[2-2], vocรช vรช o limite superior de N[2-1] รฉ 83 e o de N[2-2] รฉ 75.25. Nenhum desses valores รฉ maior que 83, portanto ambos os nรณs sรฃo cortados.
- Finalmente, os nรณs N3 e N4 tambรฉm sรฃo cortados.
- Assim, todos os nรณs da รกrvore sรฃo ramificados ou aparados, de modo que a melhor soluรงรฃo temporรกria รฉ aquela a ser procurada. Assim, vocรช precisa selecionar 3 pacotes {i = 2}, 1 pacote {i = 4} e um pacote {i = 3} com valor total de 83, o peso total รฉ 36.
Com a mesma anรกlise do segundo exemplo, vocรช tem o resultado: selecione o pacote 4 (3 vezes) e o pacote 5 (3 vezes).
Python3 cรณdigo para Trรชs gananciosos
- Primeiramente, vocรช define a 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
- Vocรช entรฃo cria uma funรงรฃo para executar o 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)

Explicaรงรฃo do cรณdigo:
- Inicialize o peso e o valor de cada pacote de mochila.
- Classifique os pacotes de mochila por custo em ordem decrescente.
- Se selecionar o pacote i.
- Se selecionar o nรบmero do pacote i รฉ suficiente.
- Pare ao navegar em todos os pacotes.
Aqui estรก Python3 cรณdigo para executar o programa acima com o primeiro exemplo:
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)
Vocรช tem a saรญda:
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
Cรณdigo C# para Greedy Three
- Primeiramente, vocรช define a 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; }
}
}
}
- Vocรช entรฃo cria uma funรงรฃo para executar o 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);
}
}
}

Explicaรงรฃo do cรณdigo:
- Inicialize o peso e o valor de cada pacote de mochila.
- Classifique os pacotes de mochila por custo em ordem decrescente.
- Se selecionar o pacote i.
- Se selecionar o nรบmero do pacote i รฉ suficiente.
- Pare ao navegar em todos os pacotes.
Aqui estรก o cรณdigo C# para executar o programa acima com o primeiro exemplo:
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);
}
Vocรช tem a saรญda:
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
Contra-exemplo de Greedy Three
O algoritmo do Greedy Three รฉ resolvido rapidamente e tambรฉm pode ser ideal em alguns casos. Contudo, em alguns casos especiais, nรฃo fornece a soluรงรฃo ideal. Aqui vocรช tem um contra-exemplo:
- Os parรขmetros do problema sรฃo: n = 3; M = 10.
- Os pacotes: {i = 1; W[i] = 7; V[i] = 9; Custo = 9/7}; {eu = 2; W[i] = 6; V[i] = 6; Custo = 1}; {eu = 3; W[i] = 4; V[i] = 4; Custo = 1}.
- O algoritmo selecionarรก (pacote 1) com valor total de 9, enquanto a soluรงรฃo รณtima do problema serรก (pacote 2, pacote 3) com valor total de 10.
Aqui estรก o cรณdigo java para executar o programa acima com o contra-exemplo:
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);
}
Aqui estรก o resultado:
Pack 0 - Weight 7.0 - Value 9.0 Max Value: 9.0
Isso รฉ tudo para o problema da mochila fracionรกria.





