Elija eficientemente n elementos aleatorios de la matriz PHP(sin barajar)
arrays performance (5)
El truco consiste en utilizar una variación de shuffle o, en otras palabras, un shuffle parcial.
el rendimiento
no es el único criterio, la eficiencia estadística, es decir, el
muestreo imparcial
es tan importante (como lo es la solución
shuffle
original)
function random_pick( $a, $n )
{
$N = count($a);
$n = min($n, $N);
$picked = array_fill(0, $n, 0); $backup = array_fill(0, $n, 0);
// partially shuffle the array, and generate unbiased selection simultaneously
// this is a variation on fisher-yates-knuth shuffle
for ($i=0; $i<$n; $i++) // O(n) times
{
$selected = mt_rand( 0, --$N ); // unbiased sampling N * N-1 * N-2 * .. * N-n+1
$value = $a[ $selected ];
$a[ $selected ] = $a[ $N ];
$a[ $N ] = $value;
$backup[ $i ] = $selected;
$picked[ $i ] = $value;
}
// restore partially shuffled input array from backup
// optional step, if needed it can be ignored, e.g $a is passed by value, hence copied
for ($i=$n-1; $i>=0; $i--) // O(n) times
{
$selected = $backup[ $i ];
$value = $a[ $N ];
$a[ $N ] = $a[ $selected ];
$a[ $selected ] = $value;
$N++;
}
return $picked;
}
NOTA:
el algoritmo es estrictamente
O(n)
tanto
en
tiempo como en espacio
, produce
selecciones
no
sesgadas
(es una
array_values
parcial imparcial
) y produce una
salida que es una matriz adecuada con claves consecutivas
(sin necesidad de
array_values
adicionales de
array_values
etc.)
Use ejemplo:
$randomly_picked = random_pick($my_array, 5);
// or if an associative array is used
$randomly_picked_keys = random_pick(array_keys($my_array), 5);
$randomly_picked = array_intersect_key($my_array, array_flip($randomly_picked_keys));
Para más variaciones y extensiones de barajar para PHP:
Tengo el siguiente código para elegir elementos
$n
de una matriz
$array
en PHP:
shuffle($array);
$result = array_splice($array, 0, $n);
Dada una gran matriz, pero solo unos pocos elementos (por ejemplo,
5
cada
10000
), esto es relativamente lento, por lo que me gustaría optimizarlo de modo que no se tengan que barajar todos los elementos.
Los valores deben ser únicos.
Estoy buscando la alternativa más eficiente.
Podemos suponer que
$array
no tiene duplicados y tiene un índice
0
.
Esta función realiza una combinación aleatoria de solo
$n
elementos donde
$n
es el número de elementos aleatorios que desea elegir.
También funcionará en matrices asociativas y matrices dispersas.
$array
es la matriz para trabajar y
$n
es el número de elementos aleatorios para recuperar.
Si definimos
$max_index
como
count($array) - 1 - $iteration
.
Funciona generando un número aleatorio entre 0 y
$max_index
.
Elegir la clave en ese índice y reemplazar su índice con el valor en
$max_index
para que nunca se pueda volver a
$max_index
, ya que
$max_index
será uno menos en la próxima iteración e inalcanzable.
En resumen,
este es el
shuffle de Fisher-Yates de Richard Durstenfeld,
pero opera solo con elementos
$n
lugar de toda la matriz.
function rand_pluck($array, $n) {
$array_keys = array_keys($array);
$array_length = count($array_keys);
$max_index = $array_length -1;
$iterations = min($n, $array_length);
$random_array = array();
while($iterations--) {
$index = mt_rand(0, $max_index);
$value = $array_keys[$index];
$array_keys[$index] = $array_keys[$max_index];
array_push($random_array, $array[$value]);
$max_index--;
}
return $random_array;
}
Esto solo mostrará beneficios para
n
pequeña en comparación con una matriz aleatoria, pero podría
-
Elija un índice aleatorio
r
n
veces, cada vez disminuyendo el límite en1
- Ajustar para índices usados ​​previamente
- Tomar valor
- Almacenar índice usado
Pseudocódigo
arr = []
used = []
for i = 0..n-1:
r = rand 0..len-i
d = 0
for j = 0..used.length-1:
if r >= used[j]:
d += 1
arr.append($array[r + d])
used.append(r)
return arr
Puede generar n veces un número aleatorio con
mt_rand()
y luego llenar estos valores en una nueva matriz.
Para ir en contra del caso en el que se devuelve el mismo índice dos veces, usamos el índice devuelto real para llenar la nueva matriz y verificamos siempre si el índice existe en la nueva matriz, si es así, usamos while para recorrerlo siempre que obtengamos un índice duplicado
Al final usamos
array_values()
para obtener una matriz indexada en 0.
$count = count($array) - 1;
$new_array = array();
for($i = 0; $i < $n; $i++) {
$index = mt_rand(0, $count);
while(isset($new_array[$index])) {
$index = mt_rand(0, $count);
}
$new_array[$index] = $array[$index];
}
$new_array = array_values($new_array);
$randomArray = [];
while (count($randomArray) < 5) {
$randomKey = mt_rand(0, count($array)-1);
$randomArray[$randomKey] = $array[$randomKey];
}
Esto proporcionará exactamente 5 elementos sin duplicados y muy rápidamente. Las llaves serán preservadas.
Nota: Tendrás que asegurarte de que $ array tenga 5 o más elementos o agregar algún tipo de verificación para evitar un bucle sin fin.