Generador de números aleatorios en forma de distribución de probabilidad arbitraria

Dados n números, cada uno con alguna frecuencia de ocurrencia. Devuelve un número aleatorio con probabilidad proporcional a su frecuencia de aparición.

Ejemplo: 

Let following be the given numbers.
  arr[] = {10, 30, 20, 40}  

Let following be the frequencies of given numbers.
  freq[] = {1, 6, 2, 1}  

The output should be
  10 with probability 1/10
  30 with probability 6/10
  20 with probability 2/10
  40 with probability 1/10

Está bastante claro que el generador de números aleatorios simple no funcionará aquí, ya que no realiza un seguimiento de la frecuencia de ocurrencia. 

Necesitamos transformar de alguna manera el problema en un problema cuya solución conozcamos.

Un método simple es tomar una array auxiliar (por ejemplo, aux[]) y duplicar los números según su frecuencia de aparición. Genere un número aleatorio (digamos r) entre 0 y Sum-1 (incluidos ambos), donde Sum representa la suma de la array de frecuencias (freq[] en el ejemplo anterior). Devuelve el número aleatorio aux[r] (La implementación de este método se deja como ejercicio para los lectores).

La limitación del método anterior discutido anteriormente es el enorme consumo de memoria cuando la frecuencia de ocurrencia es alta. Si la entrada es 997, 8761 y 1, este método claramente no es eficiente.

¿Cómo podemos reducir el consumo de memoria? El siguiente es un algoritmo detallado que usa O(n) espacio adicional donde n es el número de elementos en las arrays de entrada.
1. Tome una array auxiliar (digamos prefijo []) de tamaño n. 
2. Rellénelo con el prefijo suma, de modo que el prefijo[i] represente la suma de números del 0 al i. 
3. Genere un número aleatorio (por ejemplo, r) entre 1 y Sum (incluidos ambos), donde Sum representa la suma de la array de frecuencia de entrada. 
4. Encuentre el índice de Ceil de número aleatorio generado en el paso 3 en la array de prefijos. Sea el índice el índice c
5. Devuelve el número aleatorio arr[indexc], donde arr[] contiene los n números de entrada.
  Antes de pasar a la parte de implementación, echemos un vistazo rápido al algoritmo con un ejemplo: 
      arr[]: {10, 20, 30} 
      freq[]: {2, 3, 1} 
      Prefix[]: {2, 5 , 6} 
  Dado que la última entrada en el prefijo es 6, todos los valores posibles de r son [1, 2, 3, 4, 5, 6] 
         1: el techo es 2. El número aleatorio generado es 10. 
         2: el techo es 2. Número aleatorio generado es 10. 
         3: el techo es 5. El número aleatorio generado es 20. 
         4: el techo es 5. El número aleatorio generado es 20. 
         5: el techo es 5. El número aleatorio generado es 20. 
         6. El techo es 6. El número aleatorio generado es 30. 
  En el ejemplo anterior 
      , 10 se genera con una probabilidad de 2/6. 
      20 se genera con probabilidad 3/6. 
      30 se genera con probabilidad 1/6.

¿Como funciona esto?  
Cualquier entrada de número[i] se genera tantas veces como su frecuencia de ocurrencia porque existe un conteo de enteros en el rango (prefijo[i – 1], prefijo[i]] es entrada[i]. Como en el ejemplo anterior, 3 es generado tres veces, ya que existen 3 enteros 3, 4 y 5 cuyo techo es 5. 

C++

// C++ program to generate random numbers
// according to given frequency distribution
#include <bits/stdc++.h>
using namespace std;
 
// Utility function to find ceiling of r in arr[l..h]
int findCeil(int arr[], int r, int l, int h)
{
    int mid;
    while (l < h)
    {
        mid = l + ((h - l) >> 1); // Same as mid = (l+h)/2
        (r > arr[mid]) ? (l = mid + 1) : (h = mid);
    }
    return (arr[l] >= r) ? l : -1;
}
 
// The main function that returns a random number
// from arr[] according to distribution array
// defined by freq[]. n is size of arrays.
int myRand(int arr[], int freq[], int n)
{
    // Create and fill prefix array
    int prefix[n], i;
    prefix[0] = freq[0];
    for (i = 1; i < n; ++i)
        prefix[i] = prefix[i - 1] + freq[i];
 
    // prefix[n-1] is sum of all frequencies.
    // Generate a random number with
    // value from 1 to this sum
    int r = (rand() % prefix[n - 1]) + 1;
 
    // Find index of ceiling of r in prefix array
    int indexc = findCeil(prefix, r, 0, n - 1);
    return arr[indexc];
}
 
// Driver code
int main()
{
    int arr[] = {1, 2, 3, 4};
    int freq[] = {10, 5, 20, 100};
    int i, n = sizeof(arr) / sizeof(arr[0]);
 
    // Use a different seed value for every run.
    srand(time(NULL));
 
    // Let us generate 10 random numbers according to
    // given distribution
    for (i = 0; i < 5; i++)
    cout << myRand(arr, freq, n) << endl;
 
    return 0;
}
 
// This is code is contributed by rathbhupendra

C

//C program to generate random numbers according to given frequency distribution
#include <stdio.h>
#include <stdlib.h>
 
// Utility function to find ceiling of r in arr[l..h]
int findCeil(int arr[], int r, int l, int h)
{
    int mid;
    while (l < h)
    {
         mid = l + ((h - l) >> 1);  // Same as mid = (l+h)/2
        (r > arr[mid]) ? (l = mid + 1) : (h = mid);
    }
    return (arr[l] >= r) ? l : -1;
}
 
// The main function that returns a random number from arr[] according to
// distribution array defined by freq[]. n is size of arrays.
int myRand(int arr[], int freq[], int n)
{
    // Create and fill prefix array
    int prefix[n], i;
    prefix[0] = freq[0];
    for (i = 1; i < n; ++i)
        prefix[i] = prefix[i - 1] + freq[i];
 
    // prefix[n-1] is sum of all frequencies. Generate a random number
    // with value from 1 to this sum
    int r = (rand() % prefix[n - 1]) + 1;
 
    // Find index of ceiling of r in prefix array
    int indexc = findCeil(prefix, r, 0, n - 1);
    return arr[indexc];
}
 
// Driver program to test above functions
int main()
{
    int arr[]  = {1, 2, 3, 4};
    int freq[] = {10, 5, 20, 100};
    int i, n = sizeof(arr) / sizeof(arr[0]);
 
    // Use a different seed value for every run.
    srand(time(NULL));
 
    // Let us generate 10 random numbers according to
    // given distribution
    for (i = 0; i < 5; i++)
      printf("%d\n", myRand(arr, freq, n));
 
    return 0;
}

Java

// Java program to generate random numbers
// according to given frequency distribution
class GFG
{
 
// Utility function to find ceiling of r in arr[l..h]
static int findCeil(int arr[], int r, int l, int h)
{
    int mid;
    while (l < h)
    {
        mid = l + ((h - l) >> 1); // Same as mid = (l+h)/2
        if(r > arr[mid])
            l = mid + 1;
        else
            h = mid;
    }
    return (arr[l] >= r) ? l : -1;
}
 
// The main function that returns a random number
// from arr[] according to distribution array
// defined by freq[]. n is size of arrays.
static int myRand(int arr[], int freq[], int n)
{
    // Create and fill prefix array
    int prefix[] = new int[n], i;
    prefix[0] = freq[0];
    for (i = 1; i < n; ++i)
        prefix[i] = prefix[i - 1] + freq[i];
 
    // prefix[n-1] is sum of all frequencies.
    // Generate a random number with
    // value from 1 to this sum
    int r = ((int)(Math.random()*(323567)) % prefix[n - 1]) + 1;
 
    // Find index of ceiling of r in prefix array
    int indexc = findCeil(prefix, r, 0, n - 1);
    return arr[indexc];
}
 
// Driver code
public static void main(String args[])
{
    int arr[] = {1, 2, 3, 4};
    int freq[] = {10, 5, 20, 100};
    int i, n = arr.length;
 
    // Let us generate 10 random numbers according to
    // given distribution
    for (i = 0; i < 5; i++)
    System.out.println( myRand(arr, freq, n) );
}
}
 
// This code is contributed by Arnab Kundu

Python3

# Python3 program to generate random numbers
# according to given frequency distribution
import random
 
# Utility function to find ceiling of r in arr[l..h]
def findCeil(arr, r, l, h) :
 
    while (l < h) :   
        mid = l + ((h - l) >> 1); # Same as mid = (l+h)/2
        if r > arr[mid] :
            l = mid + 1
        else :
            h = mid
     
    if arr[l] >= r :
        return l
    else :
        return -1
 
# The main function that returns a random number
# from arr[] according to distribution array
# defined by freq[]. n is size of arrays.
def myRand(arr, freq, n) :
 
    # Create and fill prefix array
    prefix = [0] * n
    prefix[0] = freq[0]
    for i in range(n) :
        prefix[i] = prefix[i - 1] + freq[i]
 
    # prefix[n-1] is sum of all frequencies.
    # Generate a random number with
    # value from 1 to this sum
    r = random.randint(0, prefix[n - 1]) + 1
 
    # Find index of ceiling of r in prefix array
    indexc = findCeil(prefix, r, 0, n - 1)
    return arr[indexc]
 
# Driver code
arr = [1, 2, 3, 4]
freq = [10, 5, 20, 100]
n = len(arr)
 
# Let us generate 10 random numbers according to
# given distribution
for i in range(5) :
    print(myRand(arr, freq, n))
 
    # This code is contributed by divyesh072019

C#

// C# program to generate random numbers
// according to given frequency distribution
using System;
 
class GFG{
     
// Utility function to find ceiling
// of r in arr[l..h] 
static int findCeil(int[] arr, int r,
                    int l, int h) 
{ 
    int mid;
    while (l < h) 
    { 
         
        // Same as mid = (l+h)/2 
        mid = l + ((h - l) >> 1);
         
        if (r > arr[mid]) 
            l = mid + 1;
        else
            h = mid; 
    } 
    return (arr[l] >= r) ? l : -1; 
}
 
// The main function that returns a random number
// from arr[] according to distribution array 
// defined by freq[]. n is size of arrays. 
static int myRand(int[] arr, int[] freq, int n) 
{ 
     
    // Create and fill prefix array 
    int[] prefix = new int[n];
    int i; 
    prefix[0] = freq[0]; 
     
    for(i = 1; i < n; ++i) 
        prefix[i] = prefix[i - 1] + freq[i]; 
   
    // prefix[n-1] is sum of all frequencies.
    // Generate a random number with 
    // value from 1 to this sum
    Random rand = new Random();
    int r = ((int)(rand.Next() * (323567)) %
                      prefix[n - 1]) + 1; 
   
    // Find index of ceiling of r in prefix array 
    int indexc = findCeil(prefix, r, 0, n - 1); 
    return arr[indexc]; 
}
 
// Driver Code
static void Main()
{
    int[] arr = { 1, 2, 3, 4 }; 
    int[] freq = { 10, 5, 20, 100 }; 
    int i, n = arr.Length; 
     
    // Let us generate 10 random numbers
    // according to given distribution 
    for(i = 0; i < 5; i++) 
        Console.WriteLine(myRand(arr, freq, n)); 
}
}
 
// This code is contributed by divyeshrabadiya07

Javascript

<script>
 
// JavaScript program to generate random numbers
// according to given frequency distribution
 
    // Utility function to find ceiling of r in arr[l..h]
    function findCeil(arr, r, l, h)
    {
        let mid;
        while (l < h)
        {
            mid = l + ((h - l) >> 1); // Same as mid = (l+h)/2
            (r > arr[mid]) ? (l = mid + 1) : (h = mid);
        }
        return (arr[l] >= r) ? l : -1;
    }
 
    // The main function that returns a random number
    // from arr[] according to distribution array
    // defined by freq[]. n is size of arrays.
    function myRand(arr, freq,  n) {
        // Create and fill prefix array
        let prefix= [];
        let i;
        prefix[0] = freq[0];
        for (i = 1; i < n; ++i)
            prefix[i] = prefix[i - 1] + freq[i];
 
        // prefix[n-1] is sum of all frequencies.
        // Generate a random number with
        // value from 1 to this sum
        let r = Math.floor((Math.random()* prefix[n - 1])) + 1;
 
        // Find index of ceiling of r in prefix array
        let indexc = findCeil(prefix, r, 0, n - 1);
        return arr[indexc];
    }
 
    // Driver code
    let arr = [1, 2, 3, 4];
    let freq = [10, 5, 20, 100];
    let i;
    let n = arr.length;
 
    // Use a different seed value for every run.
 
    // Let us generate 10 random numbers according to
    // given distribution
    for (i = 0; i < 5; i++)
      document.write(myRand(arr, freq, n));
       
      // This code is contributed by rohitsingh07052.
</script>

Salida: puede ser diferente para diferentes ejecuciones 

4
3
4
4
4

Complejidad de tiempo: O(n)

Este artículo ha sido compilado por Aashish Barnwal . 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 *