回溯算法创建“混乱”而非随机数组

时间:2017-08-18 00:52:32

标签: javascript backtracking

我已经编写了这个回溯算法,从10个项目的输入数组中制作出70个项目的混乱数组。它需要遵循的规则是:

  1. 每组5个中没有重复项目
  2. 在3个连续的5个
  3. 组中的任何一个中,没有任何项目出现在同一位置
  4. 每个项目总共出现7次
  5. 这几乎可以工作,但只有当我使输入数组大于我的输出数组时才会破坏规则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>

1 个答案:

答案 0 :(得分:1)

没有太多的输入是一个问题。如果你运行代码足够多次,我认为你会发现它有时会起作用,但有时候,根据shuffle的结果,只是不可能将任何剩余的输入放到输出数组上。

就像玩Solitaire一样:可能在开始时就是的解决方案,但根据您所做的决定(从输入数组中挑选项目),您可能仍会失败。

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

如果您完全通过输入列表循环但没有成功,那么您永远不会。然后你有几个选择:

  • 返回您拥有的输出和剩余输入(可能只是为了查看它失败了。
  • 无论您是否记录失败的尝试,都可以重新启动,然后重试。只是蛮力试图随意找到解决方案。

一种方法:

<!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>