Je vais d'abord coller le scénario et ensuite poser ma question:
Supposons que vous ayez une liste de catégories, par exemple:
Food,Meat,Dairy,Fruit,Vegetable,Grain,Wheat,Barley
Vous avez maintenant une liste d'éléments qui rentre dans une ou plusieurs des catégories énumérées ci-dessus.
Voici un exemple de liste d'éléments: Pudding,Cheese,Milk,Chicken,Barley,Bread,Couscous,Fish,Apple,Tomato,
Banana,Grape,Lamb,Roast,Honey,Potato,Rice,Beans,Legume,Barley Soup
Lorsque vous voyez que chaque élément entre dans au moins une catégorie, il peut en contenir plus, ou peut-être que tout, sauf le minimum, correspond toujours à un.
Par exemple, Cheese
est une Food
et Dairy
.
Chaque article a deux attributs:
1) Une étiquette de prix
2) Une valeur aléatoire
Un ensemble est défini comme ayant toutes les catégories mappées sur un élément.
En d'autres termes, toutes les catégories doivent être présentes dans un ensemble.
Un ensemble des éléments ci-dessus pourrait être:
[Pudding,Lamb,Milk,Apple,Tomato,Legume,Bread,Barley Soup]
Comme vous le voyez, chaque élément est mappé sur un emplacement de catégorie:
Ma question est la suivante: quel est l'algorithme le plus efficace pour générer des ensembles dans l'ordre des catégories ci-dessus à partir d'une liste d'éléments donnés?.
Le meilleur ensemble est défini comme ayant la valeur aléatoire la plus élevée au total.
La seule contrainte est que tout ensemble généré ne puisse pas, au total, dépasser un certain montant fixe, autrement dit, tous les ensembles générés doivent se situer dans cette limite de prix.
J'espère que je suis clair, merci!
Bon, voici mon deuxième essai pour répondre à cette question.
Disons que nous avons les entrées suivantes
class Item {
public:
// using single unsigned int bitwise check sets
unsigned int category;
int name;
int value;
int price;
...
};
class ItemSet
{
public:
set<Item> items;
int sum;
};
Commencez par trier les données d'entrée en fonction de la valeur aléatoire la plus élevée, puis du prix le plus bas.
bool operator<(const Item& item1, const Item& item2) {
if (item1.value == item2.value) {
if (item1.price == item2.price) {
return item1.name < item2.name;
}
return item1.price > item2.price;
}
return item1.value < item2.value;
}
...
vector<Item> v = generateTestItem();
sort(v.begin(), v.end(), greater<Item>());
Utilisez ensuite le retour arrière pour rassembler les ensembles les plus importants jusqu'à ce que les conditions soient remplies. Le fait de trier les données d'entrée dans notre algorithme de retour en arrière garantit que les données collectées sont top basées sur la plus haute value
et la plus basse price
. Une dernière chose à noter, j’ai comparé les catégories d’items (currentCats
) à une manipulation de bits qui donne à O(1)
une complexité.
priority_queue<ItemSet> output;
void helper(vector<Item>& input, set<Item>& currentItems, unsigned int currentCats, int sum, int index)
{
if (index == input.size()) {
// if index reached end of input, exit
addOutput(currentItems);
return;
}
if (output.size() >= TOP_X) {
// if output data size reached required size, exit
return;
}
if (sum + input[index].price < PRICE_TAG) {
if ((input[index].category & currentCats) == 0) {
// this category does not exists in currentCats
currentItems.insert(input[index]);
helper(input, currentItems, currentCats | input[index].category, sum + input[index].price, index + 1);
}
} else {
addOutput(currentItems);
return;
}
if (currentItems.find(input[index]) != currentItems.end()) {
currentItems.erase(input[index]);
}
helper(input, currentItems, currentCats, sum, index + 1);
return;
}
void getTopItems(vector<Item>& items)
{
set<Item> myset;
helper(items, myset, 0, 0, 0);
}
Dans le pire des cas, ce retour en arrière aurait une complexité de O (2 ^ N), mais comme TOP_X
est une valeur limitée, il ne devrait pas durer trop longtemps en temps réel.
J'ai essayé de tester ce code en générant des valeurs aléatoires et il semble bien fonctionner. Le code complet peut être trouvé ici
Ce que vous essayez d’obtenir est une forme de correspondance maximale, et je ne sais pas s’il existe un moyen efficace de calculer des ensembles dans l’ordre, mais cette réduction pourrait néanmoins vous aider.
Définissez un graphique bipartite avec d'un côté un noeud par catégorie et de l'autre côté un noeud par élément. Ajoutez un bord entre un élément et une catégorie si ces éléments appartiennent à cette catégorie, avec un poids défini par la valeur aléatoire de l'élément.
Un "ensemble" tel que vous l'avez défini correspond à une correspondance de cardinalité maximale dans ce graphique . Ils peuvent être énumérés en un temps raisonnable, comme l'a prouvé Takeaki Uno in " Un algorithme rapide pour l'énumération de valeurs maximales non bipartites Matchings "et votre situation sera probablement encore plus rapide car votre graphique est bipartite.
Parmi ces ensembles, vous recherchez ceux qui ont un poids maximal et qui sont soumis à une contrainte de prix. Selon vos données, il suffit parfois de les énumérer toutes, de les filtrer en fonction du prix et de trier les résultats restants s’ils ne sont pas trop nombreux.
Si ce n'est pas le cas, vous pouvez trouver le meilleur ensemble en résolvant le problème d'optimisation combinatoire dont la fonction objectif est le poids total et dont les contraintes sont la limite de prix et le cardinal (appelée correspondance maximale pondérée dans la littérature). Il peut même y avoir des résolveurs déjà en ligne une fois que vous écrivez le problème dans ce formulaire. Cependant, cela ne fournira qu'un seul ensemble de ce type plutôt qu'une liste triée, mais comme ce problème est très difficile dans le cas général, c'est le mieux que vous puissiez espérer. Vous auriez besoin de davantage d'hypothèses sur vos données pour obtenir de meilleurs résultats (telles que des limites sur le nombre maximal d'ensembles, le nombre maximal d'éléments pouvant appartenir à plus de k catégories, etc.).
Je ne suis pas tout à fait sûr de ce que vous entendez par "générer des ensembles dans l'ordre".
Je pense que tout algorithme va générer des ensembles, les marquer, puis essayer de générer de meilleurs ensembles. Compte tenu de toutes les contraintes, je ne pense pas que vous puissiez générer efficacement le meilleur ensemble en un seul passage.
0-1 (problème de sac à dos } _ s’est révélé être NP-difficile , ce qui signifie qu’il n’ya pas de solution polynomiale connue (c’est-à-dire O (n ^ k)). Ce problème est identique à celui que vous auriez si, dans votre entrée, le nombre aléatoire était toujours égal au prix et qu'il n'y avait qu'une seule catégorie. En d'autres termes, votre problème est au moins aussi difficile que le problème du sac à dos, vous ne pouvez donc pas vous attendre à trouver une solution de temps polynomial garantie.
Vous pouvez générer facilement tous les ensembles valides de manière combinatoire en utilisant des boucles imbriquées: boucle par catégorie, en passant en boucle sur les éléments de cette catégorie. Au début, vous pouvez améliorer l'efficacité en sautant un élément s'il a déjà été choisi et en sautant l'ensemble lorsque vous constatez qu'il dépasse le plafonnement des prix. Mettez ces résultats dans un tas et ensuite vous pouvez les cracher dans l'ordre.
Si votre problème est que vous souhaitiez obtenir quelque chose de plus performant, cela me semble plutôt un problème de type { programmation par contraintes }, ou plus précisément un problème de type { satisfaction de contraintes . Je vous suggère de regarder les techniques utilisées pour traiter ce genre de problèmes.