¿Cómo seleccionar aleatoriamente de manera eficiente un elemento de matriz sin repeticiones?

Resuelto Russell asked hace 11 años • 14 respuestas

Soy consciente de que esta pregunta se presenta de muchas formas, pero no he podido encontrar una respuesta relacionada con mi problema específico de eficiencia.

Tengo el siguiente código que funciona bien.

Tengo una matriz de 10 elementos de los cuales selecciono aleatoriamente un elemento (al presionar la tecla Intro). El código mantiene una serie de las 5 opciones más recientes que no se pueden seleccionar al azar (para evitar demasiadas repeticiones con el tiempo).

Si la función elegirNombre() selecciona inicialmente un nombre que se ha utilizado en los últimos 5 intentos, simplemente se interrumpe y se llama a sí misma nuevamente, repitiendo hasta que encuentra un nombre "único".

Tengo dos preguntas:

  1. ¿Es correcto decir que se trata de una "función recursiva"?

  2. Me preocupa que, en teoría, esto pueda seguir repitiéndose durante mucho tiempo antes de encontrar un nombre único. ¿Existe una forma más eficiente de hacerlo?

Gracias por cualquier ayuda.

    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);
Russell avatar Jul 27 '13 04:07 Russell
Aceptado

Me gusta la idea del comentarista @YuriyGalanter de elegir elementos al azar hasta que se tomen todos y solo luego repetirlos, así que aquí hay una implementación:

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.
maerics avatar Jul 26 '2013 21:07 maerics

Siempre que se seleccione un elemento, muévalo a la parte posterior de la matriz y selecciónelo aleatoriamente de una porción de la matriz original array.slice(0, -5).

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

var chooseName = function () {
    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);

EDITAR: Esto también tiene el efecto secundario de no darle a las variables que se encuentran al final de la lista la desventaja injusta de que no se considerarán en las primeras N llamadas. Si eso es un problema para usted, tal vez intente mantener una variable estática en algún lugar para realizar un seguimiento del tamaño del segmento a usar y maximizarlo en B (en este caso, 5). p.ej

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

var chooseName = function () {
    num = Math.floor(Math.random() * a.length - N);
    N = Math.min(N + 1, B);
    name = a.splice(num,1);
    a.push(name);
}
smakateer avatar Jul 26 '2013 21:07 smakateer