Algoritma / struktur data untuk menjawab "resep apa yang bisa saya buat dengan set bahan ini?"


11

Secara formal, misalkan s ( U , Q ) = { V | VU dan VQ } di mana U , Q , dan V semua mewakili set, dan U , lebih khusus lagi, mewakili satu set set. Sebagai contoh, U mungkin merupakan satu set (set) bahan yang diperlukan untuk berbagai resep dalam buku masak dengan Q mewakili set bahan yang saya miliki V yang mewakili resep yang bisa saya buat dengan bahan-bahan itu. Kueri s ( U , Q) sesuai dengan pertanyaan "Apa yang bisa saya buat dengan bahan-bahan ini?"

Apa yang saya cari adalah representasi data yang mengindeks U sedemikian rupa sehingga mendukung kueri efisien s ( U , Q ) di mana Q dan semua anggota U umumnya akan kecil dibandingkan dengan persatuan semua anggota U . Selain itu, saya ingin dapat memperbarui U secara efisien (misalnya, menambah atau menghapus resep).

Saya tidak dapat membantu tetapi berpikir bahwa masalah ini harus dipahami dengan baik, tetapi saya belum dapat menemukan nama atau referensi untuk itu. Adakah yang tahu strategi untuk menyelesaikan ini secara efisien atau tempat di mana saya bisa membaca lebih banyak tentang itu?

Sejauh pemikiran tentang solusi, satu pikir saya punya adalah untuk membangun pohon keputusan untuk set U . Di setiap simpul di pohon, pertanyaan "apakah daftar bahan Anda mengandung x ?" akan ditanya dengan x yang dipilih untuk memaksimalkan jumlah anggota U yang dihilangkan oleh jawabannya. Ketika U diperbarui, pohon keputusan ini perlu diseimbangkan kembali untuk meminimalkan jumlah pertanyaan yang diperlukan untuk menemukan hasil yang benar. Pemikiran lain adalah merepresentasikan U dengan sesuatu seperti 'octree' boolean n- dimensional (di mana n adalah jumlah bahan-bahan unik).

Saya percaya bahwa "Resep apa yang bisa dibuat dengan bahan-bahan ini?" dapat dijawab dengan mengambil produk cartesian dari (set bahan yang diperlukan untuk) resep dalam buku resep dengan set bahan-bahan yang dimiliki seseorang dan menyaring pasangan yang dipesan untuk berpasangan di mana kedua elemen sama, tetapi ini bukan solusi efisien, dan yang saya tanyakan adalah bagaimana mengoptimalkan operasi semacam ini; bagaimana seseorang menulis ini dalam SQL sehingga akan efisien dan apa yang dilakukan SQL yang membuatnya menjadi efisien?

Walaupun saya menggunakan ilustrasi buku resep masakan dan satu set bahan, saya mengantisipasi bahwa jumlah 'resep' dan jumlah 'bahan' akan sangat besar (masing-masing hingga ratusan ribu), meskipun jumlah bahan dalam resep yang diberikan dan jumlah bahan dalam set bahan tertentu akan relatif kecil (mungkin sekitar 10-50 untuk 'resep' yang khas dan sekitar 100 untuk 'set bahan' yang khas). Selain itu, operasi yang paling umum akan query s ( U , Q ), jadi harus paling optimal. Ini juga berarti bahwa algoritma brute force yang membutuhkan memeriksa setiap resep atau beroperasi pada setiap bahan akan lambat dengan sendirinya. Dengan caching yang pintar,


1
Masalah yang seharusnya mudah dipecahkan dengan database SQL.
Robert Harvey

1
Berdasarkan uraian tambahan Anda, ini terdengar seperti masalah skala Orbitz. Mesin pencari Orbitz menggunakan mesin Lisp yang menyaring sekitar satu miliar poin data untuk mendapatkan daftar penerbangan yang sesuai untuk rencana perjalanan spesifik Anda. Ini persyaratan non-fungsional adalah bahwa ia harus mengembalikan solusi dalam 10 detik atau kurang. Lihat di sini paulgraham.com/carl.html , meskipun perlu dicatat bahwa informasinya sudah cukup lama.
Robert Harvey

Pertanyaan ini cukup luas dan memiliki dua bagian: sebuah struktur data dan algoritma untuk menemukan resep yang sudah ada yang merupakan himpunan bagian dari bahan, dan bagaimana skala ini untuk data besar. Menurut saya ini harus dua pertanyaan. Anda tidak dapat benar-benar menangani bagian data besar sampai Anda mempersempit bagian algoritma. user16054 telah mendapatkan bantuan tentang bagaimana tabel join digunakan dalam representasi basis data relasional. Jika pertanyaan ini dipersempit ke bagian algoritma / datastructure, atau pertanyaan independen lain diajukan, saya mungkin dapat menawarkan saran.
berbatu

Jawaban:


4

Untuk angka yang Anda berikan, paksa saja.

Berikut adalah program JavaScript yang memaksanya untuk 10 bahan dalam DB, 10 resep dalam DB, setiap resep membutuhkan 2 bahan, dan saya memiliki 5 bahan yang tersedia:

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);

Ini berjalan dalam 0 milidetik. Saya memilih angka-angka kecil ini sehingga Anda dapat menjalankannya sendiri beberapa kali dan meyakinkan diri Anda bahwa ia melakukan apa yang Anda inginkan dan relatif bebas dari bug.

Sekarang ubahlah sehingga kita memiliki 1'000'000 bahan dalam DB, 1'000'000 resep dalam DB, 50 bahan per resep dan 100 bahan tersedia untuk saya. Yaitu nilai-nilai yang semuanya sama atau lebih besar dari use case terbesar yang Anda berikan.

Ini berjalan dalam 125 milidetik di bawah nodejs, dan ini adalah dengan implementasi paling bodoh tanpa upaya untuk mengoptimalkan.


1
Kecuali persyaratan OP berubah, tidak ada alasan untuk tidak mengambil pendekatan semacam ini. Struktur data yang pintar? Tidak. Cukup cepat? Iya. Dapat dipertahankan dan mudah dimengerti? Paling pasti.
J Trana
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.