Javascript,Array shuffle但保持一定的距离

时间:2014-04-23 16:33:16

标签: javascript arrays distance shuffle

我需要一个函数来重排元素数组,但是,元素之间保持最小距离为2。

让我解释一下我的情况,我有一个不停的输入,每隔200毫秒不停地发送给我一个字。我还有一个带有预定义词的独特数组。

我收到的每个单词,我检查它是否正常,如果在我的条件下它是正常的,我想把它放在我的数组中。但是,在那一刻,我不再需要一个独特的阵列了,所以我的阵列中可能会有双打,事实上,我希望有两个符合我条件的单词。

好的,所以,这是问题,我收到的每个OK单词,我想把它放在数组然后随机播放它,它可以是一个双,但是,在这种情况下我想保持这个之间的距离单词和数组中的其他相同单词。

例:
独特阵列:[foo,bar,baz,qux]
我启动程序,每200ms发给我一个字 得到:fubar> OK
插入:[foo,bar,baz,qux,fubar]
得到:橙色> OK
插入:[foo,bar,baz,qux,fubar,orange]
得到:石灰> OK
插入:[foo,bar,baz,qux,fubar,orange,lime]
得到:qux> OK(DOUBLE)

插入正确的方式:[foo,bar,baz, qux ,fubar,orange,lime, qux ]
插入正确的方式:[foo, qux ,bar,baz, qux ,fubar,orange,lime]
插错方式:[foo,bar,baz, qux qux ,fubar,orange,lime]

第一个距离为3,第二个距离为2 ...... 第三个和错误的,距离为0。

任何人都可以给我一个很好的方法和/或逻辑来做到这一点?对于类似情况,最小距离= 2。

提前谢谢。


编辑1: 用户jfriend00显示阵列充满双打的可能性,假设它们都具有最小距离,并且插入的下一个元素没有正确的位置,那么,我不能插入它。


编辑2: 我还想到,我必须避免插入可能使下一次插入之一无法使用的位置,如下所示:

得到了:苹果> OK
(A)插入正确的方式:[baz, qux ,fubar,orange,lime, apple ]

得到:qux> OK(DOUBLE)
(A)插入正确的方式:[baz, qux ,fubar,orange, qux ,lime,apple]
(B)插入错误的方式:[baz, qux ,fubar,orange,lime, qux ,apple]

这里,插入物被插入距离3(B)的qux“截断”。

得到:qux> OK(DOUBLE)
(A)插入正确的方式:[baz, qux ,fubar,orange, qux ,lime,apple, qux ]
(B)插入错误的方式:[baz, qux ,fubar,orange,lime, qux ,apple]

3 个答案:

答案 0 :(得分:2)

这是一个一次添加项目的算法。基本思路是:

  1. 获取要添加到列表中的项目
  2. 如果列表为空,只需添加
  3. 即可
  4. 如果列表不为空,则生成一个可以添加到列表中的所有可能位置的数组
  5. 随机选择其中一个可能的职位
  6. 通过检查当前位于该位置的项目及其前面的项目来检查该位置是否合法。如果是合法的,请在刚检查的两个项目之间添加它。
  7. 如果该位置不合法,则从所有可能位置的数组中移除该位置,然后随机选择其他位置。将其从阵列中移除确保您不会再次尝试相同的非法位置
  8. 重复,直到你找到一个合法的位置(所以你把它插入那里)或直到你用完了可能的位置(因为你检查了所有可能的位置而无法放置它)
  9. 工作演示:http://jsfiddle.net/jfriend00/5xGV3/

    var sourceOfItems = ["apple", "grapefruit", "orange", "lime", "pear", "peach", "apricot"];
    
    function insertNextItem(list, item) {
    
        // special case an empty items array
        if (list.length === 0) {
            list.push(item);
            return true; 
        }
    
        // build an array of possible insertion positions
        // items.length + 1 means to add it at the end
        var possiblePositions = [];
        for (var i = 0; i < list.length + 1; i++) {
            possiblePositions.push(i);
        }
    
        // select random position, see if that is allowed
        // if not, remove it from the possibilities and select a new random position
        while (possiblePositions.length > 0) {
            var randIndex = Math.floor(Math.random() * possiblePositions.length);
            var pos = possiblePositions[randIndex];
            // check if this position is allowed
            if (list[pos] !== item && (pos === 0 || list[pos - 1] !== item)) {
                // position is allowed, insert it and return
                list.splice(pos, 0, item);
                return true;
            } else {
                // was not allowed, so remove this possiblePositions choice
                // and let the loop try again
                possiblePositions.splice(randIndex, 1);
            }
        }
        return false;    
    }
    
    function buildList(sourceItems, num) {
        var items = [];
        for (var i = 0; i < num; i++) {
            insertNextItem(items, sourceOfItems[Math.floor(Math.random() * sourceOfItems.length)]);
        }
        return items;
    }
    
    function go() {
        var list = buildList(sourceOfItems, 20);
    }
    

    对于可处理任何最小距离的更通用版本,请参阅此处的jsFiddle:http://jsfiddle.net/jfriend00/LxWNa/

    P.S。在查看jsFiddle时,查看调试控制台输出是非常有趣的,因为它显示了哪些位置被尝试不起作用以及哪些项目无法插入到数组中,因为它没有合法的位置。

