【问题标题】:How to randomize a generator output in JavaScript?如何在 JavaScript 中随机化生成器输出?
【发布时间】:2019-05-27 16:51:00
【问题描述】:

假设我有一台发电机,它最终会停止

function* letters () {
  let offset = 0
  while (offset < 26)
    yield String.fromCharCode(65 + offset++)
}

console.log(Array.from(letters()))
// => [ "A", "B", "C", ..., "X", "Y", "Z" ]

使用该生​​成器作为输入,我想创建另一个生成器,但对于这个,我希望值以随机顺序出现

function* randomizeGen (gen) {
  // ...
}

Array.from(randomizeGen(letters()))
// => [ "X", "T", "L", "P", "A", ..., "G", "S", "B" ] (all 26 letters)

新的生成器应该是懒惰的,就像第一个一样,但我不知道如何以聪明的方式编写它

function shuffleArray (arr) {
  // return shuffled array
}

function* randomizeGen (gen) {
   const all = Array.from(gen) // help!
   for (const one of shuffleArray(all))
     yield one
}

这会起作用,但它是通过首先完全耗尽gen 来实现的。实际上,我的初始生成器输出 数百万个值,所以我认为首先将它们全部收集到一个数组中并不是一个好主意。使用生成器的全部意义在于我可以一次处理一个值

老实说,我不知道如何随机化生成器的输出,但同时保持它的惰性。有人可以帮忙吗?

【问题讨论】:

  • 你可以在将数组传递给第二个生成器之前对其进行洗牌吗? shuffle(Array.from(letters()));
  • 似乎你最好在这里使用一个新的生成器,而不是试图从另一个输出中导出它。如果不先汇集一堆值,就没有办法做到这一点。

标签: javascript generator


【解决方案1】:

根据定义,你不能。生成器每次调用时都会以特定顺序生成一个值。因此,如果您想要其他值而不是下一个值,则必须从生成器中消耗多个值,然后选择一个返回。这是急切的评估,而不是懒惰。


但是,您可以像这样模拟预期的行为。它会在大约 50% 的时间内保持懒惰。所以它有点懒惰。

请注意,这远非真正的随机顺序。生成器中的前半部分元素每个都有 50% 的机会按正确的顺序放置(并且有 50% 的机会放入缓存中)。虽然元素的后半部分将有2/n 的机会以正确的顺序排列(其中n 是缓存中的当前元素数量,也就是n 将从N/2 变为0(其中N 是生成器中的元素总数))。

TL:DR “随机”结果的前半部分是有序的,但会在这里和那里缺少一些元素(它们出现在另一半)。

Math.random.between = (min, max) => Math.floor(Math.random()*(max-min+1)+min);

function* letters () {
  let offset = 0
  while (offset < 26)
    yield String.fromCharCode(65 + offset++)
}

function* randomizeGen (gen) {
   let cache = [];
   let current = gen.next();
   
   while (!current.done) {
    if (Math.random.between(0, 1) > 0.5) {
      yield current.value;
    } else {
      cache.push(current);
    }
    current = gen.next();
   }
   
   while (cache.length > 0) {
    const index = Math.random.between(0, cache.length-1);
    const v = cache[index];
    cache = cache.filter((_,i) => i !== index);
    yield v.value;
   }
}

const randomOrder = Array.from(randomizeGen(letters()));
console.log(randomOrder);

【讨论】:

    【解决方案2】:

    我不确定这是否是最好的方法。但是您可以使用下面的代码以随机顺序生成字母

    function* letters() {
        let arr = [];
        let char = "";
        while (arr.length < 26) {
            let flag = true;
            while (flag) {
                char = String.fromCharCode(65 + Math.floor(Math.random() * 27));
                if (!arr.find(function(element){return element === char;})){
                    arr.push(char);
                    flag = false;
                }
            }
        yield char;
        }
    }
    
    console.log(Array.from(letters()))

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多