Estou procurando maximizar o número de estrelas com um determinado orçamento e limite máximo na combinação.
Pergunta de exemplo:
Com um orçamento de 500 euros, visitando apenas o máximo permitido de restaurantes ou menos, jante e colete o máximo de estrelas possível.
Estou procurando escrever um algoritmo eficiente, que possa potencialmente processar 1 milhão de instâncias de restaurantes para até 10 restaurantes no máximo.
Observe que esta é uma postagem cruzada de uma pergunta que fiz ontem: Java: obtenha a combinação mais eficiente de uma grande lista de objetos com base em um campo
A solução abaixo atribuirá 15 $ por estrela ao r8
Restaurante, o que significa que, ao gerar a lista, ela é colocada em primeiro lugar e, com os restantes 70 $, só pode obter mais 2 estrelas, totalizando 4 estrelas. No entanto, se fosse inteligente o suficiente para pular o r8
restaurante (mesmo que seja a melhor relação dólar por estrela), o r1
restaurante seria realmente uma escolha melhor para o orçamento, pois custa 100 dólares e 5 estrelas.
Alguém pode ajudar a tentar o problema e vencer a solução atual?
import itertools
class Restaurant():
def __init__(self, cost, stars):
self.cost = cost
self.stars = stars
self.ratio = cost / stars
def display(self):
print("Cost: $" + str(self.cost))
print("Stars: " + str(self.stars))
print()
r1 = Restaurant(100, 5)
r2 = Restaurant(140, 3)
r3 = Restaurant(90, 4)
r4 = Restaurant(140, 3)
r5 = Restaurant(120, 4)
r6 = Restaurant(60, 1)
r7 = Restaurant(40, 1)
r8 = Restaurant(30, 2)
r9 = Restaurant(70, 2)
r10 = Restaurant(250, 5)
print()
print("***************")
print("** Unsorted: **")
print("***************")
print()
restaurants = [r1, r2, r3, r4, r5, r6, r7, r8, r9, r10]
for restaurant in restaurants:
print(restaurant.ratio, restaurant.stars)
print()
print("***************")
print("** Sorted: **")
print("***************")
print()
sorted_restaurants = sorted(restaurants, key = lambda x: x.ratio, reverse = True)
for restaurant in sorted_restaurants:
print(restaurant.ratio, restaurant.stars)
print()
print("*********************")
print("** Begin Rucksack: **")
print("*********************")
print()
max = 5
budget = 100
spent = 0
quantity = 0
rucksack = []
for i in itertools.count():
if len(rucksack) >= max or i == len(sorted_restaurants):
break
sorted_restaurants[i].display()
if sorted_restaurants[i].cost + spent <= budget:
spent = spent + sorted_restaurants[i].cost
rucksack.append(sorted_restaurants[i])
print("Total Cost: $" + str(sum([x.cost for x in rucksack])))
print("Total Stars: " + str(sum([x.stars for x in rucksack])))
print()
print("*****************")
print("** Final List: **")
print("*****************")
print()
for restaurant in rucksack:
restaurant.display()
budget
= peso mochila máximo em kg,max
= número de itens da mochila pode conter,stars
= algum valor no item ecost
peso = item em kgr8
restaurante, o que significa que, ao gerar a lista, coloca-o na lista primeiro e, com os 70 dólares restantes, ele pode obter apenas mais 2 estrelas. No entanto, se fosse inteligente o suficiente para ignorar isso (mesmo que seja a melhor relação dólar por estrela, or1
restaurante seria realmente uma escolha melhor para o orçamento, pois custa 100 dólares e 5 estrelasRespostas:
Parece que seu problema é praticamente o mesmo que o problema da mochila: Maximize o valor, considerando certas restrições de peso e volume. Basicamente, valor = total de estrelas, peso = preço, limite da mochila = orçamento total. Agora, há uma restrição adicional ao total de "itens" (visitas a restaurantes), mas isso não muda a essência.
Como você pode ou não saber, o problema da mochila é difícil de NP, o que significa que nenhum algoritmo com escala de tempo polinomial é conhecido.
No entanto, pode haver algoritmos pseudopolinomiais eficientes usando programação dinâmica e, é claro, existem heurísticas eficientes, como a heurística "gananciosa" que você parece ter descoberto. Essa heurística envolve começar a preencher os itens de "densidade" mais altos (a maioria das estrelas por dólar) primeiro. Como você viu, essa heurística falha em encontrar o verdadeiro ideal em alguns casos.
A abordagem de programação dinâmica deve ser muito boa aqui. É baseado em uma recursão: dado um orçamento B e várias visitas restantes V, qual é o melhor conjunto de restaurantes para visitar de um conjunto total de restaurantes R?
Veja aqui: https://en.wikipedia.org/wiki/Knapsack_problem#0/1_knapsack_problem
Basicamente, definimos uma matriz
m
para "máximas estrelas", ondem[i, b, v]
é a quantidade máxima de estrelas que podemos obter quando nos é permitido visitar restaurantes com até (incluindo) número de restaurantei
, gastar no máximob
e visitar na maioria dosv
restaurantes (o limite) .Agora, de baixo para cima preenchemos esta matriz. Por exemplo,
m[0, b, v] = 0
para todos os valores deb
ev
porque, se não podemos ir a nenhum restaurante, não podemos obter estrelas.Além disso,
m[i, b, 0] = 0
para todos os valores dei
eb
porque se esgotássemos todas as nossas visitas, não conseguiríamos mais estrelas.A próxima linha também não é muito difícil:
m[i, b, v] = m[i - 1, b, v] if p[i] > b
Ondep[i]
está o preço das refeições no restaurantei
? O que esta linha diz? Bem, se o restaurantei
é mais caro do que o que resta (b
), não podemos ir até lá. O que significa que a quantidade máxima de estrelas que podemos obter é a mesma, independentemente de incluirmos restaurantes com atéi
ou apenas atéi - 1
.A próxima linha é um pouco complicada:
m[i, b, v] = max(m[i-1, b, v]), m[i-1, b - p[i], v-1] + s[i]) if p[i] <= b
Ufa.
s[i]
é a quantidade de estrelas que você recebe do restaurantei
btw.O que esta linha diz? É o coração da abordagem de programação dinâmica. Ao considerar a quantidade máxima de estrelas que podemos obter ao procurar em restaurantes
i
, inclusive , na solução resultante, vamos para lá ou não, e "apenas" precisamos ver qual desses dois caminhos leva a mais estrelas:Se não formos a um restaurante
i
, mantemos a mesma quantidade de dinheiro e as visitas restantes. A quantidade máxima de estrelas que podemos obter nesse caminho é a mesma que se nem olhávamos no restaurantei
. Essa é a primeira parte domax
.Mas se formos a um restaurante
i
, ficaremos comp[i]
menos dinheiro, menos uma visita es[i]
mais estrelas. Essa é a segunda parte domax
.Agora a questão é simples: qual dos dois é maior.
Você pode criar essa matriz e preenchê-la com um loop for relativamente simples (inspire-se no wiki). Isso apenas fornece a quantidade de estrelas, mas não a lista real de restaurantes para visitar. Para isso, adicione alguma contabilidade extra ao cálculo de
w
.Espero que a informação seja suficiente para levá-lo na direção certa.
Como alternativa, você pode escrever seu problema em termos de variáveis binárias e uma função objetiva quadrática e resolvê-lo no analisador quântico D-Wave :-p Envie-me uma mensagem se quiser saber mais sobre isso.
fonte
Usando a mesma idéia da minha resposta aqui :
você pode criar a lista a partir dos possíveis restaurantes "mais baratos" .
As etapas do algoritmo:
Obviamente, você não pode selecionar novamente um restaurante.
Eu acho que no pior caso, você terá que calcular 5x5x5 ... = 5 ^ 10 + 5 ^ 9 + ... + 5 ^ 2 + 5 (= cerca de 12 milhões) soluções.
Em javascript
fonte