答案 1 :(得分:2)

这不会在以后积极添加项目,但会随机随机播放阵列! 也许它会帮助激发一种只在之后添加索引的方法?希望它有所帮助!

//Shuffles an array randomly
var shuffle = function (arrayinput){
  //loops around shuffling for four times the length of the array
  for(i= 0; i<(4 * arrayinput.length); i++){
    //Math.random generates a pseudo-random number between 0 and 1
    //that multiplied by arrayinput.length gives us a random number between 0 and
    //the array length. Math.floor rounds the number down, so we have a pseudo-random
    //number between 0 and the array length - 1, which is all of the array's indexes.
    var a = arrayinput[Math.floor(Math.random() * (arrayinput.length))];
    //saves the value at index a as var b
    var b = arrayinput[a];
    //deletes the one random value at index a from the array
    arrayinput.splice(a, 1);
    //adds that random value back to the beginning of the array
    arrayinput.unshift(b);
  }
  //returns shuffled array
  return arrayinput;
}

答案 2 :(得分:1)

这不简单,我必须承认。让我们从一些辅助函数开始:

Array.prototype.unique = function() {
    var i = 0, t = 1, l = (this.length >>> 0) - 1;
    for (; i < l; t = ++i + 1)
        while ((t = this.indexOf(this[i], t)) !== -1)
            this.splice(t, 1);
    return this;
};
Array.prototype.count = function(item) {
    var i = 0, t = 0;
    while (i = this.indexOf(item, i) + 1) t++;
    return t;
};
Array.prototype.remove = function(item) {
    var i = this.indexOf(item);
    if (i !== -1) this.splice(i, 1);
    return this;
};

我希望意思清楚,即使代码看起来很模糊。对于旧浏览器(IE8-,基本上),还有Array.prototype.indexOf的polyfill。

现在代码:

function shuffleArray(array, previous) {
    if (!array.length) return array;
    // The list of unique items in the array
    var univals = array.slice().unique(),
        idx, pivot, rest;
    if (previous != null)
        // The array can't start with the previous element
        univals.remove(previous);

    while (univals.length) {
        // We choose an element from the possible ones
        idx = Math.floor(Math.random() * univals.length);
        pivot = univals[idx];
        // We try to shuffle the rest of the array.
        rest = shuffleArray(array.slice().remove(pivot), pivot);
        // If we got a shuffled array, we're done.
        // Else, we remove our pivot element from the possible ones
        if (rest != null)
            return [pivot].concat(rest);
        else univals.splice(idx, 1);
    }
    return null;
}

用法是:

shuffleArray(array);

您可以添加第二个可选参数,以避免以某个元素开头。它在函数本身内递归使用。如果得到null,那么就不可能使用给定的约束来重新排列数组。

请记住,此算法可以进行优化。特别是,您可以更改它,使其不是递归的,而是迭代的,更适合更大的数组。