尽可能多地随机播放阵列

时间:2013-09-08 06:40:00

标签: javascript

我有一个像这样的数组

[0,2,3]

这个数组的可能改组是

[0,2,3], [2,3,0], [3,0,2], [3,2,0], [0,3,2], [2,0,3]

我如何获得这些组合?我目前唯一的想法是

n = maximum num of possible combinations, coms = []
while( coms.length <= n )
    temp = shuffle( original the array );
    if temp is there in coms
       return
    else 
       coms.push(temp);

但我不认为这是有效的,因为我们盲目地依赖于随机方法的均匀分布。

这个问题有替代发现吗?

8 个答案:

答案 0 :(得分:9)

首先要注意的是,关于元素的数量(13个元素= 6个bilion排列),排列的数量增加得非常快,因此生成它们的任何类型的算法都会因为足够大的输入而在性能上恶化阵列。

要注意的第二件事是,由于排列的数量非常大,因此将它们存储在内存中是很昂贵的,因此最好使用生成器进行排列,并在生成它们时使用它们。 / p>

第三点需要注意的是,递归算法会带来很大的开销,因此即使找到递归解决方案,也应该努力获得非递归算​​法。如果存在递归解,则获得非递归解决方案总是可行的,但这可能会增加代码的复杂性。

我根据Steinhaus-Johnson-Trotter算法(http://en.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm)为您编写了非递归实现

function swap(arr, a,b){
  var temp = arr[a];
  arr[a]=arr[b];
  arr[b]=temp;
}

function factorial(n) {
  var val = 1;
  for (var i=1; i<n; i++) {
    val *= i;
  }
  return val;
}


function permute(perm, func){
  var total = factorial(perm.length);

  for (var j=0, i=0, inc=1;  j<total;  j++, inc*=-1, i+=inc) {

    for (; i<perm.length-1 && i>=0; i+=inc) {
      func.call(perm);
      swap (perm, i, i+1);
    }  

    func.call(perm);

    if (inc === 1) {
      swap(perm, 0,1);
    } else {
      swap(perm, perm.length-1, perm.length-2);
    }
  }
}

console.clear();

count = 0;
permute([1,2,3,4,5,6], function(){console.log(this); count++;});

console.log('There have been ' + count + ' permutations');

http://jsbin.com/eXefawe/2/edit

答案 1 :(得分:4)

尝试递归方法。这是一个提示:[0,2,3]的每个排列都是

  • [0]加上[2,3]
  • 的排列
  • [2]加上[0,3]
  • 的排列
  • [3]加上[0,2]
  • 的排列

答案 2 :(得分:4)

正如黄道带所提到的,这个问题的最佳解决方案是递归的:

var permute = (function () {
    return permute;

    function permute(list) {
        return list.length ?
            list.reduce(permutate, []) :
            [[]];
    }

    function permutate(permutations, item, index, list) {
        return permutations.concat(permute(
            list.slice(0, index).concat(
            list.slice(index + 1)))
            .map(concat, [item]));
    }

    function concat(list) {
        return this.concat(list);
    }
}());

alert(JSON.stringify(permute([1,2,3])));

希望有所帮助。

答案 3 :(得分:3)

通过选择集合中的元素并递归地置换(重新排列)其余元素,可以找到集合中的所有permutationsBacktracking方法可用于寻找解决方案。

算法步骤(source):

enter image description here

伪代码(source):

permute(i) 
   if i == N  output A[N] 
   else 
      for j = i to N do 
         swap(A[i], A[j]) 
         permute(i+1) 
         swap(A[i], A[j])

Javascript实施(jsFiddle):

Array.prototype.clone = function () {
    return this.slice(0);
};

var input = [1, 2, 3, 4];
var output = [];

function permute(i) {
    if (i == input.length)
        output.push(input.clone());
    else {
        for (var j = i; j < input.length; j++) {
            swap(i, j);
            permute(i + 1);
            swap(i, j); // backtrack
        }
    }
};

function swap(i, j) {
    var temp = input[i];
    input[i] = input[j];
    input[j] = temp;
}

permute(0);
console.log(output);

答案 4 :(得分:1)

对于长度为n的数组,我们可以预先计算可能的排列数。这是n! (n阶乘)

function factorial(n){ return n<=0?1:n*factorial(n-1);}
//There are better ways, but just for illustration's sake

并且,我们可以创建一个函数,将0 ... n!-1之间的整数p映射到不同的排列。

function map(p,orgArr){
 var tempArr=orgArr.slice(); //Create a copy
 var l=orgArr.length;
 var permArr=[];
 var pick; 
 do{
  pick=p%l; //mod operator
  permArr.push(tempArr.splice(pick,1)[0]); //Remove item number pick from the old array and onto the new
  p=(p-pick)/l;
  l--;
 }while(l>=1)
 return permArr;  
}

此时,您需要做的就是创建一个数组ordering=[0,1,2,3,...,factorial(n)-1]并将其改组。然后,您可以循环for(var i=0;i<=ordering.length;i++) doSomething(map(ordering[i],YourArray));

这就留下了如何改变排序数组的问题。我相信这个文档很好,并且超出了你的问题的范围,因为答案取决于你的应用程序(即伪随机足够好,或者你需要一些加密强度,所需的速度等等)。见How to randomize (shuffle) a JavaScript array?和其他许多人。

或者,如果排列的数量如此之大,以至于您不想创建这个巨大的排序数组,那么您只需要在0-n!-1之间明确选择上述循环的i值。如果只需要统一性而不是随机性,一种简单的方法就是使用原始根:http://en.wikipedia.org/wiki/Primitive_root_modulo_n

答案 5 :(得分:1)

你不需要递归。该演示应该使模式非常清晰:http://jsfiddle.net/BGYk4/

function shuffle(arr) {
        var output = [];
        var n = arr.length;
        var ways = [];
        for(var i = 0, j = 1; i < n; ways.push(j *= ++i));
        var totalWays = ways.pop();
        for(var i = 0; i < totalWays; i++) {
                var copy = arr.slice();
                output[i] = [];
                for(var k = ways.length - 1; k >= 0; k--) {
                        var c = Math.floor(i/ways[k]) % (k + 2);
                        output[i].push(copy.splice(c,1)[0]);
                }
                output[i].push(copy[0]);
        }
        return output;
}

这应该输出所有可能的“shuffles”

console.log(shuffle(['a', 'b', 'c', 'd', 'e']));

答案 6 :(得分:1)

这个人的可爱(但另一个例子的输出更清晰):http://jsfiddle.net/wCnLf/

function shuffle(list) {
    var shufflings = [];
    while(true) {
        var clone = list.slice();
        var shuffling = [];
        var period = 1;
        while(clone.length) {
            var index = Math.floor(shufflings.length / period) % clone.length;
            period *= clone.length;
            shuffling.push(clone.splice(index,1)[0]);
        }
        shufflings.push(shuffling);
        if(shufflings.length == period) return shufflings;
    }
}

当然它仍会输出所有可能的“shuffles”

console.log(shuffle(['a', 'b', 'c', 'd', 'e']));

答案 7 :(得分:0)

tibos上面的示例正是我所寻找的,但我在运行它时遇到了一些麻烦,我作为npm模块制作了另一个解决方案:

var generator = new Permutation([1, 2, 3]);
while (generator.hasNext()) {
  snippet.log(generator.next());
}
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<script src="http://rawgit.com/bcard/iterative-permutation/master/iterative-permutation.js"></script>

https://www.npmjs.com/package/iterative-permutation

https://github.com/bcard/iterative-permutation