随机播放阵列并防止连续2个以上

时间:2015-10-19 17:44:27

标签: javascript arrays

我有一个像这样构建的数组:

var entries = ['L','L','L','L','L','L','L','L','L','L','R','R','R','R','R','R','R','R','R','R','M','M','M','M','M']

这意味着阵列总是充满10倍L,10倍R和5倍M

我想要实现的输出是一个随机生成的数组,所以我想出了一个简单的解决方案,只需用

进行随机播放
function shuffle(o){
        for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
        return o;
}

我现在遇到的问题是结果有一条规则,连续两次以上的信件都不会超过2次。所以我想我只是使用do / while循环来进行洗牌,直到满足该条件。但在我的测试运行中,这完全失败了很长的循环。

所以我的问题是 - 在不依靠运气的情况下构建此阵列的最佳方法是什么。我失败的完整程序是这样的

function shuffle(o){
  for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
  return o;
}

function createProgram(numShooters){
    var programs = [];

    for(var s = 0; s < numShooters; s++){

        //Build array with L/R/M
        for( d=0; d < 10; d++ ){
            program.push('L');
            program.push('R');
            if(d < 5){
                program.push('M');    
            }
        }
        
        // This will run way too long and is not reliable
      
        //do{
        //  program = shuffle(program);    
        //}while(!checkProgram(program))
                      
        if(!checkProgram(program)){
            console.log('invalid program at ' + s);
        }

        programs[s] = program;

    }

    return programs;
}

function checkProgram(program){
    var len = program.length;
    var last = null;
    var dups = 0;

    for(var i=0; i<len; len++){
        
        if(program[i] == last){
            dups++;
        }else{
            dups = 0;
        }

        if(dups == 2){
            return false;
        }
        
        last = program[i];
    }

    return true;
}

createProgram(5);

2 个答案:

答案 0 :(得分:1)

您可以通过随机选择字符来创建它们,而不是仅仅随机播放数组并希望没有重复项,而是专门避免选择之前选择的字符。

如果您跟踪每个角色的剩余数量,您可以控制角色选择的几率,以便分配正确。例如,如果前两个字符是L,那么剩下10 R和5 M可以选择(和8 L,但是它们被排除在下一个选择中),所以应该有2:3的机会选择一个R和三分之一的机会挑选和M.

这种方法可能会遇到无法完成数组的死胡同,因此必须重新开始。运行它几百次我看到了10%的开销,所以如果你创建五个数组,你应该平均每隔一段时间看一次重试。

function createProgram(numShooters){
  var programs = [];

  for(var s = 0; s < numShooters; s++){

    var chars = [ 'L', 'R', 'M' ];
    var program;
    do {
        
      program = [];
      var cnt = [ 10, 10, 5, 0 ]; // picks left
      var prev = 3; // previous pick
      var tot = 25; // total picks left
      while (program.length < 25) {
        // check for duplicates
        var x = program.length >= 2 && program[program.length - 2] == program[program.length - 1] ? prev : 3;
        // check if more picks are possible
        if (tot - cnt[x] <= 0) {
          console.log('invalid program ' + program);
          break;
        }
        // pick from the possible
        var r = Math.floor(Math.random() * (tot - cnt[x]));
        // determine what character was picked
        var c = 0;
        while (c == x || r >= cnt[c]) {
          if (c != x) r -= cnt[c];
          c++;
        }
        program.push(chars[c]);
        cnt[c]--;
        tot--;
        prev = c;
      }

    } while (program.length < 25);

    programs[s] = program;

  }

  return programs;
}

console.log(createProgram(1).toString());

答案 1 :(得分:0)

所以这是我提出的最终解决方案,因为评论Guffas解决方案也很有效且顺畅。但经过一些测试后,我的速度提高了约30%,可读性也提高了,但是时间更长,所以我会接受Guffas - 感谢所有人的投入!

function createProgram(numShooters){
    var programs = [];

    for(var s = 0; s < numShooters; s++){

        var program = buildProgram();
        programs[s] = program;

    }

    return programs;
}

function buildProgram(){
    var program = [];
    var ls = fillArray('L',10);
    var rs = fillArray('R',10);
    var ms = fillArray('M',5);

    //Use either L or R to mix into M - adds variation
    var side = Math.random() > 0.5 ? ls : rs;
    var otherSide = side == ls ? rs : ls;
    var initProg = side.concat(ms);
    initProg = shuffle(initProg);

    var program = [];

    //Correcting invalid positions as suggested
    for(var p1 = 0; p1 < initProg.length; p1++){
        if(p1 > 1 && initProg[p1-1] == initProg[p1-2] && initProg[p1-1] == initProg[p1]){
            if(otherSide.length > 0){
                program.push(otherSide.pop());    
            }else{
                return buildProgram(); //impossible state, redo...
            }
        }
        program.push(initProg[p1]);
    }

    //Fill into remaining other pos
    for(var p2 = 0; p2 < otherSide.length; p2++){
        program = addAtRandomPos(program,otherSide[p2]);
    }

    return program;
}

function addAtRandomPos(arr,chr){
    var pos = getRandomInt( 0, arr.length - 1 );
    var charCur = arr[pos];

    var pprev = pos > 1 ? arr[pos-2] : null;
    var prev  = pos > 0 ? arr[pos-1] : null;
    var next  = pos < arr.length - 1 ? arr[pos] : null;
    var nnext = pos < arr.length - 2 ? arr[pos+1] : null;

    var str = pprev + prev + chr + next + nnext;

    if(str.indexOf('MMM') !== -1 || str.indexOf('RRR') !== -1 || str.indexOf('LLL') !== -1){
        return addAtRandomPos(arr,chr);
    }else{
        arr.splice(pos,0,chr);
    }

    return arr;

}

function getRandomInt (min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

function fillArray(chr,num){
    var arr = [];
    for(i = 0; i < num; i++){
        arr.push(chr);
    }
    return arr;
}