Sized array of random unique numbers

怎甘沉沦 提交于 2019-12-12 21:44:45

问题


I was wondering what was the most concise way to get an array of a certain size, of unique random numbers.

I get random numbers like this:

times(4, () => random(30, 95));

However this is not unique. I can filter this with uniq but I need to gurantee length of 4 of array. And I want to do it the lodash way. Any ideas?


回答1:


I know this isn't "the lodash way", but it guarantees uniqueness, and allows you to use the same arguments as you were using before. It also scales better than methods that require a binary or linear search through an array, as set.has() is O(1) on average, rather than O(log(n)) or O(n).

function uniqRandom (times, ...args) {
  const set = new Set()

  while (times > 0) {
    const rand = _.random(...args)

    if (!set.has(rand)) {
      set.add(rand)
      times--
    }
  }

  return Array.from(set)
}
 
console.log(uniqRandom(4, 30, 33));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>



回答2:


Much easiear would be...

const uniqRandomNumbers  = _.sampleSize(_.range(30, 95), 4);
console.log(uniqRandomNumbers);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>



回答3:


I solved it using from a functional programming perspective. refillRandom is a recursive function that checks the number of items left to generate and calls itself again until the are the required number of items.

It also throws an Error when is imposible to generate the sequence, if the distance between min and max random number is greater than the required unique items. It's better to throw an Error than waiting forever.

const generator = (min, offset) => () =>
    Math.floor(Math.random() * offset + min);

const refillRandom = (list, min, max, times) => {
  const offset = max - min,
    num = times - list.length;
  if (times > offset) {
    throw new Error("Imposible to generate it");
  }
  
  const result = _.uniq(_.times(num, generator(min,offset)));
  if (result.length < num) {
    return result.concat(
      refillRandom(list, min, max, num - result.length)
      );
  }
  return result;
}

const r = refillRandom([], 30, 95, 4);

console.log(r);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>

EDIT: I found another solution, I mantain an ordered array of generated numbers and increment the generated number so it get mapped to a number that has not been generated yet. This way I only call random the times specified.

const randomGenerator = (min, offset, generated, times) => {
  if (!times || !offset) return generated;

  var number = Math.floor(Math.random() * offset + min);
  const len = generated.length;
  for (var i = 0; i < len; i++) {
    if (generated[i] <= number) {
      number++;

    } else {
      generated.splice(i, 0, number);
      return randomGenerator(min, offset - 1, generated, times - 1);
    }
  }
  generated[i] = number;
  return randomGenerator(min, offset - 1, generated, times - 1);

};


const r = randomGenerator(30, 95 - 30, [], 12);

console.log(r);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>


来源:https://stackoverflow.com/questions/49743588/sized-array-of-random-unique-numbers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!