Archivo

Entradas Etiquetadas ‘rand’

Algoritmos: generar números aleatorios para la lotería

Lunes, 9 de Abril de 2012 Gaspar Fernández Sin comentarios

Es un ejemplo típico y nos muestra el uso de rand() con arrays para generar varios números aleatorios y no repetidos.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/**
*************************************************************
* @file loteria.c
* @brief Saca números aleatorios para la lotería
* Basado en el sorteo de la primitiva, hay que sacar 7 números
* del 1 al 49, sin repetir
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* http://totaki.com/poesiabinaria/algoritmos/
*************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include <time.h>       /* para time() */


int numero_aleatorio(int desde, int hasta)
{
  return desde+rand()%(hasta-desde+1);
}

short numero_repetido(int numeros[7], int n)
{
  int i=0;

  while (i<n)
    {
      /* Si un número sacado anteriormente es igual al número
         en la posición n, decidido, está repetido. */

      if (numeros[i]==numeros[n])
    return 1;
      i++;
    }

  /* Si llegamos hasta aquí, el número no se ha repetido */
  return 0;
}

void numeros_loteria(int numeros[7])
{
  int i;

  /* El primer número lo generamos, este no se repetirá con nadie
     anterior */

  numeros[0]=numero_aleatorio(1, 49);

  /* A partir del segundo número tenemos que verificar que no se
     repite. */

  for (i=1; i<7; i++)
    {
      do
    {
      /* Generamos un número */
      numeros[i]=numero_aleatorio(1,49);
      /* Si el número está repetido, volvemos a generar */
    } while (numero_repetido(numeros, i));
    }
}

int main(int argc, char *argv[])
{
  int numeros_premiados[7];
  int i;
  srand(time(NULL));        /* Una nueva semilla de números aleatorios */

  numeros_loteria(numeros_premiados);
  for (i=0; i<7; i++)
    printf ("Numero %d -> %d\n", i+1, numeros_premiados[i]);
 
  return EXIT_SUCCESS;
}

Descargar: loteria.c (1.6Kb)

En este ejemplo vamos guardando todos los números en un array y vamos comparando los nuevos números que vamos generando con los antiguos.

Pecados capitales: ORDER BY RAND() cuando sólo queremos una fila

Martes, 8 de Diciembre de 2009 Gaspar Fernández 1 comentario

Cometí el error cuando estrené las aplicaciones de frases en Facebook de utilizar la siguiente sentencia SQL para sacar una frase aleatoria:

1
SELECT * FROM `frases` ORDER BY RAND() LIMIT 1;

Al principio funcionaba bien, la verdad es que no confiaba mucho en el crecimiento de la aplicación, por lo que dejé el código así. Pero al cabo de unos días, noté que mi proveedor de hosting desactivó mi cuenta por excesivo uso de CPU: el rápido crecimiento de usuarios y frases dentro de la aplicación causaron un uso exponencial de CPU en mi servidor.
Al principio no parece lógico pero si nos fijamos en lo que estamos haciendo con esta línea:

  • Ordenamos de forma aleatoria la tabla frases (normalmente es más pesado que ordenar por fecha, o por un entero)
  • Escoger el primero de la lista obtenida (esto ya no cuesta nada)

Es decir, estamos ordenando la lista por completo (de unas 2000 frases) cada vez que un usuario quiere consultar sólo 1. Por lo que al final (imaginémonos unos 30000 usuarios a la hora), hacemos muchas ordenaciones innecesarias que aumentan el uso de CPU.

Una buena solución podría ser extraer el total de filas que tenemos en la tabla (es más, no tendremos que extraerlo siempre, sino cuando añadamos o borremos información), es una consulta bastante ligera; y luego extraer un sólo elemento empezando por la fila obtenida al azar.
En el siguiente ejemplo propongo un código en PHP que obtiene una fila aleatoria de una tabla (estas líneas las extraigo de un proyecto más grande, por lo que se podrían simplificar un poco más):

1
2
3
4
5
6
$sql="SELECT COUNT(id) FROM `frases`";
$res=mysql_fetch_row(mysql_query($sql));
// Tenemos en $res[0] el número de filas
$num=rand(0, $res-1); // Obtenemos un número aleatorio.
$sql="SELECT * FROM `frases` LIMIT ".$num.",1";
$frase_elegida=mysql_fetch_array(mysql_query($sql));

He dejado un poco de lado las comprobaciones y he dado por hecho que en la tabla frases hay algún contenido. Pero creo que queda bastante clara la idea.

Puede haber más soluciones, pero creo que con esta tenemos más control sobre los resultados (podemos introducir condiciones al número y más cosas).

Categories: MySQL, PHP Tags: , , , , ,

Visita otras webs de la red