簡體   English   中英

創建用於分配唯一項目的算法

[英]Creating an algorithm for distributing unique items

我不確定如何在代碼中針對特定問題創建算法,因此我創建了這個難題

  • 有3個Cookie怪物。
  • 每個怪物都有一個袋子,里面裝有從0到有限數量(比如100)的隨機餅干。
  • Cookie的類型很多,但我們不知道有多少種(生成Cookie后可以查明)。
  • 每個怪物都會從自己的袋子里吃1個餅干
    • 其他所有怪物都無法獲得相同類型的Cookie。
    • 如果兩個或多個怪物的包中沒有任何獨特的味道,則索引較低的怪物應該選擇cookie,而其他人則沒有cookie。
  • 我們要確保每個怪物都盡可能獲得一個cookie。

// A cookie maker for good measure:
function cookieMaker(quant) {
  // Fill in your own flavours...
  const flavours = [...]
  const cookies = []
  for(let i = 0; i < quant; i++) {
    cookies.push(flavours[Math.floor(Math.random() * flavours.length)])
  }
  return cookies
}

// This is our function
function pickCookies(monsters) {
  ...
}

pickCookies([
  {
    name: 'Fluffy',
    cookies: ['choco', 'vanilla', 'blueberry']
  },
  {
    name: 'Pillowpants',
    cookies: ['choco', 'vanilla']
  },
  {
    name: 'Pinky',
    cookies: ['choco']
  }
])

// Should return:
[
  {
    name: 'Fluffy',
    eat: 'blueberry'
  },
  {
    name: 'Pillowpants',
    eat: 'vanilla'
  },
  {
    name: 'Pinky',
    eat: 'choco'
  }
]

// Note, that it should also work if you shuffle the list.

我以json格式為您列出了一種口味

您將如何解決這個難題?

修訂

我似乎至少遺漏了一個細節,因此,如果您已經開始研究它,我將在此處添加任何更改,以免突然更改規則而不會混淆任何人:

  • #1如果可能的話,怪物應該嘗試從袋子的頂部獲取餅干(較低的數組索引)。

我發現您的問題很有趣,因此我通過創建此算法試了一下:

用文字

  1. 為每個怪物創建一個數組,在其包中包含所有cookie類型(刪除重復項)。
  2. 對於每個怪物,計算他們的選擇(可用口味)。
  3. 選擇數量最少的怪物。
  4. 對於每個選項,請檢查還有多少其他怪物具有這種味道。
  5. 以最小數量的擁有者的口味,讓怪物吃掉它。
  6. 從剩下的怪物袋中去除那種味道。
  7. 從第2步開始重復,直到[沒有其他飢餓的怪物了]或[剩下的每個怪物都沒有其他可用的口味]。

在代碼中

