Muestreo de yacimientos

El muestreo de reservorio es una familia de algoritmos aleatorios para elegir aleatoriamente k muestras de una lista de n elementos, donde n es un número muy grande o desconocido. Por lo general, n es lo suficientemente grande como para que la lista no quepa en la memoria principal. Por ejemplo, una lista de consultas de búsqueda en Google y Facebook.
Así que tenemos una gran array (o flujo) de números (para simplificar), y necesitamos escribir una función eficiente para seleccionar aleatoriamente k números donde 1 <= k <= n . Deje que la array de entrada sea stream[]. 

Una solución simple es crear un depósito de array [] de tamaño máximo k . Uno por uno, seleccione aleatoriamente un elemento de stream[0..n-1] . Si el elemento seleccionado no se seleccionó previamente, colóquelo en depósito[] . Para verificar si un elemento se seleccionó previamente o no, debemos buscar el elemento en el depósito [] . La complejidad temporal de este algoritmo será O(k^2) . Esto puede ser costoso si k es grande. Además, esto no es eficiente si la entrada tiene la forma de una secuencia. 

Se puede resolver en tiempo O(n) . La solución también se adapta bien a la entrada en forma de flujo. La idea es similar a este post. Los siguientes son los pasos. 1) Cree un depósito de array [0..k-1] y copie los primeros k elementos de stream[] en él. 2) Ahora, uno por uno, considere todos los elementos desde (k+1) hasta el elemento n . … a) Genere un número aleatorio de 0 a i donde i es el índice del elemento actual en stream[] . Deje que el número aleatorio generado sea j



b) Si j está en el rango de 0 a k-1 , reemplace el depósito [j] con la corriente [i]

A continuación se muestra la implementación del algoritmo anterior. 

C++

// An efficient program to randomly select
// k items from a stream of items
#include <bits/stdc++.h>
#include <time.h>
using namespace std;
 
// A utility function to print an array
void printArray(int stream[], int n)
{
    for (int i = 0; i < n; i++)
        cout << stream[i] << " ";
    cout << endl;
}
 
// A function to randomly select
// k items from stream[0..n-1].
void selectKItems(int stream[], int n, int k)
{
    int i; // index for elements in stream[]
 
    // reservoir[] is the output array. Initialize
    // it with first k elements from stream[]
    int reservoir[k];
    for (i = 0; i < k; i++)
        reservoir[i] = stream[i];
 
    // Use a different seed value so that we don't get
    // same result each time we run this program
    srand(time(NULL));
 
    // Iterate from the (k+1)th element to nth element
    for (; i < n; i++)
    {
        // Pick a random index from 0 to i.
        int j = rand() % (i + 1);
 
        // If the randomly picked index is smaller than k,
        // then replace the element present at the index
        // with new element from stream
        if (j < k)
        reservoir[j] = stream[i];
    }
 
    cout << "Following are k randomly selected items \n";
    printArray(reservoir, k);
}
 
// Driver Code
int main()
{
    int stream[] = {1, 2, 3, 4, 5, 6,
                    7, 8, 9, 10, 11, 12};
    int n = sizeof(stream)/sizeof(stream[0]);
    int k = 5;
    selectKItems(stream, n, k);
    return 0;
}
 
// This is code is contributed by rathbhupendra

C

// An efficient program to randomly select k items from a stream of items
 
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
// A utility function to print an array
void printArray(int stream[], int n)
{
    for (int i = 0; i < n; i++)
        printf("%d ", stream[i]);
    printf("\n");
}
 
// A function to randomly select k items from stream[0..n-1].
void selectKItems(int stream[], int n, int k)
{
    int i;  // index for elements in stream[]
 
    // reservoir[] is the output array. Initialize it with
    // first k elements from stream[]
    int reservoir[k];
    for (i = 0; i < k; i++)
        reservoir[i] = stream[i];
 
    // Use a different seed value so that we don't get
    // same result each time we run this program
    srand(time(NULL));
 
    // Iterate from the (k+1)th element to nth element
    for (; i < n; i++)
    {
        // Pick a random index from 0 to i.
        int j = rand() % (i+1);
 
        // If the randomly  picked index is smaller than k, then replace
        // the element present at the index with new element from stream
        if (j < k)
          reservoir[j] = stream[i];
    }
 
    printf("Following are k randomly selected items \n");
    printArray(reservoir, k);
}
 
