Je cherche à maximiser le nombre d'étoiles étant donné un certain budget et une limite maximale sur la combinaison.
Exemple de question:
Avec un budget de 500 euros, ne visitant que le maximum de restaurants autorisés, dîner et collecter le plus d'étoiles possible.
Je cherche à écrire un algorithme efficace, qui pourrait potentiellement traiter 1 million d'instances de restaurant pour un maximum de 10 restaurants.
Remarque, ceci est une publication croisée d'une question que j'ai posée hier: Java: Obtenez la combinaison la plus efficace d'une grande liste d'objets basée sur un champ
La solution ci-dessous attribuera 15 $ par étoile au r8
restaurant, ce qui signifie que lors de la génération de la liste, il la place en premier dans la liste, et avec les 70 $ restants, elle ne peut obtenir que 2 étoiles supplémentaires, soit un total de 4 étoiles. Cependant, s'il était assez intelligent pour sauter le r8
restaurant (même s'il s'agit du meilleur rapport dollar / étoile), ler1
qualité restaurant serait en fait un meilleur choix pour le budget, car il coûte 100 $ et 5 étoiles.
Quelqu'un peut-il aider à tenter de résoudre le problème et à battre la solution actuelle?
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
= poids maximum du sac à dos en kg,max
= nombre d'articles que le sac à dos peut contenir,stars
= une certaine valeur sur l'article etcost
= poids de l'article en kgr8
restaurant, ce qui signifie que lors de la génération de la liste, il le met en premier, et avec les 70 $ restants, il ne peut obtenir que 2 étoiles supplémentaires. Cependant, s'il était assez intelligent pour ignorer cela (même s'il s'agit du meilleur ratio dollar par étoile, ler1
restaurant serait en fait un meilleur choix pour le budget, car il coûte 100 $ et 5 étoilesRéponses:
On dirait que votre problème est à peu près le même que le problème du sac à dos: maximisez la valeur en fonction de certaines contraintes de poids et de volume. Fondamentalement, valeur = nombre total d'étoiles, poids = prix, limite de sac à dos = budget total. Maintenant, il y a une contrainte supplémentaire du total des «articles» (visites au restaurant), mais cela ne change pas l'essentiel.
Comme vous le savez peut-être ou non, le problème du sac à dos est NP difficile, ce qui signifie qu'aucun algorithme avec une échelle de temps polynomiale n'est connu.
Cependant, il peut y avoir des algorithmes pseudopolynomiaux efficaces utilisant la programmation dynamique, et bien sûr il existe des heuristiques efficaces, comme l'heuristique "gourmande" que vous semblez avoir découverte. Cette heuristique implique de commencer à se remplir avec les éléments de «densité» les plus élevés (la plupart des étoiles par mâle) en premier. Comme vous l'avez vu, cette heuristique ne parvient pas à trouver le véritable optimum dans certains cas.
L'approche de programmation dynamique devrait être assez bonne ici. Il est basé sur une récursivité: étant donné un budget B et un nombre de visites restantes V, quel est le meilleur ensemble de restaurants à visiter sur un ensemble total de restaurants R?
Voir ici: https://en.wikipedia.org/wiki/Knapsack_problem#0/1_knapsack_problem
Fondamentalement, nous définissons un tableau
m
pour "max stars", oùm[i, b, v]
est le nombre maximum d'étoiles que nous pouvons obtenir lorsque nous sommes autorisés à visiter les restaurants jusqu'à (et y compris) le numéro du restauranti
, dépenser au maximumb
et visiter au maximumv
restaurants (la limite) .Maintenant, nous remplissons de bas en haut ce tableau. Par exemple,
m[0, b, v] = 0
pour toutes les valeurs deb
etv
parce que si nous ne pouvons aller dans aucun restaurant, nous ne pouvons pas obtenir d'étoiles.Aussi,
m[i, b, 0] = 0
pour toutes les valeurs dei
etb
parce que si nous avons utilisé toutes nos visites, nous ne pouvons plus obtenir d'étoiles.La ligne suivante n'est pas trop difficile non plus:
m[i, b, v] = m[i - 1, b, v] if p[i] > b
oùp[i]
est le prix d'un repas au restauranti
. Que dit cette ligne? Eh bien, si le restauranti
est plus cher que nous avons de l'argent (b
), nous ne pouvons pas y aller. Ce qui signifie que le nombre maximum d'étoiles que nous pouvons obtenir est le même, que nous incluions des restaurants jusqu'ài
ou juste jusqu'ài - 1
.La ligne suivante est un peu délicate:
m[i, b, v] = max(m[i-1, b, v]), m[i-1, b - p[i], v-1] + s[i]) if p[i] <= b
Phew.
s[i]
est le nombre d'étoiles que vous obtenez du restauranti
btw.Que dit cette ligne? C'est le cœur de l'approche de programmation dynamique. Lorsque l'on considère le nombre maximum d'étoiles que nous pouvons obtenir en regardant les restaurants jusqu'à et y compris
i
, alors dans la solution résultante, nous y allons ou non, et nous devons "juste" voir lequel de ces deux chemins mène à plus étoiles:Si nous n'allons pas au restaurant
i
, nous gardons la même somme d'argent et les visites restantes. Le nombre maximum d'étoiles que nous pouvons obtenir sur ce chemin est le même que si nous ne regardions même pas le restauranti
. C'est la première partie dumax
.Mais si nous allons au restaurant
i
, nous nous retrouvons avecp[i]
moins d'argent, une visite de moins ets[i]
plus d'étoiles. C'est la deuxième partie dumax
.Maintenant, la question est simple: lequel des deux est plus grand.
Vous pouvez créer ce tableau et le remplir avec une boucle for relativement simple (inspirez-vous du wiki). Cela vous donne juste le nombre d'étoiles, pas la liste réelle des restaurants à visiter. Pour cela, ajoutez une comptabilité supplémentaire au calcul de
w
.J'espère que les informations sont suffisantes pour vous mettre dans la bonne direction.
Alternativement, vous pouvez écrire votre problème en termes de variables binaires et d'une fonction objectif quadratique et le résoudre sur l'annelaer quantique D-Wave :-p Envoyez-moi un message si vous voulez en savoir plus à ce sujet.
la source
En utilisant la même idée que ma réponse ici :
vous pouvez construire la liste à partir des restaurants potentiels "les moins chers" .
Les étapes de l'algorithme:
Bien sûr, vous ne pouvez pas resélectionner un restaurant.
Je pense que le pire des cas, vous devrez calculer 5x5x5 ... = 5 ^ 10 + 5 ^ 9 + ... + 5 ^ 2 + 5 (= environ 12 millions) de solutions.
En javascript
la source