Noticias

¡RECUERDA QUE SI ERES UN NUEVO USUARIO, DEBES PRESENTARTE PARA PODER PUBLICAR MENSAJES! | TENEMOS CANAL OFICIAL DE TELEGRAM: t.me/unity3dspain

[Aportación] C# Números aleatorios sin repetirse

Iniciado por totemalf, Junio 07, 2015, 09:41:00 PM

Tema anterior - Siguiente tema
Junio 07, 2015, 09:41:00 PM Ultima modificación: Junio 07, 2015, 09:42:28 PM por kalua66
Buenas, 
he creado un script con C# que permite asignar valores a los indices de una lista, sin que se repitan. Estoy seguro que no será la manera más ortodoxa ni la de mejor rendimiento, pero si a alguien le sirve genial. Si alguien quiere aportar un modo que tenga un mejor rendimiento sería genial. Saludos!
 

int [] elemento = new int[10]; 
 
void Awake ()
{
 
for(int contador1 =0;contador1<elemento.Length;contador1++)
{
 
elemento[contador1]= Random.Range(0,10); 
int contador2 =0;
 
while(contador2<contador1) 
{
if(elemento[contador2]==elemento[contador1]) 
{
elemento[contador1]=Random.Range(0,10);
contador2 = 0;
continue;
}
contador2++;
}
}

puedes almacenar el ultimo numero en una variable temporal, y luego comparar si es igual a la nueva, si es así entonces que tome otro numero.

Buenas, 
francoe1, gracias por tu respuesta, creo que entiendo lo que dices más o menos; al almacenar el nuevo random en una variable temporal, compararía con dicha variable todas las posiciones de elemento con el contador 2 del while,¿es correcto?.
esta tarde probaré a ver que tal y subo el código retocado. 
 
Saludos

En cualquiera de los casos, ya no sería un "random" propiamente dicho, porque estaríamos mani***ndo a voluntad la condición de que no se repita un número. Lo estamos descartando manualmente... ¿Lo entendéis?
 
 
Lo que sí puede hacerse es "forzar/permitir" hasta un mínimo o máximo de probabilidades (en %) de que un número de la lista salga x veces seguidas. Me explico:
 
Ahora mismo, vosotros directamente lo estáis descartando, si vuelve a salir, ¿ok?  Yo lo que propongo es darle un nº máximo de oportunidades para permitir que se repita:
 
  1. Define un nº máximo de "vidas" u oportunidades para las que cualquier número pueda repetirse. Por ejemplo, max_repeats=3.

  2. Saca un número del Random. Ahora ese número ya tendrá "1 oportunidad menos" ó "num_repeated += 1".

  3. Saca otro número. Si sale el mismo, el número ahora tendrá "2 repeated". Si no, paso nº2.

  4. Saca otro número. Si vuelve a salir el mismo, num_repeated >= max_repeats,  y por tanto, ese nº queda totalmente descartado hasta dentro de 10 minutos; o 50 nuevas iteraciones; o lo que sea... Lo podemos meter incluso dentro de una blacklist, por ejemplo...

 
 
El sistema tiene desventajas, claro. Ocupamos espacio al requerir hasta 2-3 variables extra de control. Pero así nos aseguramos de que se produce el menor número posible de randoms por iteración y , en caso de ocurrir, dejamos que la Estadística se encargue y no nosotros mismos.
 
 
 
Es una idea. 

Buenas de nuevo, en respuesta a francoe1, no estoy seguro de si es a algo como lo que pongo a continuación a lo que te referías, y tampoco estoy del todo seguro de si es más eficiente, creo que sí. ¿Por cierto,como se puede saber en Unity si un código es más eficiente que otro? me refiero al consumo de recursos de la maquina

//Array de enteros. Quiero asignar un valor del 0 al 9 en cada indice sin que se repitan
// O lo que es lo mismo, distribuir los numeros del 0 al 9 entre los indices
int [] elemento = new int[10];
void Awake ()
{
   //Asigno un valor al primer elemento del array fuera del for
   elemento
  • = Random.Range(0,10);
   //El for guarda un numero aleatorio en la variable numeroAzar
   for(int contador1 = 1;contador1<elemento.Length;contador1++)
   {
      int numeroAzar = Random.Range(0,10);
      int contador2 = 0;
      //El while guarda el valor del numeroAzar en cada indide del Array
      //si no existe ya en un indice anterior
      while(contador2<contador1)
      {
         if (elemento[contador2]==numeroAzar)
         {
            numeroAzar= Random.Range(0,10);
            contador2 = 0;
            continue;
         }
         else
         {
               elemento [contador1]= numeroAzar;
         }
         contador2++;
      }
   }
}

 
En respuesta a pioj, tienes toda la razón , el nombre del tema no es el más apropiado, ya que lo que hacemos es distribuir unos valores numericos predeterminados en unos index, sin repetirlos, por tanto no es un random tal cual.
 
Yo en mi caso lo que necesitaba era crear aleatoriedad en las posiciones de unas cartas, para que cada vez que se ejecute el juego las cartas aparezcan en posiciones diferentes. Qué demonios, pongo el código completo, así veis a lo que me refiero:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Cartas : MonoBehaviour
{
public List <GameObject> listaObjetos = new List<GameObject> ();
public List <Vector2> posiciones = new List<Vector2> ();
//Array de enteros. Quiero asignar un valor del 0 al 9 en cada indice sin que se repitan
// O lo que es lo mismo, distribuir los numeros del 0 al 9 entre los indices
int [] elemento = new int[10];
void Awake ()
{
   //Asigno un valor al primer elemento del array fuera del for
   elemento
  • = Random.Range(0,10);
   //El for guarda un numero aleatorio en la variable numeroAzar
   for(int contador1 = 1;contador1<elemento.Length;contador1++)
   {
      int numeroAzar = Random.Range(0,10);
      int contador2 = 0;
      //El while guarda el valor del numeroAzar en cada indide del Array
      //si no existe ya en un indice anterior
      while(contador2<contador1)
      {
         if (elemento[contador2]==numeroAzar)
         {
            numeroAzar= Random.Range(0,10);
            contador2 = 0;
            continue;
         }
         else
         {
               elemento [contador1]= numeroAzar;
         }
         contador2++;
      }
   }

   foreach(int contar in elemento)
   {
      print (elemento[contar]);
   }
}
void Start ()
{
   listaObjetos [elemento[0]].transform.position = posiciones
  • ;
   listaObjetos [elemento[1]].transform.position = posiciones [1];
   listaObjetos [elemento[2]].transform.position = posiciones [2];
   listaObjetos [elemento[3]].transform.position = posiciones [3];
   listaObjetos [elemento[4]].transform.position = posiciones [4];
   listaObjetos [elemento[5]].transform.position = posiciones [5];
   listaObjetos [elemento[6]].transform.position = posiciones [6];
   listaObjetos [elemento[7]].transform.position = posiciones [7];
   listaObjetos [elemento[8]].transform.position = posiciones [8];
   listaObjetos [elemento[9]].transform.position = posiciones [9];
   print ("posiciones " + posiciones.Count);
   print ("lista de objetos " + listaObjetos.Count);
}
void Update ()
{
}
}

Comentaba esa idea, porque lo que os puede acabar pasando es que se genere un "mal Random". Es decir, que en principio os parezca que los números son aleatorios y no se repiten, pero luego os déis cuenta de que siempre se acaba repitiendo una tendencia parecida o patrón. Ejemplos:
 
  • Casualmente, los primeros números siempre acaban siendo menores que 5: 4,1,3...

  • hay seguidos demasiados números pares: 8,2,6...

  • Los últimos números parece que siempre vayan en orden descendiente: 9,5,2...

 
Estas cosas apenas se notan con pocos valores, así que tratad de hacer varios ejemplos con rangos de 30-50 números, a ver qué sucede.

Gracias pioj, 
esta tarde haré varias pruebas a ver si se cumplen patrones como los que comentas con un rango más alto de valores.
 
Un saludo

Buenas, 
creo que he encontrado un modo mejor de hacer esto, e imagino que será más eficiente, ya que no tengo la necesidad de hacer el for anidado de mis casos anteriores. ¿[EX3] serías tan amable de probar el rendimiento en tu com***dora para poder comparar con los métodos anteriores?

List<int> listaEnteros = new List<int> ();
List<int> listaEnteros2 = new List<int> ();
public int numeroElementos = 10;
int numeroAleatorio;
void Awake ()
{
   for (int contador = 0; contador < numeroElementos; contador++) {
      listaEnteros.Add (contador);
      print ("La posicion " + contador + " alberga el valor " + listaEnteros [contador]);
   }
}

void Start ()
{
   for (int contador2 = 0; contador2 <numeroElementos; contador2++)
   {
      numeroAleatorio = Random.Range (0, listaEnteros.Count);
      listaEnteros2.Add(listaEnteros[numeroAleatorio]);
      listaEnteros.RemoveAt (numeroAleatorio);
      print (listaEnteros2[contador2]);
   }
}

Me da la impresión de que es una forma de hacerlo más ingeniosa, espero no equivocarme y que me exploteis mi burbuja, ahora estoy de subidón. 

Saludos y gracias

Porque no pruebas con...

Enumerable.Range(0,100).OrderBy(x => random.Next()).Take(cant_num_a_generar);

Gracias GSG3D, esta tarde lo probaré a ver que tal. Luego os cuento!

Cita de: totemalf date=1433881036¿[EX3] serías tan amable de probar el rendimiento en tu com***dora para poder comparar con los métodos anteriores?
Creo que [EX3] no esta en este foro

Jejeje, se me coló en el copy-paste desde Stratos

De momento el código más eficiente va siendo este:

int[] elemento = new int[10];
void Awake ()
{
   int cont;
   int pos;
   int temp;
   //Rellena el array con valores ordenados
   for(cont = 0; cont< elemento.Length;cont++)
   {
      elemento [cont]= cont;
      print ("el elemento "+ cont + " tiene el valor "+elemento[cont]);
   }
   //Recorrer la lista de principio a fin intercambiando cada valor por uno al azar del resto de la lista
   for(cont =0;cont < elemento.Length;cont++)
   {
      pos = Random.Range(cont, elemento.Length);
      temp = elemento[pos];
      elemento[pos]=elemento[cont];
      elemento[cont]= temp;
      print ("el elemento "+ cont + " tiene el valor "+elemento[cont]);
   }
}

Voy a ver el de GSG3D a ver que tal.
Saludos!

Etiquetas: