19

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

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

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

如果 chooseName() 函数最初选择了最近 5 次中使用的名称,它会简单地中断并再次调用自己,重复直到找到“唯一”名称。

我有两个问题:

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

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

感谢您的任何帮助。

    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);
4

11 回答 11

36

我喜欢评论者@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.
于 2013-07-26T21:34:41.807 回答
12

Whenever an item is selected, move it to the back of the array and randomly select from a slice of the original array 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)。例如

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);
}
于 2013-07-26T21:23:57.063 回答
5

我推荐你使用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);
于 2013-07-26T21:30:05.567 回答
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);
}
于 2013-07-26T21:30:30.730 回答
0

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

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

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

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

于 2016-09-03T21:10:50.887 回答
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;
    }
}
于 2018-03-31T13:56:58.763 回答
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>

于 2019-05-23T00:00:15.980 回答
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);
}
于 2020-07-18T15:37:59.707 回答
0
var a = ["Roger", "Russell", "Clyde", "Egbert", "Clare", "Bobbie", "Simon", 
"Elizabeth", "Ted", "Caroline","Brezza","Elephant","Jack","Virat"];    
let b=[a[Math.floor(Math.random() * a.length)]];

for(let i=1; i<=12; i++){
  let t = a[Math.floor(Math.random() * a.length)];
  const n = b.indexOf(t);
   if (n >= 0) {
      b = b.filter((it, i) => it !== t);
    } else {
     b = [...b, t];
    } 
      if(b.length === 12 ){
         break;
       }else{
         if(i === 12){
             i--;
         }
      }
   }
于 2021-06-07T18:58:29.700 回答
-1

尝试一下。

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;
}
于 2019-12-06T14:54:20.617 回答
-2

//试试这个:

var a = [“罗杰”、“罗素”、“克莱德”、“埃格伯特”、“克莱尔”、“博比”、“西蒙”、“伊丽莎白”、“泰德”、“卡罗琳”];

变量 b = [];

b = shuffle(a).slice(0,5); // 如果你想要 5 个数字,这是前提条件。
控制台.log(b);

于 2019-11-29T18:47:07.647 回答