如何有效地随机选择数组项而不重复?

时间:2013-07-26 21:16:03

标签: javascript

我知道这个问题存在许多问题,但我无法找到与我的具体效率问题相关的答案。

我有以下代码可以正常工作。

我有一个10项目数组,我随机选择一个项目(按下回车键)。代码保留了5个最新选择的数组,这些选择不能随机选择(以避免随着时间的推移重复过多)。

如果chooseName()函数最初选择了一个在最近的5中使用的名称,那么它只会断开并再次调用自身,重复直到找到“唯一”名称。

我有两个问题:

  1. 说这是一个“递归函数”是正确的吗?

  2. 我担心理论上这可能会在找到一个唯一名称之前保持循环很长时间 - 是否有更有效的方法来做到这一点?

  3. 感谢您的帮助。

        var a = ["Roger", "Russell", "Clyde", "Egbert", "Clare", "Bobbie", "Simon", "Elizabeth", "Ted", "Caroline"];
        var b = [];
    
        var chooseName = function () {
        var unique = true;
        b.length = 5;
        num = Math.floor(Math.random() * a.length);
        name = a[num];    
            for (i = 0; i < a.length; i++) {
            if (b[i] == name) {
                chooseName();
                unique = false;
                break;
                }
            }
            if (unique == true) {
            alert(name);
            b.unshift(name);
            }
        }
    
    
        window.addEventListener("keypress", function (e) {
            var keycode = e.keyCode;
            if (keycode == 13) {
            chooseName();
            }
        }, false);
    

11 个答案:

答案 0 :(得分:25)

我喜欢评论者@ YuriyGalanter关于随机选择项目的想法,直到所有项目都被采用,然后才重复,所以这是一个实现:

function randomNoRepeats(array) {
  var copy = array.slice(0);
  return function() {
    if (copy.length < 1) { copy = array.slice(0); }
    var index = Math.floor(Math.random() * copy.length);
    var item = copy[index];
    copy.splice(index, 1);
    return item;
  };
}

var chooser = randomNoRepeats(['Foo', 'Bar', 'Gah']);
chooser(); // => "Bar"
chooser(); // => "Foo"
chooser(); // => "Gah"
chooser(); // => "Foo" -- only repeats once all items are exhausted.

答案 1 :(得分:8)

每当选择一个项目时,将其移动到数组的后面,然后从原始数组array.slice(0, -5)的切片中随机选择。

var a = ["Roger", "Russell", "Clyde", "Egbert", "Clare", "Bobbie", "Simon", "Elizabeth", "Ted", "Caroline"];

var chooseName = function () {
    var unique = true;
    num = Math.floor(Math.random() * a.length - 5);
    name = a.splice(num,1);
    a.push(name);
}


window.addEventListener("keypress", function (e) {
    var keycode = e.keyCode;
    if (keycode == 13) {
        chooseName();
    }
}, false);

编辑:这也有副作用,即不给出任何变量发生在列表中的不公平的缺点,即在前N次调用中不会考虑它们。如果这对您来说是个问题,可以尝试在某处保持一个静态变量来跟踪要使用的切片的大小,并在B处最大化(在本例中为5)。 e.g。

var a = ["Roger", "Russell", "Clyde", "Egbert", "Clare", "Bobbie", "Simon", "Elizabeth", "Ted", "Caroline"];
B = 5; //max size of 'cache'
N = 0;

var chooseName = function () {
    var unique = true;
    num = Math.floor(Math.random() * a.length - N);
    N = Math.min(N + 1, B);
    name = a.splice(num,1);
    a.push(name);
}

答案 2 :(得分:4)

我建议您使用underscore.js,这将非常简单。

函数shuffle以统一分布的方式实现,因此如果数组a包含更多数据,重复的概率将会很低。

var a = ["Roger", "Russell", "Clyde", "Egbert", "Clare", "Bobbie", "Simon", "Elizabeth", "Ted", "Caroline"];
b = _.shuffle(a).slice(0,5);
console.log(b);

答案 3 :(得分:1)

当您实例化Shuffler时,请将您的数组作为参数提供。它将创建数组的副本,每次调用next()时,它将从副本返回一个随机元素,并将其从副本数组中删除,以便不会重复。

var Shuffler = function(a) {
    var aCopy = [],
        n     = 0;

    // Clone array
    for (n=0; n<a.length; n++) {
        aCopy.push(a[n]);
    }

    this.next = function() {
        if (aCopy.length == 0) { return null; }

        var nRandom  = Math.floor(Math.random() * (aCopy.length + 1)),
            mElement = aCopy[nRandom];

        delete aCopy[nRandom];
        return mElement;
    }
}

var oShuffler   = new Shuffler([/* names go here */]),
    sRandomName = null;

while (sRandomName = oShuffler.next()) {
    console.log(sRandomName);
}

答案 4 :(得分:0)

是的,这是递归的,因为它不会减少状态,理论上它可以永远地继续下去。

我假设不允许更改数组,否则你可以简单地从数组中删除最近的选择并在最近的选择缓冲区溢出时将它们推回。

相反:从选择中排除数组末尾的buffersize项。 (Buffersize从0开始,并随着最近的选择被添加到缓冲区而增长到预设的buffersizemax。)当您做出选择时,将其与您最近的bufffersize选项进行比较。如果找到匹配项,则选择相应的排除项。

显然,这仍然无法检查缓冲区中的每个最近选择以避免匹配。但它确实具有避免可能的递归的效率。

答案 5 :(得分:0)

这项工作对我来说就像是一种魅力,没有任何重复。

   var Random_Value = Pick_Random_Value(Array);
function Pick_Random_Value(IN_Array) 
{
    if(IN_Array != undefined && IN_Array.length > 0)
    {
        var Copy_IN_Array = JSON.parse(JSON.stringify(IN_Array));
        if((typeof window.Last_Pick_Random_Index !== 'undefined') && (window.Last_Pick_Random_Index !== false))
        {
            if(Copy_IN_Array[Last_Pick_Random_Index] != undefined)
            {
                Copy_IN_Array.splice(Last_Pick_Random_Index,1);
            }
        }

        var Return_Value = false;

        if(Copy_IN_Array.length > 0)
        {
            var Random_Key = Math.floor(Math.random() * Copy_IN_Array.length);
            Return_Value = Copy_IN_Array[Random_Key];
        }
        else
        {
            Return_Value = IN_Array[Last_Pick_Random_Index];
        }

        window.Last_Pick_Random_Index = IN_Array.indexOf(Return_Value);
        if(window.Last_Pick_Random_Index === -1)
        {
            for (var i = 0; i < IN_Array.length; i++) 
            {
                if (JSON.stringify(IN_Array[i]) === JSON.stringify(Return_Value)) 
                {
                    window.Last_Pick_Random_Index = i;
                    break;
                }
            }
        }


        return Return_Value;
    }
    else
    {
        return false;
    }
}

答案 6 :(得分:0)

我知道这是一个比较老的问题,但是我在为Web开发课程做一些准备工作时正在做类似的事情。在我的特定情况下,我想随机更改一个盒子的颜色,但没有任何连续的相同颜色的重复。这是我想出的解决方案。使用while循环,我能够达到预期的结果。希望这对某人有帮助。

var colors = ["black","red","yellow","green","blueviolet","brown","coral","orchid","olivedrab","purple"];

function getRandomColor(){
    num = Math.floor(Math.random() * 10); // get a random number between 0-9
    return colors[num];
}

function randomizeColor(){
    currentColor = document.getElementById("box").style.backgroundColor; // got the current color of the box on the page.
    randomColor = getRandomColor(); 
    while (randomColor == currentColor){ // if we get the same color as the current color, try again.
        randomColor = getRandomColor();
    }
    document.getElementById("box").style.backgroundColor = randomColor; // change box to new color
}
<!DOCTYPE html>
<html>
<head>
    <title>Random Color Box</title>
</head>
<body>

    <p>Press the buttons to change the box!</p>
    <div id="box" style="height:150px; width:150px; background-color:red; margin:25px; opacity:1.0;"></div>

    <button id="button" onclick="randomizeColor()">Random Color</button>

    <script type="text/javascript" src="javascript.js"></script>

</body>
</html>

答案 7 :(得分:0)

//尝试以下操作:

var a = [“ Roger”,“ Russell”,“ Clyde”,“ Egbert”,“ Clare”,“ Bobbie”,“ Simon”,“ Elizabeth”,“ Ted”,“ Caroline”]];

var b = [];

b = shuffle(a).slice(0,5); //如果您需要5个数字,则为该数字。
console.log(b);

答案 8 :(得分:0)

尝试一下。

function doShuffle(a) {
   for (var i = a.length - 1; i > 0; i--) {
       var j = Math.floor(Math.random() * (i + 1));
       [a[i], a[j]] = [a[j], a[i]];
   }
   return a;
}

答案 9 :(得分:0)

选择的解决方案很好,但是如果您不想弄乱阵列的顺序,请使用以下解决方案:

创建一个数组,其中包含用于保存要随机选择的数据的数组的索引。然后从该索引数组中随机选择一个项目,并使用其存储的值从数据数组中检索该项目。然后删除索引项,以使索引数组继续变小。

类似这样的东西:

let items = ["red", "blue", "yellow"];
let randomItems = [];
let arrayIndexes =  [...Array(items.length).keys()];

let totalItems = items.length;


for (let i = 0; i < totalItems; i++) {
    let item;

    let mapIndex = Math.floor(Math.random() * (arrayIndexes.length - 1));
    let index = arrayIndexes[mapIndex];
    item = items[index];
    arrayIndexes.splice(mapIndex, 1);

    if ((i === (totalItems - 1)) && (arrayIndexes.length === 0)) {
        // If you choose to set totalItems to a value larger than your dataset,
        // this will re-initialize your array indexes, but you will end up with
        // duplicates in your results. If you don't want duplicates, just make
        // sure totalItems is set to items.length.

        arrayIndexes = [...Array(items.length).keys()];
    }


    randomItems.push(item);
}

答案 10 :(得分:0)

binance_new_order