第一槍

 var monsters = [{name:'Fluffy'}, {name:'Pillowpants'}, {name:'Pinky'}], flavors = ['choco', 'vanilla', 'blueberry', 'peanut butter'], maxNumberOfCookies = 6; $('#generateBtn').click(generateExample); generateExample(); function generateExample() { // Fill each monster's bag for(var i=0; i<monsters.length; i++) monsters[i].cookies = cookieMaker(); // 3, 2, 1, bon appetit! var res = pickCookies(monsters); displayResults(monsters, res); } function cookieMaker() { var cookies = [], // The quantity is random for each monster quant = Math.floor(Math.random() * maxNumberOfCookies); for(var i=0; i<quant; i++) { cookies.push(flavors[Math.floor(Math.random() * flavors.length)]) } return cookies; } function pickCookies(monsters) { var res = []; // List flavors available for each monster for(var i=0; i<monsters.length; i++) { var m = monsters[i], flavorsInBag = []; for(var j=0; j<m.cookies.length; j++) { if(flavorsInBag.indexOf(m.cookies[j]) < 0) { flavorsInBag.push(m.cookies[j]); } } res.push({name: m.name, flavors: flavorsInBag, eat: false}); } while(!allMonstersAte(res) && !noMoreFlavors(res)) { // Take the monster with the smallest number of options var monsterWithLeastFlavors = false; for(var i=0; i<res.length; i++) { if(!res[i].flavors.length) continue; if(!monsterWithLeastFlavors || res[i].flavors.length < monsterWithLeastFlavors.flavors.length) { monsterWithLeastFlavors = res[i]; } } // Select the flavor owned by the fewest monsters var flavorWithLeastOwners = monsterWithLeastFlavors.flavors[0], fewestNbOfOwners = res.length; for(var i=0; i<monsterWithLeastFlavors.flavors.length; i++) { var nbOfOwners = getNbOfOwners(monsterWithLeastFlavors.flavors[i], res); if(nbOfOwners < fewestNbOfOwners) { flavorWithLeastOwners = monsterWithLeastFlavors.flavors[i]; fewestNbOfOwners = nbOfOwners; } } makeMonsterEat(monsterWithLeastFlavors, flavorWithLeastOwners, res); } return res; } // Returns true if all monsters have a property "eat" != false function allMonstersAte(res) { return !res.some(function(monster){ return !monster.eat; }); } // Returns true if all monsters have no flavor left to choose from function noMoreFlavors(res) { return !res.some(function(monster){ return monster.flavors.length; }); } // Returns the number of monsters who have that flavor function getNbOfOwners(flavor, monsters) { return monsters.filter(function(monster){ monster.flavors.indexOf(flavor)>-1; }).length; } function makeMonsterEat(monster, flavor, res) { monster.flavors = []; monster.eat = flavor; for(var i=0; i<res.length; i++) { res[i].flavors = res[i].flavors.filter(function(fl){ return fl != flavor; }); } } function displayResults(monsters, res) { var initial = ""; for(var i=0; i<monsters.length; i++) { initial += '<b>' + monsters[i].name + '\\'s bag contains:</b> ' + (monsters[i].cookies.length ? monsters[i].cookies.join(', ') : '<span style="color:red">NOTHING</span>') + '<br>'; } var result = ""; for(var i=0; i<res.length; i++) { result += '<b>' + res[i].name + ' ate:</b> ' + (res[i].eat ? res[i].eat : '<span style="color:red">NOTHING</span>') + '<br>'; } $('#result').html('<h2>Initial state</h2>' + initial + '<h2>Result</h2>' + result ); } 
 *{margin: 0; padding: 0} body{font-family: Arial, Helvetica, sans-serif; padding: 1em; font-size: 14px} h2{font-size: 18px; margin: .3em 0} 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <button id="generateBtn">Generate new example</button> <div id="result"></div> 

更實用的方法

我不知道那不是你想要的,但是我嘗試了...

 var monsters = [{name:'Fluffy'}, {name:'Pillowpants'}, {name:'Pinky'}], flavors = ['choco', 'vanilla', 'blueberry', 'peanut butter'], maxNumberOfCookies = 6; $.getJSON('https://cdn.rawgit.com/demux/ad32c612b303aa12d1cdf043225fa1d2/raw/3a037fb7ad30d8a383526ebc62b3671a1656c06d/flavours.json') .done(init); function init(data) { flavors = data; $('body').html('<button id="generateBtn">Generate new example</button><div id="result"></div>'); $('#generateBtn').click(generateExample); generateExample(); } function generateExample() { // Fill each monster's bag for (var i = 0; i < monsters.length; i++) monsters[i].cookies = cookieMaker(Math.floor(Math.random() * maxNumberOfCookies)); // 3, 2, 1, bon appetit! var res = pickCookies(monsters); displayResults(monsters, res); } function cookieMaker(quant) { var cookies = []; for (var i = 0; i < quant; i++) { cookies.push(flavors[Math.floor(Math.random() * flavors.length)]) } return cookies; } /* * Returns a new Array of monsters who ate unique cookies */ function pickCookies(monsters) { var res = getMonstersWithFlavors(monsters); while (!allMonstersAte(res) && !noMoreFlavors(res)) { var monsterIndex = indexOfMonsterWithLeastFlavors(res), flavor = flavorWithLeastOwners(res[monsterIndex].flavors, res); for (var i = 0; i < res.length; i++) { if (i == monsterIndex) res[i] = makeMonsterEat(res[i], flavor); else res[i] = removeFlavor(res[i], flavor); } } return res; } /* * Returns a new Array of monsters with their flavors */ function getMonstersWithFlavors(monsters) { return monsters.map(function(monster) { return { name: monster.name, flavors: removeDuplicates(monster.cookies), eat: false }; }); } /* * Returns a new Array without duplicates */ function removeDuplicates(arr) { return Array.from(new Set(arr)); } /* * Returns the index of the monster with least flavors */ function indexOfMonsterWithLeastFlavors(monsters) { var tmp = { monsterIndex: -1, count: false }; for (var i = 0; i < monsters.length; i++) { if (!monsters[i].flavors.length) continue; if (!tmp.count || monsters[i].flavors.length < tmp.count) { tmp = { monsterIndex: i, count: monsters[i].flavors.length }; } } return tmp.monsterIndex; } /* * Returns the flavor owned by least monsters */ function flavorWithLeastOwners(flavors, monsters) { var tmp = { flavor: '', count: false }; for (var i = 0; i < flavors.length; i++) { var nbOfOwners = getNbOfOwners(flavors[i], monsters); if (!tmp.count || nbOfOwners < tmp.count) { tmp = { flavor: flavors[i], count: nbOfOwners }; } } return tmp.flavor; } /* * Checks if all monsters have a property "eat" != false */ function allMonstersAte(res) { return !res.some(function(monster) { return !monster.eat; }); } /* * Checks if all monsters have no flavor left to choose from */ function noMoreFlavors(res) { return !res.some(function(monster) { return monster.flavors.length; }); } /* * Returns the number of monsters who have that flavor */ function getNbOfOwners(flavor, monsters) { return monsters.filter(function(monster) { monster.flavors.indexOf(flavor) > -1; }).length; } /* * Returns a new monster Object with the cookie they ate */ function makeMonsterEat(monster, flavor) { return { name: monster.name, flavors: [], eat: flavor }; } /* * Returns a new monster Object without the cookie flavor */ function removeFlavor(monster, flavor) { return { name: monster.name, flavors: monster.flavors.filter(function(fl) { return fl != flavor }), eat: monster.eat }; } function displayResults(monsters, res) { var initial = ""; for (var i = 0; i < monsters.length; i++) { initial += '<b>' + monsters[i].name + '\\'s bag contains:</b> ' + (monsters[i].cookies.length ? monsters[i].cookies.join(', ') : '<span style="color:red">NOTHING</span>') + '<br>'; } var result = ""; for (var i = 0; i < res.length; i++) { result += '<b>' + res[i].name + ' ate:</b> ' + (res[i].eat ? res[i].eat : '<span style="color:red">NOTHING</span>') + '<br>'; } $('#result').html('<h2>Initial state</h2>' + initial + '<h2>Result</h2>' + result ); } 
 *{margin: 0; padding: 0} body{font-family: Arial, Helvetica, sans-serif; padding: 1em; font-size: 14px} h2{font-size: 18px; margin: .3em 0} 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <h2>Loading...</h2> 

這個問題稱為“不同代表系統”,關於它們有一些定理,並且至少有一些公開的算法可以解決該問題。 您可能會發現霍爾的婚姻定理很有趣。 在上下文中進行總結:

當且僅當對於每個怪物子集,在那些怪物的袋子中存在更多不同類型的餅干,然后在子集中存在怪物時,每個怪物才有可能吃餅干。

@blex編寫了一個算法,它是一個很好的貪婪近似算法。 當可能的Cookie類型數量遠大於怪獸數量時,它應該總是可以工作,但是當這些數量彼此非常接近並且也非常大時,通常會失敗。

以下是一個人為設計的示例,其中@blex的算法失敗,出現了8個怪物和8個不同的Cookie類型。 馬上,Blubite做出了錯誤的選擇,他必須選擇香草,但他選擇了choco,而且這種選擇沒有可能的解決方法,因為Fluffy,Fred,Jub和Pillowpants是四個怪物的子集,它們之間只有三個選擇(燕麥片,藍莓和花生醬)。

初始狀態

Blubite的手提袋包含:巧克力,香草
蓬松的袋子包含:巧克力,藍莓,花生醬
弗雷德(Fred)的書包包含:燕麥片,藍莓,花生醬
奶壺的袋子包含:燕麥片,藍莓,花生醬
枕頭袋包含:燕麥粥,藍莓,花生醬
小指袋包含:香草,燕麥片,糖,朗姆酒,綠色
Scuzzy的袋子包含:香草,燕麥片,糖,朗姆酒,綠色
Ziffu的手提袋內含:香草,燕麥片,糖,朗姆酒,綠色

@blex算法

Blubite吃:巧克力
蓬松吃:藍莓
弗雷德(Fred)吃:燕麥片
吃過的食物:花生醬
Pillowpants吃:NOTHING
吃了小指:香草
吃不清:糖
Ziffu ate:朗姆酒

真正的解決方案

吃了Blubite:香草
蓬松吃:巧克力
弗雷德(Fred)吃:燕麥片
吃過的食物:藍莓
吃了枕頭:花生醬
吃了小指:糖
吃不清:朗姆酒
Ziffu吃:綠色

這里有一本書,其中包含兩種可以在一般情況下正確解決問題的算法 ,這是一篇 1956年的學術論文 ,上面對這兩種算法之一進行了原始描述。 我不打算在JavaScript中實現該算法,但是如果需要休息,可以稍后再訪問。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM