回溯算法创建 "jumbled" 但不是随机数组

Backtracking algorithm to create a "jumbled" but not random array

我编写了这个回溯算法,以从包含 10 个项目的输入数组中生成一个包含 70 个项目的混乱数组。它需要遵循的规则是:

  1. 每组5个无重复项
  2. 在 5 个连续 3 组中的任何一个中,没有项目出现在相同位置
  3. 每个项目总共出现 7 次

这几乎可行,但前提是我使输入数组大于输出数组,这违反了规则 3。如果我使输入数组长度为 70,算法有时会工作但有时会溢出。

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="content-type" content="text/html" />

 <title>Backtracking Pseudo Randomiser</title>
</head>

<body>
<button onclick=go();>Go</button>

<script>
function go() {
   
    function pseudoRan(input,output) {
        if (output.length==70) {
            output=listToMatrix(output,5);
            printIt(output);
            return; 
        }
                else {
                    var tmp=input.shift();
                    var mod=output.length % 5;
                    if (output.slice(-mod-1).indexOf(tmp)==-1 && output[output.length-5]!=tmp && output[output.length-10]!=tmp) {
                        output.push(tmp);
                        pseudoRan(input,output);
                    }
                    else {
                        input.push(tmp);
                        pseudoRan(input,output);
                    }
                    
                }
                    
                
    }
    
var input=["A","B","C","D","E","F","G","H","I","K"];
var output=[];
input=pool(input,70);
input=yatesShuffle(input);
pseudoRan(input, output);

    
    //analyse it
var freqs=output.byCount();
var strFreqs="";
for(a=0;a<freqs.length;a++){
      strFreqs+="Item: " + freqs[a].item + "   ..." + freqs[a].frequency + "<br />";
      document.getElementById("2").innerHTML=strFreqs;
    }
}
    
    
function pool(array,total) {
    var factor=total/array.length;
    var newArray=[];
    for (a=0;a<factor;a++) {
        for (b=0;b<array.length;b++) {
            newArray.push(array[b]);
        }
    }
    //console.log(newArray);
    return newArray;
}
    
function yatesShuffle (array) {
    for (var i = array.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * i); // no +1 here!
        var temp = array[i];
        array[i] = array[j];
        array[j] = temp;
        }   
    return array;
}
    

function listToMatrix(list, elementsPerSubArray) {
    var matrix = [], i, k;

    for (i = 0, k = -1; i < list.length; i++) {
        if (i % elementsPerSubArray === 0) {
            k++;
            matrix[k] = [];
        }

        matrix[k].push(list[i]);
    }

    return matrix;
}
    
function printIt(array) {
    for (i=0;i<array.length;i++) {
        var str=" ";
        for (j=0;j<array[i].length;j++) {
            str+=array[i][j]+" ";
        }
        document.getElementById("1").insertAdjacentHTML('beforeend',str + "</br>");
        
        //console.log(array[i]);
    }
}
Array.prototype.byCount= function(){
    var itm, a= [], L= this.length, o= {};
    for(var i= 0; i<L; i++){
        itm= this[i];
        if(!itm) continue;
        if(o[itm]== undefined) o[itm]= 1;
        else ++o[itm];
    }
    for(var p in o) a[a.length]= {item: p, frequency: o[p]};
    return a.sort(function(a, b){
        return o[b.item]-o[a.item];
    });
}
    
</script>
<div id="1" style="font-family:'Courier New';"></div>
    <br />
<div id="2"></div>

</body>
</html>

问题不在于输入太多。如果您 运行 代码足够多次,我想您会发现有时它会起作用,但有时,根据随机播放的结果,不可能将任何剩余的输入放入输出数组。

这就像玩纸牌游戏:可能 是一开始的解决方案,但取决于您在进行过程中做出的决定(从输入数组中挑选项目),您可能还是输了


如果您完全循环输入数组但没有任何成功,您应该至少跟踪。

如果您已经完全遍历了输入列表但没有成功,那么您将永远不会成功。那么你有几个选择:

  • Return 您的输出和剩余的输入(可能有助于查看失败。
  • 无论您是否记录失败的尝试,您都可以重新启动并重试。只是随机尝试蛮力找到解决方案。

一种方法:

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="content-type" content="text/html" />

 <title>Backtracking Pseudo Randomiser</title>
</head>

<body>
<button onclick=go();>Go</button>

<script>
function go() {
       
    var tracker = 0

    function pseudoRan(input,output) {
        if (output.length==70) {
            output=listToMatrix(output,5);
            printIt(output);
            return true; 
        }
        else {
            var tmp=input.shift();
            var mod=output.length % 5;

            console.log('input.length::tracker ==>', input.length + '::' + tracker)
            
            if (output.slice(-mod-1).indexOf(tmp)==-1 && output[output.length-5]!=tmp && output[output.length-10]!=tmp) {
                tracker = 0
                output.push(tmp);
                return pseudoRan(input,output);
            }
            else {
                tracker++
                if (tracker > input.length) {
                    console.log('Failed this time.')
                    output=listToMatrix(output,5);
                    console.log('output-----------------------');
                    printIt(output);
                    console.log('input------------------------');
                    printIt(input);
                    return false // FAILED to finish
                }
                input.push(tmp);
                return pseudoRan(input,output);
            }
            
        }
                    
                
    }
        
    var input=["A","B","C","D","E","F","G","H","I","K"];
    input=pool(input,300);

    // run until we get an answer
    do {
        var output=[];
        tracker = 0;
        // operate on copy of the data
        runInput=yatesShuffle(JSON.parse(JSON.stringify(input)));
    } while (!pseudoRan(runInput, output))
    

        
    // analyse it
    var freqs=output.byCount();
    var strFreqs="";
    for(a=0;a<freqs.length;a++){
        strFreqs+="Item: " + freqs[a].item + "   ..." + freqs[a].frequency + "<br />";
        document.getElementById("2").innerHTML=strFreqs;
    }
}
        
    
function pool(array,total) {
    var factor=total/array.length;
    var newArray=[];
    for (a=0;a<factor;a++) {
        for (b=0;b<array.length;b++) {
            newArray.push(array[b]);
        }
    }
    //console.log(newArray);
    return newArray;
}
    
function yatesShuffle (array) {
    for (var i = array.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * i); // no +1 here!
        var temp = array[i];
        array[i] = array[j];
        array[j] = temp;
        }   
    return array;
}
    

function listToMatrix(list, elementsPerSubArray) {
    var matrix = [], i, k;

    for (i = 0, k = -1; i < list.length; i++) {
        if (i % elementsPerSubArray === 0) {
            k++;
            matrix[k] = [];
        }

        matrix[k].push(list[i]);
    }

    return matrix;
}
    
function printIt(array) {
    for (i=0;i<array.length;i++) {
        var str=" ";
        for (j=0;j<array[i].length;j++) {
            str+=array[i][j]+" ";
        }
        document.getElementById("1").insertAdjacentHTML('beforeend',str + "</br>");
        
        console.log(array[i]);
    }
}
Array.prototype.byCount= function(){
    var itm, a= [], L= this.length, o= {};
    for(var i= 0; i<L; i++){
        itm= this[i];
        if(!itm) continue;
        if(o[itm]== undefined) o[itm]= 1;
        else ++o[itm];
    }
    for(var p in o) a[a.length]= {item: p, frequency: o[p]};
    return a.sort(function(a, b){
        return o[b.item]-o[a.item];
    });
}
    
</script>
<div id="1" style="font-family:'Courier New';"></div>
    <br />
<div id="2"></div>

</body>
</html>