// Driver program to test above function.
int main()
{
    int stream[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    int n = sizeof(stream)/sizeof(stream[0]);
    int k = 5;
    selectKItems(stream, n, k);
    return 0;
}

Java

// An efficient Java program to randomly
// select k items from a stream of items
import java.util.Arrays;
import java.util.Random;
public class ReservoirSampling {
   
    // A function to randomly select k items from
    // stream[0..n-1].
    static void selectKItems(int stream[], int n, int k)
    {
        int i; // index for elements in stream[]
 
        // reservoir[] is the output array. Initialize it
        // with first k elements from stream[]
        int reservoir[] = new int[k];
        for (i = 0; i < k; i++)
            reservoir[i] = stream[i];
 
        Random r = new Random();
 
        // Iterate from the (k+1)th element to nth element
        for (; i < n; i++) {
            // Pick a random index from 0 to i.
            int j = r.nextInt(i + 1);
 
            // If the randomly  picked index is smaller than
            // k, then replace the element present at the
            // index with new element from stream
            if (j < k)
                reservoir[j] = stream[i];
        }
 
        System.out.println(
            "Following are k randomly selected items");
        System.out.println(Arrays.toString(reservoir));
    }
 
    // Driver Program to test above method
    public static void main(String[] args)
    {
        int stream[]
            = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
        int n = stream.length;
        int k = 5;
        selectKItems(stream, n, k);
    }
}
// This code is contributed by Sumit Ghosh

Python3

# An efficient Python3 program
# to randomly select k items
# from a stream of items
import random
# A utility function
# to print an array
def printArray(stream,n):
    for i in range(n):
        print(stream[i],end=" ");
    print();
 
# A function to randomly select
# k items from stream[0..n-1].
def selectKItems(stream, n, k):
        i=0;
        # index for elements
        # in stream[]
         
        # reservoir[] is the output
        # array. Initialize it with
        # first k elements from stream[]
        reservoir = [0]*k;
        for i in range(k):
            reservoir[i] = stream[i];
         
        # Iterate from the (k+1)th
        # element to nth element
        while(i < n):
            # Pick a random index
            # from 0 to i.
            j = random.randrange(i+1);
             
            # If the randomly picked
            # index is smaller than k,
            # then replace the element
            # present at the index
            # with new element from stream
            if(j < k):
                reservoir[j] = stream[i];
            i+=1;
         
        print("Following are k randomly selected items");
        printArray(reservoir, k);
     
# Driver Code
 
if __name__ == "__main__":
    stream = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
    n = len(stream);
    k = 5;
    selectKItems(stream, n, k);
 
# This code is contributed by mits

C#

// An efficient C# program to randomly
// select k items from a stream of items
using System;
using System.Collections;
 
public class ReservoirSampling
{
    // A function to randomly select k
    // items from stream[0..n-1].
    static void selectKItems(int []stream,
                            int n, int k)
    {
        // index for elements in stream[]
        int i;
         
        // reservoir[] is the output array.
        // Initialize it with first k
        //  elements from stream[]
        int[] reservoir = new int[k];
        for (i = 0; i < k; i++)
            reservoir[i] = stream[i];
         
        Random r = new Random();
         
        // Iterate from the (k+1)th
        // element to nth element
        for (; i < n; i++)
        {
            // Pick a random index from 0 to i.
            int j = r.Next(i + 1);
             
            // If the randomly picked index
            // is smaller than k, then replace
            // the element present at the index
            // with new element from stream
            if(j < k)
                reservoir[j] = stream[i];        
        }
         
        Console.WriteLine("Following are k " +
                    "randomly selected items");
        for (i = 0; i < k; i++)
        Console.Write(reservoir[i]+" ");
    }
     
    //Driver code
    static void Main()
    {
        int []stream = {1, 2, 3, 4, 5, 6, 7,
                        8, 9, 10, 11, 12};
        int n = stream.Length;
        int k = 5;
        selectKItems(stream, n, k);
    }
}
 
// This code is contributed by mits

PHP

<?php
// An efficient PHP program
// to randomly select k items
// from a stream of items
 
// A utility function
// to print an array
function printArray($stream,$n)
{
    for ($i = 0; $i < $n; $i++)
        echo $stream[$i]." ";
    echo "\n";
}
 
// A function to randomly select
// k items from stream[0..n-1].
function selectKItems($stream, $n, $k)
    {
        $i; // index for elements
            // in stream[]
         
        // reservoir[] is the output
        // array. Initialize it with
        // first k elements from stream[]
        $reservoir = array_fill(0, $k, 0);
        for ($i = 0; $i < $k; $i++)
            $reservoir[$i] = $stream[$i];
         
        // Iterate from the (k+1)th
        // element to nth element
        for (; $i < $n; $i++)
        {
            // Pick a random index
            // from 0 to i.
            $j = rand(0,$i + 1);
             
            // If the randomly picked
            // index is smaller than k,
            // then replace the element
            // present at the index
            // with new element from stream
            if($j < $k)
                $reservoir[$j] = $stream[$i];    
        }
         
        echo "Following are k randomly ".
                      "selected items\n";
        printArray($reservoir, $k);
    }
     
// Driver Code
$stream = array(1, 2, 3, 4, 5, 6, 7,
                8, 9, 10, 11, 12);
$n = count($stream);
$k = 5;
selectKItems($stream, $n, $k);
 
// This code is contributed by mits
?>

Javascript

<script>
 
// An efficient program to randomly select
// k items from a stream of items
 
// A utility function to print an array
function printArray(stream, n)
{
    for(let i = 0; i < n; i++)
        document.write(stream[i] + " ");
         
    document.write('\n');
}
 
// A function to randomly select
// k items from stream[0..n-1].
function selectKItems(stream, n, k)
{
     
    // Index for elements in stream[]
    let i;
 
    // reservoir[] is the output array. Initialize
    // it with first k elements from stream[]
    let reservoir = [];
    for(i = 0; i < k; i++)
        reservoir[i] = stream[i];
 
    // Use a different seed value so that
    // we don't get same result each time
    // we run this program
 
    // Iterate from the (k+1)th element
    // to nth element
    for(; i < n; i++)
    {
        // Pick a random index from 0 to i.
        let j = (Math.floor(Math.random() *
                 100000000) % (i + 1));
                  
        // If the randomly picked index is
        // smaller than k, then replace the
        // element present at the index
        // with new element from stream
        if (j < k)
            reservoir[j] = stream[i];
    }
 
    document.write("Following are k randomly " +
                   "selected items \n");
    printArray(reservoir, k);
}
 
// Driver Code
let stream = [ 1, 2, 3, 4, 5, 6,
               7, 8, 9, 10, 11, 12 ];
let n = stream.length;
let k = 5;
 
selectKItems(stream, n, k);
 
// This code is contributed by rohan07
 
</script>

Producción: 

Following are k randomly selected items
6 2 11 8 12
Note: Output will differ every time as it selects and prints random elements

Complejidad de tiempo: O(n)

Espacio Auxiliar: O(k)

¿Como funciona esto?  
Para probar que esta solución funciona perfectamente, debemos probar que la probabilidad de que cualquier flujo de elementos[i] donde 0 <= i < n esté en el depósito final[] es k/n . Dividamos la prueba en dos casos ya que los primeros k elementos se tratan de manera diferente.

Caso 1: Para los últimos nk elementos de flujo, es decir, para flujo[i] donde k <= i <n Para cada uno de esos elementos de flujo flujo[i] , elegimos un índice aleatorio de 0 a i y si el índice seleccionado es uno de los primeros k índices, reemplazamos el elemento en el índice seleccionado con stream[i] Para simplificar la prueba, primero consideremos el último elemento . La probabilidad de que el último artículo esté en el reservorio final = La probabilidad de que uno de los primeros k índices sea seleccionado para el último artículo = k/n (la probabilidad de seleccionar uno de los k artículos de una lista de tamaño n ) 


Consideremos ahora el penúltimo elemento . La probabilidad de que el penúltimo elemento esté en el reservorio final[] = [Probabilidad de que uno de los primeros k índices se elija en la iteración para la corriente[n-2] ] X [Probabilidad de que el índice se seleccione en la iteración para la corriente[n-1 ] no es lo mismo que el índice seleccionado para el flujo [n-2] ] = [ k/(n-1)]*[(n-1)/n ] = k/n .
De manera similar, podemos considerar otros elementos para todos los elementos del flujo desde el flujo [n-1] hasta el flujo [k] y generalizar la prueba.

Caso 2: Para los primeros k elementos de flujo, es decir, para flujo[i] donde 0 <= i <k 
Los primeros k elementos se copian inicialmente en depósito[] y pueden eliminarse más tarde en iteraciones para flujo[k] a flujo[n ]
La probabilidad de que un elemento del flujo[0..k-1] esté en la array final = Probabilidad de que el elemento no se elija cuando los elementos fluyen[k], transmiten[k+1], …. stream[n-1] se consideran = [k/(k+1)] x [(k+1)/(k+2)] x [(k+2)/(k+3)] x … x [ (n-1)/n] = k/n

Referencias: 
http://en.wikipedia.org/wiki/Reservoir_sampling
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *