【问题标题】:Fill an array with random non-consecutive numbers用不连续的随机数填充数组
【发布时间】:2020-05-28 09:33:18
【问题描述】:

我想创建一个数组 (indexes),该数组应填充从 0length - 1 的随机数。

所以如果length = 3; 那么indexes 应该从0 延伸到2,例如它可能类似于:[0, 2, 1]

我们应该遵循一个条件:(上帝,我怎么能用英语来描述呢:)

条件:我们不接受连续的数字。

所以我们不应该返回:

[0, 1, 2]       => Because 0 and 1 and 2 are consecutive.

[2, 0, 1]       => Because 0 and 1 are consecutive.

[2, 3, 1, 0]    => Because 2 and 3 are consecutive.

好的,我知道可能有一个例外,最后一个元素可能是连续的,因为那时我们别无选择!

我写了一段代码,但不幸的是,由于 CPU 使用率高,它导致浏览器崩溃!

请帮忙,我和我的笔记本电脑都很困惑!

// generate number from 0 to max (max could include in the result)
function getRandom(max) {
    return Math.floor(Math.random() * (max + 1));
};

// our array to be filled with unordered numbers
let indexes = [];

// we will fill the above array with 0, 1 ,2 ,3 I mean 0 to (length - 1) 
let length = 4;

// loop through indexes so that it is filled with 4 elements
while( indexes.length <= length){

      // get a number randomally from 0 to 3
      let result = getRandom(length - 1);
       // we don't want any repeated number so..
       if(!indexes.includes(result)){     
          // finally here is our condition, we should   
          if( result !== indexes[indexes.length - 1] + 1 ){
              indexes.push(result);
          } else if(indexes.length == length){
            // push the last element to the array despite the condition
            indexes.push(result);
          }
       }

};

console.log(indexes);

【问题讨论】:

  • 你的标题是错误的......“用随机连续数字填充数组”与你想要的相反。
  • 对不起.. 感谢 VLAZ 的编辑...
  • 只是对数组进行排序然后反转它?那么所有数字都将不连续?
  • [3, 2] 有效吗?
  • 旁注,因为您现在有很多答案,但是您的原始代码正在崩溃,因为您的 while 循环永远不会结束。将&lt;= 更改为&lt;

标签: javascript arrays random numbers


【解决方案1】:

您可以通过以下方式调整modern Fisher-Yates 算法:

如果交换索引 ij 处的值会在索引 i 处带来一个值,而该值是不允许的,因为已放置在索引 i+1 处,则不要进行该交换,而是在这三个相关索引之间进行三重轮换。

这是实现,以及 400 次运行的测试:

