شرح الانفجارات التوليفية بالآيس كريم: كيفية إضافة القليل والحصول على الكثير

دعنا نستكشف عالم التوليفات الممتع غير البديهية.

قد يكون الجمع بين القيم لتكوين مجموعات من التوليفات المميزة أمرًا صعبًا. حتى إذا تجاهلت النظام ، فإن عدد المجموعات الممكنة ينمو بشكل مثير للقلق.

لمصفوفة من قيمتين [1 ، 2] ، يمكنك إنشاء:

  • [] (مجموعة فارغة)
  • [1]
  • [2]
  • [1،2] (أو [2،1])

إذا تم السماح بالتكرار ([2 ، 2] على سبيل المثال) ، تكون الزيادة أكبر. مع زيادة عدد قيم الإدخال ، ينطلق عدد مجموعات الإخراج المقابلة عبر السقف!

دعنا نسمي عناصر قيم الإدخال وكل مجموعة من هذه القيم اختيارًا . علاوة على ذلك ، دعنا نسمح بالعناصر المتعددة ، لكل منها خيارات مميزة. مثال جيد للعمل سيكون من القائمة. سنقوم بمحاكاة قائمة Ye Olde Ice Cream Shoppe ، والتي تقدم لعملائها مزيجًا من الآيس كريم والطبقة ونكهات الشراب.

نكهات الآيس كريم هي: الشوكولاتة ، الفراولة ، الفانيليا

الإضافات: أناناس ، فراولة ، رقائق جوز الهند ، جوز البقان

العصائر: شوكولاتة ، مارشميلو ، باترسكوتش ، قيقب

هناك بعض القيود على الخيارات: يمكن للعملاء اختيار أي نوعين من الآيس كريم ، واثنين من الإضافات ، وشراب واحد . الآيس كريم وخيارات التزيين حصرية ، مما يعني أنه لا يمكنني اختيار الأناناس + الأناناس ، على سبيل المثال. قد يختار العميل عدم وجود طبقة خارجية أو شراب ، ولكن يجب عليه اختيار آيس كريم واحد على الأقل. مع هذه القيود ، يكون معدل الزيادة أسيًا ، من الترتيب 2 إلى القوة n ، وهو أقل بكثير مما لو كان الطلب مهمًا وكان مسموحًا بالتكرار.

استساغة

إن Ye Olde Ice Cream Shoppe هو في الواقع حديث تمامًا في منهجه في العمل ، ويقوم بتطوير نظام خبير ذكاء اصطناعي للحكم على مجموعات الآيس كريم والغطاء والشراب المستساغة. سيتم عرض الخوادم تحذيرًا على سجلاتهم عندما يختار العميل اختيارًا غير مستساغ. ثم يتم توجيه الخوادم للتحقق مرة أخرى مع العميل من صحة طلبهم.

الخطوة الأولى: بناء البيانات

يمكن العثور على رمز لهذه المقالة هنا. سأفترض أنك معتاد على JavaScript و Node.js. معرفة العمل بـ Lodash (أو Underscore) مفيدة. يستخدم الكود خريطة / تقليل قاعدة البيانات للتخزين.

ستكون الخطوة الأولى هي إنشاء قاعدة بيانات لجميع مجموعات الآيس كريم ، والأضافات ، والشراب. ستكون المدخلات على النحو التالي:

var menu = { iceCream: {min: 1, max: 2, values: ["CHOCOLATE", "STRAWBERRY", "VANILLA"]}, topping: {min: 0, max: 2, values: ["pineapple", "strawberry", "coconut flakes", "pecans"]}, syrup: {min:0, max: 1, values: ["chocolate", "marshmallow", "butterscotch", "maple"]} }

باستخدام هذه البيانات ، يمكنني كتابة دالة Combinator تأخذ كل عنصر قائمة وتولد جميع التركيبات الممكنة المسموح بها. يتم تخزين كل مجموعة كمصفوفة. على سبيل المثال ، قد تبدو تركيبات الآيس كريم كما يلي:

[ [ ‘CHOCOLATE’, ‘STRAWBERRY’ ], [ ‘CHOCOLATE’, ‘VANILLA’ ], [ ‘CHOCOLATE’ ], [ ‘STRAWBERRY’, ‘VANILLA’ ], [ ‘STRAWBERRY’ ], [ ‘VANILLA’ ] ]

بمجرد تحديد تركيبات الآيس كريم ، والطبقة ، والعصائر ، كل ما تبقى هو تكرار كل مجموعة مع العناصر الأخرى:

