Algoritmo / struttura dei dati per rispondere "quali ricette posso realizzare con questo set di ingredienti?"


11

Formalmente, s ( U , Q ) = { V | VU e VQ } dove U , Q e V rappresentano tutti gli insiemi e U , più specificamente, rappresenta un insieme di insiemi. Per fare un esempio, U potrebbe essere un insieme di (insiemi di) ingredienti richiesti per varie ricette in un libro di cucina con Q che rappresenta l'insieme di ingredienti che ho V che rappresenta una ricetta che potrei fare con quegli ingredienti. La query s ( U , Q) corrisponde alla domanda "Cosa posso fare con questi ingredienti?"

Quello che sto cercando è una rappresentazione dei dati che indicizzi U in modo tale da supportare query efficienti di s ( U , Q ) in cui Q e tutti i membri di U saranno generalmente piccoli rispetto all'unione di tutti i membri di U . Inoltre, vorrei che fosse in grado di aggiornare U in modo efficiente (ad esempio, aggiungere o rimuovere una ricetta).

Non posso fare a meno di pensare che questo problema debba essere ben compreso, ma non sono stato in grado di trovare un nome o un riferimento per esso. Qualcuno conosce una strategia per risolverlo in modo efficiente o un posto dove posso leggere di più al riguardo?

Per quanto riguarda pensare a una soluzione, un pensiero che avevo era quello di costruire un albero decisionale per il set di U . Ad ogni nodo dell'albero, la domanda "l'elenco degli ingredienti contiene x ?" verrebbe chiesto con x scelto per massimizzare il numero di membri di U che vengono eliminati dalla risposta. Man mano che U viene aggiornato, è necessario riequilibrare questo albero decisionale per ridurre al minimo il numero di domande richieste per trovare il risultato corretto. Un altro pensiero è quello di rappresentare U con qualcosa di simile a un 'ottetto' booleano n- dimensionale (dove n è il numero di ingredienti unici).

Credo che "Quali ricette possono essere fatte con questi ingredienti?" si può rispondere prendendo il prodotto cartesiano delle (serie di ingredienti richieste per) le ricette nel ricettario con il powerset degli ingredienti che si hanno e filtrando le coppie ordinate risultanti per coppie in cui entrambi gli elementi sono uguali, ma questo non è un soluzione efficiente, e quello che chiedo è come ottimizzare questo tipo di operazione; come si potrebbe comporre questo in SQL in modo tale che sia efficiente e cosa fa SQL che consente di renderlo efficiente?

Anche se utilizzo l'illustrazione di un ricettario di ricette e una serie di ingredienti, prevedo che il numero di "ricette" e il numero di "ingredienti" saranno molto grandi (fino a centinaia di migliaia ciascuno), sebbene il numero di ingredienti in una data ricetta e il numero di ingredienti in un determinato set di ingredienti sarà relativamente piccolo (probabilmente circa 10-50 per una tipica "ricetta" e circa 100 per un tipico "set di ingredienti"). Inoltre, l'operazione più comune sarà la query s ( U , Q ), quindi dovrebbe essere la più ottimale. Ciò significa anche che un algoritmo a forza bruta che richiede il controllo di ogni ricetta o il funzionamento su ogni ingrediente sarebbe indesiderabilmente lento da solo, tuttavia. Con una cache intelligente,


1
Un problema che dovrebbe essere facilmente risolvibile con un database SQL.
Robert Harvey,

1
Sulla base della tua descrizione aggiuntiva, sembra un problema su scala Orbitz. Il motore di ricerca di Orbitz utilizza un motore Lisp che passa in rassegna circa un miliardo di punti dati per ottenere un elenco di voli adatti al tuo itinerario specifico. Il requisito non funzionale è che deve restituire una soluzione in 10 secondi o meno. Vedi qui paulgraham.com/carl.html , sebbene le informazioni siano piuttosto vecchie.
Robert Harvey,

Questa domanda è piuttosto ampia e ha due parti: una struttura di dati e un algoritmo per trovare ricette esistenti che sono sottoinsiemi di ingredienti e come ridimensionarlo per dati di grandi dimensioni. La mia opinione è che questa dovrebbe essere due domande. Non puoi davvero indirizzare la parte di dati di grandi dimensioni fino a quando non restringi la parte dell'algoritmo. user16054 ha già ottenuto assistenza per l'utilizzo delle tabelle di join in una rappresentazione di database relazionale. Se questa domanda viene ristretta alla parte algoritmo / struttura dati o se viene posta un'altra domanda indipendente, potrei essere in grado di offrire suggerimenti.
roccioso

Risposte:


4

Per i numeri che hai dato, basta forzarlo.

Ecco un programma JavaScript che bruta lo impone per 10 ingredienti nel DB, 10 ricette nel DB, ogni ricetta ha bisogno di 2 ingredienti e ho 5 ingredienti disponibili:

var i, j;
var numIngredients = 10;
var numRecipes = 10;
var numIngredientsPerRecipe = 2;
var numIngredientsInQuery = 5;

function containsAll(needles, haystack){ 
  var i, len;
  for(i = 0 , len = needles.length; i < len; i++){
      if(haystack.indexOf(needles[i]) == -1) {
          return false;
      }
  }
  return true;
}

// Set up a fake DB of recipes
var ingredients = [];
for (i = 0; i < numIngredients; i++) {
    ingredients.push(i);
}
console.log('Here are the ingredients:', ingredients);

var recipes = [];
for (i = 0; i < numRecipes; i++) {
    var neededIngredients = [];
    for (j = 0; j < numIngredientsPerRecipe; j++) {
        neededIngredients.push(Math.floor(Math.random() * numRecipes));
    }
    recipes.push({ recipeId: i, needed: neededIngredients});
}
console.log('Here are the recipes:', recipes);

// Set up a fake query
var ingredientsAvailable = [];
for (i = 0; i < numIngredientsInQuery; i++) {
    ingredientsAvailable.push(Math.floor(Math.random() * numRecipes));
}

console.log("Here's a query:", ingredientsAvailable);

//Time how long brute force takes
var start = Date.now();
var result = [];
for (i = 0; i < numRecipes; i++) {
    var candidateRecipe = recipes[i];
    if (containsAll(candidateRecipe.needed, ingredientsAvailable)) {
        result.push(candidateRecipe);
    }
}
var end = Date.now();
console.log('Found ' + result.length + ' recipes in ' + (end - start) + ' milliseconds.');
console.log(result);

Funziona in 0 millisecondi. Ho scelto questi piccoli numeri in modo da poterlo eseguire da solo un paio di volte e convincerti che fa quello che vuoi ed è relativamente privo di bug.

Ora cambiatelo in modo da avere 1'000'000 ingredienti nel DB, 1'000'000 ricette nel DB, 50 ingredienti per ricetta e 100 ingredienti disponibili per me. Vale a dire valori tutti uguali o maggiori del caso d'uso più grande fornito.

Funziona in 125 millisecondi sotto nodejs, e questo è con l'implementazione più stupida senza alcuno sforzo di ottimizzazione.


1
A meno che i requisiti del PO non cambino, non c'è motivo di non adottare questo tipo di approccio. Struttura dei dati intelligente? No. Abbastanza veloce? Sì. Manutenibile e facile da capire? Sicuramente.
J Trana,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.