function shuffled(n) {
    let a = [...Array(n).keys()];
    for (let i = n - 1; i > 0; i--) {
        let j = Math.floor(Math.random() * (i + 1));
        if (a[j]+1 === a[i+1]) { // a swap is going to violate the rule at i/i+1
            // Triple rotation at j, i, i+1 (or, when j == i, swap at i, i+1)
            a[j] = a[i];
            a[i] = a[i+1]--;
        } else { // standard swap
            let temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
    }
    // Finally check first two values:
    if (a[0]+1 === a[1]) {
        let temp = a[0];
        a[0] = a[1];
        a[1] = temp;
    }
    return a;
}

// Test this solution:
function verify(a) {
    let set = new Set(a);
    return !a.some((v, i) => v === a[i-1]+1 || !set.has(i));
}

for (let size = 2; size < 6; size++) {
    for (let i = 0; i < 100; i++) {
        let a = shuffled(size);
        if (!verify(a)) throw "not correct";
    }
}
console.log("tests successful");

这具有可预测的线性时间复杂度。没有多次尝试失败的风险。

【讨论】:

  • 感谢您的回答...加一个...但是 Nina Scholz 的回答更快...
  • 短如 4 到 5 个元素
  • 我制作了一个包含 5 个数组的测试套件,从两个解决方案中取出初始数组生成(因为这确实是相同的逻辑):jsbench.me/4kk6jtbabn/1。告诉我你得到了什么。
  • 我收到了我接受的答案,请查看此代码笔,比较您的算法与 Nina 的算法之间的结果(结果分布)...codepen.io/pixy-dixy/pen/BaNjNpY?editors=0010
  • 嗯,是的,如果您的要求是 同质 分布,那么请坚持这个答案。我没有在你的问题中看到这个要求。但在我看来,你上面的第一条评论(关于速度)是不正确的。我现在将尝试考虑一种算法,它也将保持均匀分布,并且仍然不需要重试......
【解决方案2】:

更新:

我在比较错误的数字来检查不连续的规则

您可以创建一个索引列表,随机取第一项。如果满足条件或者它是最后一个元素,则一次从索引中删除一个项目

const range = (n) => {
    const array = [];
    for (let  i = 0; i < n; i++) array.push(i);
    return array;
};

const randomIndex = (array) => (
    parseInt(array.length * Math.random())
);

const randomNonConsecutive = (n) => {
    const array = range(n);
    const result = array.splice(randomIndex(array), 1);

    while (array.length) {
        const last = result[result.length - 1];
        const next = last + 1;
        const index = randomIndex(array);
        const item = array[index];

        if (item !== next || array.length === 1) {
            result.push(item)
            array.splice(index, 1);
        }
    }

    return result;
}

console.log(randomNonConsecutive(3))
console.log(randomNonConsecutive(4))
console.log(randomNonConsecutive(5))

【讨论】:

  • 跑了几次,我一直看到重复的东西——我第一次得到了几次0, 1, 2,然后又得到了几次1, 2, 0。似乎没有强制执行非连续性
  • @VLAZ 是的,我正在比较索引的最后一项而不是结果
【解决方案3】:

这维护了一组可能的数字,供下一个数字选择。

从这组中选出的下一个数字是随机选择的。

如果选择了一个连续的数字,则交换顺序。

不允许上当。

function randomNonConsecutive(upto) {
  const result = []
  let set = Object.keys([...Array(upto)])
  for(let x = 0; x < upto; x++) {
    let index = Math.floor(Math.random()*set.length)
    let insert = set[index]
    if(x > 0 && Number(result[x-1]) === insert-1) {
        [result[x-1], insert] = [insert, result[x-1]]
    }
    result[x] = insert
    set.splice(index,1)
  }
  return result
}

let result = randomNonConsecutive(10)
console.log(result)

【讨论】:

  • 不幸的是没有重复 - OP 正在改变要求 LOL
【解决方案4】:

如果唯一剩余的值是最后一个值之前的值的增量值,那么您的算法采用随机值并且最后一个值有问题。

例如取值为102,剩余值为3

          v
[1, 0, 2, 3]

在这种情况下,除了不需要的值之外,没有其他可用值。这会产生一个无限循环。


您可以获取所有索引的数组并使用 Fisher-Yates shuffle algorithm 并随机播放,直到数组中没有连续的递增值。

function shuffle(array) { // https://stackoverflow.com/a/2450976/1447675
   var currentIndex = array.length,
       temporaryValue,
       randomIndex;

    // While there remain elements to shuffle...
    while (0 !== currentIndex) {
        // Pick a remaining element...
        randomIndex = Math.floor(Math.random() * currentIndex);
        currentIndex -= 1;

        // And swap it with the current element.
        temporaryValue = array[currentIndex];
        array[currentIndex] = array[randomIndex];
        array[randomIndex] = temporaryValue;
    }

    return array;
}

let length = 4,
    indices = Array.from({ length }, (_, i) => i);

do indices = shuffle(indices);
while (indices.some((v, i, a) => v + 1 === a[i + 1]))

console.log(...indices);

【讨论】:

  • 不是 dv,但总的来说,在没有解释可能是家庭作业问题的情况下给出解决方案可能不会帮助他学习或理解他哪里出错了。
  • @xdumaine 同意这部分'给出一个没有解释的解决方案',不同意这部分'可能是一个家庭作业问题';)
【解决方案5】:

长度为 4 时,您的结果数组只有几个可能的结果(见下文)。

您的函数的问题是,如果您从没有可能答案的数字开始,它将永远运行,并且只有 4 个可能的数字,发生这种情况的可能性非常高。 如果返回 0 或 3 作为第一个数字,它将永远找不到解决方案。如果返回 1 并且第二个数字不是 3,或者如果 2 是第一个并且第二个数字不是 0,则相同。 更新,因为 cmets 澄清了允许降序的连续数字(这看起来很奇怪)。

你也将永远运行,因为你的 while 循环应该使用 indexes.length &lt; length 而不是 &lt;=

[0, 1, 2, 3] // no
[0, 1, 3, 2] // no
[0, 2, 1, 3] // yes
[0, 2, 3, 1] // no
[0, 3, 1, 2] // no
[0, 3, 2, 1] // yes
[1, 0, 2, 3] // no
[1, 0, 3, 2] // yes
[1, 2, 3, 0] // no
[1, 2, 0, 3] // no
[1, 3, 0, 2] // yes
[1, 3, 2, 0] // yes
[2, 0, 1, 3] // no
[2, 0, 3, 1] // yes
[2, 1, 0, 3] // yes
[2, 1, 3, 0] // yes
[2, 3, 0, 1] // no
[2, 3, 1, 0] // no
[3, 0, 1, 2] // no
[3, 0, 2, 1] // yes
[3, 1, 0, 2] // yes
[3, 1, 2, 0] // no
[3, 2, 0, 1] // no
[3, 2, 1, 0] // yes

这告诉我们,这个算法很可能会失败,因为当没有剩余的数字可以满足条件时,它没有“失败”条件处理。您可以检查一下,如果是这种情况,请重新启动,但它仍然可能很慢。换一种方法会更好。

【讨论】:

  • 感谢您的回答。现在我知道代码有什么问题了..加一个
  • @foxer 你还有另一个问题是你的while循环应该使用&lt;而不是&lt;=
猜你喜欢
  • 2011-01-23
  • 2017-06-04
  • 1970-01-01
  • 2013-09-16
  • 1970-01-01
  • 2013-04-18
  • 1970-01-01
相关资源
最近更新 更多