var allChoices = []; _.each(iceCreamChoices, function(ic) { _.each(toppingChoices, function(tp) { _.each(syrupChoices, function(sy) { allChoices.push([ic,tp,sy]); }) }) })

ينتج عن ذلك مزيج من الآيس كريم (الآيس كريم) ، والأكواب (الأغطية) ، والشراب ، مثل:

[ [ 'VANILLA' ], [ 'coconut flakes', 'pecans' ], [] ], [ [ 'VANILLA' ], [ 'coconut flakes' ], [ 'chocolate' ] ], [ [ 'VANILLA' ], [ 'coconut flakes' ], [ 'marshmallow' ] ],...

الخيارات المعروضة تترجم على النحو التالي:

  • آيس كريم فانيليا مع رقائق جوز الهند وجوز البقان ، بدون شراب
  • آيس كريم فانيليا مع رقائق جوز الهند وشراب الشوكولاتة
  • آيس كريم فانيليا مع رقائق جوز الهند وشراب المارشميلو

حتى مع وجود عدد قليل من عناصر القائمة المقيدة ، فإن عدد الخيارات المسموح بها هو 330!

الخطوة الثانية: تخزين البيانات

مع تحديد كل مجموعة من العناصر القابلة للطلب الآن ، يمكن القيام بمزيد من العمل. تبين أن نظام الذكاء الاصطناعي لتحديد مجموعات الاختيار المستساغة معقد ولن يتم تضمينه في نظام تشغيل السجلات. بدلاً من ذلك ، سيتم تقديم طلب AJAX إلى خادم يحتوي على برنامج AI. ستكون المدخلات هي اختيارات قائمة العميل ، وستقوم المخرجات بتقييم مدى استساغة هذه الاختيارات كأحد: [ugh، meh، tasty، sublime]. يؤدي تصنيف استساغة لاف إلى التحذير المذكور أعلاه.

نحتاج إلى استجابة سريعة للطلب ، لذلك سيتم تخزين تصنيفات الاستساغة مؤقتًا في قاعدة بيانات. نظرًا لطبيعة الزيادة الأسية ، يمكن أن يتطور هذا ليصبح مشكلة بيانات كبيرة إذا تمت إضافة المزيد من خيارات العناصر إلى القائمة في المستقبل.

لنفترض أنه تقرر تخزين مجموعات الاختيار والتصنيفات في قاعدة بيانات NoSQL. باستخدام PouchDB ، يتم تخزين كل اختيار وقيمة استساغة كمستندات JSON. A المؤشر الثانوي (الملقب عرض و) مع كل خيار كمفتاح يسمح لنا أن ننظر بسرعة في تصنيف استساغة. بدلاً من دفع البيانات إلى مصفوفة allChoices كما هو موضح أعلاه في buildChoices.js ، يمكنني دفع مستندات JSON إلى قاعدة البيانات للتخزين.

بالمتابعة بسذاجة ، يمكنني إجراء بعض التغييرات في Step1.js للوصول إلى Step2.js: أولاً وقبل كل شيء ، أحتاج إلى تثبيت PouchDB عبر npm ، ثم أطلبه. بعد ذلك ، أقوم بإنشاء قاعدة بيانات NoSQL تسمى الاختيارات .

var PouchDB = require('pouchdb'); var db = new PouchDB('choices');

الآن ، يتم نشر كل اختيار في قاعدة بيانات الاختيارات:

