如何打乱 Javascript 数组以确保每个索引都位于新数组中的新位置?

How do I shuffle a Javascript Array ensuring each Index is in a new position in the new Array?

我有一个对象数组,就像这样。

var usersGoing = [
    { user: 0 },
    { user: 1 },
    { user: 2 },
    { user: 3 },
    { user: 4 }
];

我需要打乱这个数组,以便没有对象保留在与实例化时相同的索引中,如下所示:

[
    { user: 3 },
    { user: 2 },
    { user: 4 },
    { user: 0 },
    { user: 1 }
]

生成的数组必须以这种方式排序,因为每个用户对象都将分配给不同的用户对象。

我尝试了几种不同的排序算法,包括 Fisher-Yates,并且我尝试使用 Underscore.js' _.shuffle(),以及 Kirupa Shuffling an Array in JavaScript 的这个变体:

function shuffleFY(input) {
    for (var i = input.length-1; i >=0; i--) {
        var randomIndex = Math.floor(Math.random()*(i+1)); 
        var itemAtIndex = input[randomIndex]; 

        input[randomIndex] = input[i]; 
        input[i] = itemAtIndex;
    }
    return input;
}

我试过的都没有用。有帮助吗?

更新:我在下面将答案标记为正确,因为正确遵循了 Sattolo 循环的关键点。此外,这不是 Shuffles Random Numbers with no repetition in Javascript/PHP 的重复项,因为此问题对结果数组有额外要求,不仅不包含重复项,而且不能在相同的初始索引位置包含项目。

对于每个位置,随机选择一个位置较高的索引并将两者交换。

每个对象要么在自己的位置并换出并锁定其原始位置,要么在其位置准备好更换时换到较低的位置并锁定到位。

当您到达倒数第二个位置时,您将保证换出最后一个位置(它可能与原来的值相同,也可能不同),您就完成了。最终位置无事可做。

1 2 3 4 5 - 原始值

3 2 1 4 5

3 1 2 4 5

3 1 4 2 5

3 1 4 5 2

试试这个 Fisher-Yates-Durstenfeld shuffle:

var usersGoing = [
    { user: 0 },
    { user: 1 },
    { user: 2 },
    { user: 3 },
    { user: 4 }
];


shuffle(usersGoing);
function shuffle(sourceArray) {
    for (var n = 0; n < sourceArray.length - 1; n++) {
        var k = n + Math.floor(Math.random() * (sourceArray.length - n));

        var temp = sourceArray[k];
        sourceArray[k] = sourceArray[n];
        sourceArray[n] = temp;
    }
}

console.log(usersGoing);

您在 Python 中使用 Sattolo 的算法发布了 link:

from random import randrange

def sattoloCycle(items):
    i = len(items)
    while i > 1:
        i = i - 1
        j = randrange(i)  # 0 <= j <= i-1
        items[j], items[i] = items[i], items[j]
    return

这里翻译成JavaScript:

function sattoloCycle(items) {
  for(var i = items.length; i-- > 1; ) {
    var j = Math.floor(Math.random() * i);
    var tmp = items[i];
    items[i] = items[j];
    items[j] = tmp;
  }
}

有点过时的答案,可能您的软件现在已经完成、打包和销售,但有更好的方法来实现这一点...

const arr = [1,2,3,4,5,6,7,8,9];
const sfl = arr.sort( () => { Math.random() - .5 } );
// sfl == [2,9,5,1,3,6,...]

@xaxxon 删掉的答案是对的。就“洗牌”一词而言,这不是一个有意义的请求。洗牌的时候不能设置这样的限制条件,因为洗牌涉及到随机性。对于你想要的,只需旋转阵列就可以了。如果您现在对轮换不满意,您必须解释原因...