[C#] Random biaisé

Cette section est destinée aux scripts partagés par la communauté. Chaque post est destiné à un script. Suivez bien les recommandations.
Avatar de l’utilisateur
Alesk
Messages : 2303
Inscription : 13 Mars 2012 09:09
Localisation : Bordeaux - France
Contact :

[C#] Random biaisé

Message par Alesk » 31 Août 2013 12:21

Bonjour,

Voici un petit bout de code avec une fonction random agrémentée d'un paramètre pouvant altérer le résultat : la valeur bias, allant de -1f à 1f permet de définir si les nombres retournés auront plutôt tendance à être proches de la valeur minimale ou de la valeur maximale.
Si le bias est à 0, le random retourné est homogène, comme la fonction normale.

Pour visualiser ça, j'ai créé ce petit script qui affiche une texture où les pixels sont dessinés aléatoirement (j'ai laissé volontairement un pixel de marge, pour m'assurer que la fonction n'est jamais hors limites)
Le slider permet de faire varier le bias, le rendu est remis à zéro à chaque changement.
La case à cocher "Quick" permet de visualiser plus rapidement le résultat, en dessinant directement des pixels blancs, sinon le rendu par défaut accumule les valeurs RGB pour avoir un affichage plus progressif.

Pour tester, créez une nouvelle scène est assignez ce script à la caméra.

Code : Tout sélectionner

using UnityEngine;
using System.Collections;

public class RandomBiasTest : MonoBehaviour {
	
	public int textureSize = 256;
	
	Texture2D buffer;
	Color[] colorList;

	float bias = 0.0f;
	float prev_bias = 0f;
	bool quick_display = false;

	void Start () {
		
		colorList = new Color[textureSize*textureSize];
		buffer = new Texture2D(textureSize,textureSize);
		
		int i,j,k = 0;
		for(i=0;i<textureSize;i++){
			for(j=0;j<textureSize;j++){
				colorList[k] = Color.black;
				k++;
			}
		}
		
		buffer.SetPixels(colorList);

	}

	void Update () {
		int x,y,i,j,k = 0;
		Color col;
		
		if(bias != prev_bias){
			
			for(i=0;i<textureSize;i++){
				for(j=0;j<textureSize;j++){
					colorList[k] = Color.black;
					k++;
				}
			}
			prev_bias = bias;
		}
		
		for(i = 0; i < 10000; i++){
			x = RandomBias(1,textureSize-2,bias);
			y = RandomBias(1,textureSize-2,0);
			
			if(quick_display){
				col = Color.white;
			}else{
				col = colorList[(y*textureSize)+x];
				col.a = 1;
				if(col.r < 1){
					col.r += 0.1f;
				}else if(col.g < 1f){
					col.g += 0.1f;
				}else if(col.b < 1f){
					col.b += 0.1f;
				}
			}
			colorList[(y*textureSize)+x] = col;
		}
		
		buffer.SetPixels(colorList);
		
		buffer.Apply();
	}
	
	void OnGUI(){
		GUI.DrawTexture(new Rect(5,45,textureSize,textureSize),buffer);
		bias = GUI.HorizontalSlider(new Rect(5, 5, textureSize, 15), bias, -1f, 1f);
		quick_display = GUI.Toggle(new Rect(5,20,50,15),quick_display,"Quick");
		
	}
	
	int RandomBias(int min , int max , float bias = 0f){
		return (int)Mathf.Max (min , Mathf.Min (max , RandomBias((float)min , (float)max , bias) ));
	}
	
	float RandomBias(float min , float max , float bias = 0f){
		float maxval = 1000f;
		float maxlog = Mathf.Log(maxval);
		
		bias = Mathf.Max(-1 , Mathf.Min (1 , bias));
		
		float r = Random.Range (1, maxval);
		float lval = 0f;
		if (bias > 0){
			lval = Mathf.Log( r ) / maxlog * maxval;
			r = lval * bias + r * (1 - bias);
		}
		if (bias < 0){
			lval = Mathf.Log( r ) / maxlog * maxval;
			r = maxval - 1 + lval * bias - r * (1 + bias);
		}
		r = Mathf.Max (min , min + Mathf.Min (max , (max + 1 - min) * r / maxval));
		
		return r;
	}
}
Si vous avez de meilleures idées (ou plus optimisées) pour obtenir le même résultat, ça m'intéresse :mrgreen:

Avatar de l’utilisateur
artemisart
Messages : 1893
Inscription : 21 Juin 2011 19:51
Localisation : Centre
Contact :

Re: [C#] Random biaisé

Message par artemisart » 31 Août 2013 22:41

sympa !

J'ai juste remplacé les max/min tout moches :mrgreen: par des clamps et j'ai essayé une autre technique pour le random mais j'ai bien foiré ^^ (sinon juste un truc : int min est exclusif dans RandomBias, normal ?).

Code : Tout sélectionner

using UnityEngine;
using System.Collections;

public class RandomBiasTest : MonoBehaviour
{
	public int textureSize = 256;
	public float bias = 0;
	public float colorIncrement = 0.1f;

	private Texture2D buffer;
	private Color[] colorList;

	private float prev_bias = 0f;
	private bool quick_display = false;

	private void Start ()
	{
		colorList = new Color[textureSize * textureSize];
		buffer = new Texture2D (textureSize, textureSize);

		ResetColors ();
		buffer.SetPixels (colorList);
	}

	private void Update ()
	{
		Color col = Color.white;

		if (bias != prev_bias)
			ResetColors ();

		int x, y;
		for (int i = 0; i < 10000; i++)
		{
			y = RandomBias (1, textureSize - 2, 0);
			x = y > textureSize / 2 ?
				RandomBias (1, textureSize - 2, bias) :
				RandomExponentBias (1, textureSize - 2, bias);

			if (!quick_display)
			{
				col = colorList[(y * textureSize) + x];
				if (col.r < 1)
					col.r += colorIncrement;
				else if (col.g < 1f)
					col.g += colorIncrement;
				else if (col.b < 1f)
					col.b += colorIncrement;
			}
			colorList[(y * textureSize) + x] = col;
		}

		buffer.SetPixels (colorList);
		buffer.Apply ();
	}

	private void OnGUI ()
	{
		GUI.DrawTexture (new Rect (5, 45, textureSize, textureSize), buffer);
		bias = GUI.HorizontalSlider (new Rect (5, 5, textureSize, 15), bias, -1f, 1f);
		quick_display = GUI.Toggle (new Rect (5, 20, 50, 15), quick_display, "Quick");
	}

	private void ResetColors ()
	{
		for (int i = 0; i < textureSize * textureSize; i++)
			colorList[i] = Color.black;
		prev_bias = bias;
	}

	private int RandomBias (int min, int max, float bias = 0f)
	{
		return Mathf.Clamp ((int)(RandomBias ((float)min, (float)max, bias) + 0.5), min, max);
	}

	private float RandomBias (float min, float max, float bias = 0f)
	{
		float maxValue = 1000f;
		float maxLog = Mathf.Log (maxValue);

		bias = Mathf.Clamp (bias, -1f, 1f);

		float r = Random.Range (1, maxValue);
		float lval = Mathf.Log (r) / maxLog * maxValue;

		if (bias < 0)
			r = maxValue - 1 + lval * bias - r * (1 + bias);
		else if (bias > 0)
			r = lval * bias + r * (1 - bias);

		r = min + Mathf.Clamp ((max + 1 - min) * r / maxValue, min, max);

		return r;
	}

	private int RandomExponentBias (int min, int max, float bias = 0f)
	{
		return Mathf.Clamp ((int)(RandomExponentBias ((float)min, (float)max, bias) + 0.5f), min, max);
	}

	private float RandomExponentBias (float min, float max, float bias = 0f)
	{
		float value = Mathf.Pow (Random.value, 1 - Mathf.Abs (bias));
		if (bias < 0)
			value = 1 - value;
		return Mathf.Lerp (min, max, value);
	}
}

Avatar de l’utilisateur
Alesk
Messages : 2303
Inscription : 13 Mars 2012 09:09
Localisation : Bordeaux - France
Contact :

Re: [C#] Random biaisé

Message par Alesk » 01 Sep 2013 16:59

artemisart a écrit :sympa !J'ai juste remplacé les max/min tout moches :mrgreen: par des clamps
Merci, j'oublie toujours d'utiliser clamp :p
artemisart a écrit :et j'ai essayé une autre technique pour le random mais j'ai bien foiré ^^.
Ah vi, bonne piste, dommage que ça ne sorte pas tous les chiffres au final :/
artemisart a écrit : (sinon juste un truc : int min est exclusif dans RandomBias, normal ?)
Oué j'avais bidouillé dans le calcul, d'ailleurs là y'a une erreur dans ta modif ça ne sort plus la valeur minimale,
voici une version rectifiée :

Code : Tout sélectionner

    using UnityEngine;
    using System.Collections;

    public class RandomBiasTest : MonoBehaviour
    {
       public int textureSize = 256;
       public float bias = 0;
       public float colorIncrement = 0.1f;

       private Texture2D buffer;
       private Color[] colorList;

       private float prev_bias = 0f;
       private bool quick_display = false;

       private void Start ()
       {
          colorList = new Color[textureSize * textureSize];
          buffer = new Texture2D (textureSize, textureSize);

          ResetColors ();
          buffer.SetPixels (colorList);
       }

       private void Update ()
       {
          Color col = Color.white;

          if (bias != prev_bias)
             ResetColors ();

          int x, y;
          for (int i = 0; i < 10000; i++)
          {
             y = RandomBias (1, textureSize - 2, 0);
             x = y > textureSize / 2 ?
                RandomBias (1, textureSize - 2, bias) :
                RandomExponentBias (1, textureSize - 2, bias);

             if (!quick_display)
             {
                col = colorList[(y * textureSize) + x];
                if (col.r < 1)
                   col.r += colorIncrement;
                else if (col.g < 1f)
                   col.g += colorIncrement;
                else if (col.b < 1f)
                   col.b += colorIncrement;
             }
             colorList[(y * textureSize) + x] = col;
          }

          buffer.SetPixels (colorList);
          buffer.Apply ();
       }

       private void OnGUI ()
       {
          GUI.DrawTexture (new Rect (5, 45, textureSize, textureSize), buffer);
          bias = GUI.HorizontalSlider (new Rect (5, 5, textureSize, 15), bias, -1f, 1f);
          quick_display = GUI.Toggle (new Rect (5, 20, 50, 15), quick_display, "Quick");
       }

       private void ResetColors ()
       {
          for (int i = 0; i < textureSize * textureSize; i++)
             colorList[i] = Color.black;
          prev_bias = bias;
       }

       private int RandomBias (int min, int max, float bias = 0f)
       {
          return (int)Mathf.Clamp (RandomBias ((float)min, (float)max, bias), min, max);
       }

       private float RandomBias (float min, float max, float bias = 0f)
       {
          float maxValue = 1000f;
          float maxLog = Mathf.Log (maxValue);

          bias = Mathf.Clamp (bias, -1f, 1f);

          float r = Random.Range (1, maxValue);
          float lval = Mathf.Log (r) / maxLog * maxValue;

          if (bias < 0)
             r = maxValue - 1 + lval * bias - r * (1 + bias);
          else if (bias > 0)
             r = lval * bias + r * (1 - bias);

          r = Mathf.Clamp (min + (max + 1 - min) * r / maxValue, min, max);

          return r;
       }

       private int RandomExponentBias (int min, int max, float bias = 0f)
       {
          return Mathf.Clamp ((int)(RandomExponentBias ((float)min, (float)max, bias) + 0.5f), min, max);
       }

       private float RandomExponentBias (float min, float max, float bias = 0f)
       {
          float value = Mathf.Pow (Random.value, 1 - Mathf.Abs (bias));
          if (bias < 0)
             value = 1 - value;
          return Mathf.Lerp (min, max, value);
       }
    }

Avatar de l’utilisateur
artemisart
Messages : 1893
Inscription : 21 Juin 2011 19:51
Localisation : Centre
Contact :

Re: [C#] Random biaisé

Message par artemisart » 01 Sep 2013 17:11

Alesk a écrit :Oué j'avais bidouillé dans le calcul, d'ailleurs là y'a une erreur dans ta modif ça ne sort plus la valeur minimale,
voici une version rectifiée :
Arf désolé :oops: j'ai pas du tout comprendre à ça :

Code : Tout sélectionner

r = Mathf.Max (min , min + Mathf.Min (max , (max + 1 - min) * r / maxval));
du coup mon clamp était mauvais (en même temps on peut pas dire que c'était très clair ^^).

Répondre

Revenir vers « Scripts »