var count = 0; _.each(iceCreamChoices, function(ic) { _.each(toppingChoices, function(tp) { _.each(syrupChoices, function(sy) { //allChoices.push([ic,tp,sy]); db.post({choice: [ic,tp,sy]}, function(err, doc){ if (err) console.error(err); else console.log(`stored ${++count}`); }); }) }) }); console.log('done??');

هذا يعمل! نوعا ما. كما يمكن الاستدلال عليه بواسطة معلمة رد الاتصال لـ db.post ، فإن هذه العملية غير متزامنة. ما نراه في السجل هو:

>node Step2.js done?? stored 1 stored 2 stored 3 ...

لذا يقول الكود أنه تم القيام به حتى قبل تخزين السجل 1. ستكون هذه مشكلة إذا كان لدي المزيد من المعالجة لقاعدة البيانات ولم تكن جميع السجلات موجودة بعد.

الخطوة الثالثة: التثبيت والتكرير

هناك أيضًا مشكلة أكثر دقة: استنفاد الموارد المحتمل. إذا كانت قاعدة البيانات تحد من عدد الاتصالات المتزامنة ، فقد يؤدي عدد كبير من طلبات النشر المتزامنة إلى انقضاء مهلات الاتصال.

بالنسبة لـ Step3.js ، قمت ببعض إصلاح الأخطاء وإعادة التهيئة وإعادة هيكلة ما تم كتابته في Step2.js. كان أحد الأخطاء هو أن كل تشغيل أضاف المزيد والمزيد من السجلات إلى قاعدة البيانات ، مما يؤدي إلى تكرار ما كان موجودًا من قبل. كان الحل هو تدمير قاعدة البيانات الموجودة وإعادة إنشائها ثم تشغيل البرنامج الرئيسي:

// remove old db.destroy(null, function () { db = new PouchDB('choices'); run(); });

كان التالي هو إضافة عدد جاري من المستندات المخزنة ونشر الطلبات قيد المعالجة بحيث يكون البرنامج: 1) يعرف متى يتم تخزين آخر مستند ؛ 2) يسمح فقط لخمس مشاركات بالمتابعة في وقت واحد. تبدو طريقة run () بهذا الشكل الآن (مع بعض الإغفالات):

function run() { var menu = { //... } var iceCreamChoices = new Combinator({ //... }); var toppingChoices = new Combinator({ //... }); var syrupChoices = new Combinator({ //... }); var count = 0; var total = iceCreamChoices.length * toppingChoices.length * syrupChoices.length; var postCount = 0; var postCountMax = 5; _.each(iceCreamChoices, function (ic) { _.each(toppingChoices, function (tp) { _.each(syrupChoices, function (sy) { var si = setInterval(() => { if (postCount < postCountMax) { clearInterval(si); postChoice(ic, tp, sy); } }, 10); }) }) }); function postChoice(ic, tp, sy) { ++postCount; db.post({ choice: [ic, tp, sy] }, function (err, doc) { --postCount; done(err); }); } function done(err) { if (err) { console.error(err); process.exit(1); } console.log(`stored ${++count}`); if (count === total) { console.log('done'); } } }

التغييرات الرئيسية التي يجب ملاحظتها هي:

  1. A postCount يقيس كم من المشاركات المعلقة
  2. يتحقق مؤقت الفاصل الزمني من postCount وسيقوم بالنشر والخروج عند توفر خانات النشر
  3. ل ذلك () يسمى معالج عندما يتم تخزين كافة الخيارات

الخطوة 4: إضافة الاستساغة

مع وجود جميع خيارات القائمة الممكنة ، يمكننا الآن جعل الذكاء الاصطناعي يحدد مدى استساغة كل منها. الذكاء الاصطناعي هو مجرد محاكاة في الوقت الحالي ، حيث يقوم بتعيين قيم عشوائية لكل سجل مستند في PouchDB. سيتم تخزين هذه القيم في قاعدة البيانات عن طريق تحديث كل وثيقة مع تصنيف الذوق.

var _ = require('lodash'); var PouchDB = require('pouchdb'); var db = new PouchDB('choices'); db.allDocs({ include_docs: true }) .then(docs => { _.each(docs.rows, r => { r.doc.taste = palatability(); db.put(r.doc); }); }); function palatability() { var scale = Math.round(Math.random() * 10); var taste; switch (true) { // this switch is a horrible hack; don't ever do this ;-P case (scale < 2): taste = "ugh"; break; case (scale < 5): taste = "meh"; break; case (scale < 8): taste = "tasty"; break; default: taste = "sublime"; break; } return taste; }

فقط للتحقق من أننا قمنا بتخزين الأشياء بشكل صحيح ، يمكننا تفريغ المستندات الموجودة في قاعدة البيانات إلى وحدة التحكم:

db.allDocs({ include_docs: true }) .then(docs => { _.each(docs.rows, r => { console.log(r.doc.choice, r.doc.taste) }); }); //output looks like: /* [ [ 'STRAWBERRY' ], [ 'coconut flakes' ], [ 'maple' ] ] 'sublime' [ [ 'CHOCOLATE' ], [ 'pecans' ], [ 'chocolate' ] ] 'tasty' [ [ 'CHOCOLATE', 'STRAWBERRY' ], [], [ 'chocolate' ] ] 'sublime' [ [ 'VANILLA' ], [], [ 'marshmallow' ] ] 'meh' [ [ 'CHOCOLATE', 'STRAWBERRY' ], [ 'pineapple' ], [ 'marshmallow' ] ] 'meh' */

الخطوة الخامسة: البحث عن الاستساغة

المستندات موجودة في قاعدة البيانات ، ولكن الآن يجب أن تكون هناك طريقة لتحديد مدى استساغة اختيارات العميل. يتم ذلك عن طريق تحديد طريقة عرض ، وهي دالة تقوم بإرجاع مفتاح لكل مستند ، إلى جانب قيمة. ماذا يجب أن يكون المفتاح؟

I could use r.doc.choice as the key, but arrays have an order and that order might change if the menu items defined in Step 1 were later rearranged. The key is just an identifier of the choice selection and doesn’t carry an semantic meaning of its own. What should work is to:

  • flatten each r.doc.choice array,
  • order the elements alphabetically, then
  • concatenate them together
  • result is a key

If more choices are added in the future, though, the key length might be over the limit allowed by the database. Instead of using the key as constructed, a hash the key could be used as the real key. A SHA256 hash in hex is 64 characters long, and the likelihood of a hash collision, even for a quadrillion choices, is essentially zero. Writing the hash function for choices is easy, using the Node.js crypto module and a Lodash chain:

const crypto = require('crypto'); const _ = require('lodash') function hash(choice) ') .value(); return crypto.createHmac('sha256', 'old ice cream') .update(str) .digest('hex');  module.exports = hash;

Adding the hash to our existing documents is a simple matter of iterating through each database document, computing its hash, and updating the document with a key value:

const _ = require('lodash'); const hash = require('./hash'); const PouchDB = require('pouchdb'); const db = new PouchDB('choices'); db.allDocs({ include_docs: true }) .then(docs => { _.each(docs.rows, r => { r.doc.key = hash(r.doc.choice); db.put(r.doc); }); }) .catch(e => { console.error(e) });

Next, a database view is built using the document key field as an index; I’ll call it choice.

const PouchDB = require('pouchdb'); const db = new PouchDB('choices'); // doc that defines the view var ddoc = { _id: '_design/choice', views: { by_key: { map: function (doc) { emit(doc.key, doc.taste); }.toString() } } }; // remove any existing view, then add new one: db.get(ddoc._id) .then(doc => { return db.remove(doc); }) .then(() => { db.put(ddoc) .catch(function (err) { console.error(err); }); });

For any document key (hash of choice array), I can find its taste via the view choice. Now everything is in place to determine whether a customer’s choice is ugh, meh, tasty, or sublime. To test this, we make some random choices and see if we can find the taste:

 const choices = [ [['VANILLA'], ['coconut flakes', 'pecans'], ['marshmallow']], [['CHOCOLATE'], ['pecans'], ['chocolate']], [['STRAWBERRY', 'VANILLA'], ['pineapple', 'coconut flakes'], ['marshmallow']], [['STRAWBERRY'], ['pecans'], ['maple']], [['VANILLA'], ['coconut flakes', 'pineapple'], ['chocolate']], [['CHOCOLATE, STRAWBERRY'], ['pineapple', 'pecans'], ['butterscotch']], ]; const keys = _.map(choices, c => { return hash(c); }); db.query('choice/by_key', { keys: keys, include_docs: false, }, function (err, result) { if (err) { return console.error(err); } _.each(result.rows, (r, i) => { console.log(`${choices[i]} tastes ${r.value}`); }) });

The results are:

=> node test VANILLA,coconut flakes,pecans,marshmallow tastes ugh CHOCOLATE,pecans,chocolate tastes sublime STRAWBERRY,VANILLA,pineapple,coconut flakes,marshmallow tastes tasty STRAWBERRY,pecans,maple tastes meh VANILLA,coconut flakes,pineapple,chocolate tastes sublime

هذا هو! كل ما تبقى هو كتابة برنامج العميل الذي يقدم اختيارات عبر AJAX ويستعيد قيمة طعم (استساغة). إذا كان الأمر كذلك ، فسيظهر تحذير في السجل.

في منشور لاحق ، قمت بتنقيح الخوارزمية المستخدمة أعلاه. تحقق من ذلك!

المراجع

النمو الأسي ليس رائعًا. الانفجار الاندماجي.

الكثير من صناعة التكنولوجيا مهووسة بالنمو الهائل. أي شيء خطي يموت ، أو مات منذ سنوات ...

www.torbair.com

التوليفات والتباديل حاسبة

اكتشف عدد الطرق المختلفة التي يمكنك من خلالها اختيار العناصر. للحصول على شرح مفصل للصيغ ، يرجى زيارة ...

www.mathsisfun.com