简体   繁体   中英

How to Influence Math.Random to Make Numbers More Likely?

I am trying to figure out how to influence Math.random() so that items where the counter of timesSinceLastSelected with a higher number have a higher chance of being selected.

For example, if "item1" has a timesSinceLastSelected of 4 and "item2" has a timesSinceLastSelected of 3, then "item1" might have a 75% to be chosen, but "item2" still has a 25% chance even if you don't know how many items are in the array.

let arr = [{ //Unknown number of items
    "name": "item1",
    "deselected": false,
    "favourite": false,
    "timesSinceLastSelected": 0
  },
  {
    "name": "item2",
    "deselected": false,
    "favourite": true,
    "timesSinceLastSelected": 0
  },
  {
    "name": "item3",
    "deselected": true,
    "favourite": false,
    "timesSinceLastSelected": 0
  },
  {
    "name": "item4",
    "deselected": true,
    "favourite": true,
    "timesSinceLastSelected": 0
  }
];

$('#button').click(function() {
  // Selecting only the items where deselected = false
  let filter_deselected = arr.filter(val => val.deselected === false);
  // Selecting only the items where favourite = true
  let filter_favourite = filter_deselected.filter(val => val.favourite === true);

  // How do I make timesSinceLastSelected with higher numbers more likely?

  if (Math.round(Math.random() * 10 > 5)) { // Selecte random item
    var selected = Math.floor(Math.random() * filter_deselected.length);
  } else { // Select random favourite item
    var selected = Math.floor(Math.random() * filter_favourite.length);
  }


  // Adding 1 to every item's counter
  arr.forEach(function(val) {
    val.timesSinceLastSelected++;
  });
  // Returning the selected item's counter back to 0
  filter_deselected[selected].timesSinceLastSelected = 0;
  // Logging the randomly selected item
  console.log(filter_deselected[selected]);
});

https://jsfiddle.net/knsfr2xh/

You could get the sum of the not selected count and get a random number up to the sum and get the item.

Then update unselected and reset the selected item.

At start give all items one for equal distribution.

 const getRandom = () => { let sum = items.reduce((s, { timesNotSelected }) => s + timesNotSelected, 0), random = Math.random() * sum, item = items.find(({ timesNotSelected }) => (random -= timesNotSelected) <= 0); items.forEach(o => o.timesNotSelected = o === item? 1: o.timesNotSelected + 1) return item; }, items = [{ id: 1, timesNotSelected: 1 }, { id: 2, timesNotSelected: 1 }, { id: 3, timesNotSelected: 1 }, { id: 4, timesNotSelected: 1 }]; console.log(getRandom().id); console.log(items); console.log(getRandom().id); console.log(items); console.log(getRandom().id); console.log(items); console.log(getRandom().id); console.log(items); console.log(getRandom().id); console.log(items); console.log(getRandom().id); console.log(items); console.log(getRandom().id); console.log(items); console.log(getRandom().id); console.log(items); console.log(getRandom().id); console.log(items); console.log(getRandom().id); console.log(items);
 .as-console-wrapper { max-height: 100%;important: top; 0; }

const getSum = (data, key) => data.reduce((sum, { [key]: n }) => sum += (n + 1), 0);

const randomThreshold = (data, key) => ~~(Math.random() * getSum(data, key));

const randomElement = (data, key, threshold = null, total = 0, i = 0) => {
  const datum = data[i];
  const newTotal = total + datum[key] + 1;
  const newThreshold = threshold || randomThreshold(data, key);
  return newTotal >= newThreshold ? datum : randomElement(data, key, newThreshold, newTotal, i + 1);
}

Use like this:

console.log(randomElement(items, 'timesSinceLastSelected').name)

 const items = [ { "name": "item1", "deselected": false, "favourite": false, "timesSinceLastSelected": 4 }, { "name": "item2", "deselected": false, "favourite": true, "timesSinceLastSelected": 3 }, { "name": "item3", "deselected": true, "favourite": false, "timesSinceLastSelected": 2 }, { "name": "item4", "deselected": true, "favourite": true, "timesSinceLastSelected": 1 } ]; const getSum = (data, key) => data.reduce((sum, { [key]: n }) => sum += (n + 1), 0); const randomThreshold = (data, key) => ~~(Math.random() * getSum(data, key)); const randomElement = (data, key, threshold = null, total = 0, i = 0) => { const datum = data[i]; const newTotal = total + datum[key] + 1; const newThreshold = threshold || randomThreshold(data, key); return newTotal >= newThreshold? datum: randomElement(data, key, newThreshold, newTotal, i + 1); } for (let i = 0; i < 10; i++) { console.log(randomElement(items, 'timesSinceLastSelected').name); }

Based on this article.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM