# Random.Value with Percentage is Bugged

I googles this a lot and it seems I have to be doing something wrong as I am doing what everyone else says.

I want to spawn something with only 0.05% chance. However, I just played the game it it spawned twice in a row. Unless I am crazy, that seems insane and something isn’t working.

`````` if(Random.value < 0.05f)
``````

This should work, correct?

Well, first of all `if(Random.value < 0.05f)` is not `0.05%` but `5%`. Second it’s supposed to be random, so why do you think its crazy if it happens two times in a row? It seems you fall for the gambler’s fallacy?

1 Like

Have you tried testing it more that twice to make sure it wasn’t just a fluke?

How about Debug.Log-ing the value output by it (cache it first) to see what you’re getting.

And yeah, as noted, your maths is off.

You don’t use unity engines random.range?

If (Random.Range(0.0f,100.0f) < 0.05f)
// Unity Engine Results 0.05%

you can int it aswell for the whole chances. Say 1% 5% if the chance is between 1,100 if it’s less than your chance then from my experience operates just fine

A numberphile video mentioned in the forums. Love it

@TomTheMan59 : Maybe what you want is more of pseudo random system. Imagine like a bowl full of marbles, 95 black ones and 5 white ones. Now you draw from them at random until it is empty and then you refill and restart picking. It still would not prevent you from getting two in a row but you would be guaranteed that this can only happen up to 5 times for the white marbles within once cycle (10 times globally).

Here is a class I use for such instances. It is serializable so you can keep the “bowl of marbles” across multiple game sessions (beware of cheaters ;-).

Usage:

``````var myPool = new PseudoRandomPool(new int[] { 9, 1 });
// GetNext() will return 1 in one out of ten cases (10%).
// In the back it uses an array looking like this: { 1,0,0,0,0,0,0,0,0,0 }.
// It shuffles it once. If a value is picked it will be removed. Refilled if empty.
var result = myPool.GetNext();

// 5% chance would be:
var myPool = new PseudoRandomPool(new int[] { 95, 5 });
var result = myPool.GetNext();

// 0.05% chance would be:
var myPool = new PseudoRandomPool(new int[] { 9995, 5 });
var result = myPool.GetNext();
``````

Code:

``````using UnityEngine;
using System;
using System.Linq;

namespace Kamgam.Helpers
{
/// <summary>
/// Serializable pool of numbers.

/// NOTICE: The serialized data may get very BIG for big ratios as for each possibility one Integer is stored.

/// TODO: (n2h) change serialization from serializing the whole list into custom compressed serialization (RLE or similar).

///

/// Usage:

///  We want a pool of seven 10s and one 3. Equals to 87.5% chance to get a 10 and 12.5% to get a 3.

///  var pool = new PseudoRandomPool(new int[] { 7, 1 }, new int[] { 10, 3 });

///
///  If no values are given then the index of the ratios is used (starting with 0). E.G.: 95% chance for a 0 and 5% chance for a 1.
///  var pool = new PseudoRandomPool(new int[] { 95, 5 });

/// </summary>
[System.Serializable]
public class PseudoRandomPool
{
[SerializeField]
protected int[] pool;

[System.NonSerialized]
protected int[] poolRatios;

[System.NonSerialized]
protected int[] poolValues;

public PseudoRandomPool()
{
}

public PseudoRandomPool( int[] poolRatios, int[] poolOptions = null )
{
SetRatiosAndValues(poolRatios, poolOptions);
}

/// <summary>
/// Sets the used ratios and options.

/// NOTICE: The serialized data may get very BIG for big ratios as for each possibility one Integer is stored.

/// Example:

///  We want a pool of seven 10s and one 3. Equals to 87.5% chance to get a 10 and 12.5% to get a 3.

///  pool.SetRatiosAndValues(new int[] { 7, 1 }, new int[] { 10, 3 });

///

///  If no values are given then the index of the ratios is used (starting with 0). E.G.: 95% chance for a 0 and 5% chance for a 1.

///  pool.SetRatiosAndValues(new int[] { 95, 5 });

/// </summary>
/// <param name="poolRatios"></param>
/// <param name="poolValues">The values are matched to the ratios by index (order in array).
If no values are provided then the index of the ratio will be returned as value, e.g.: 0, 1,2,3 ... poolRatios.Length - 1.</param>
public void SetRatiosAndValues(int[] poolRatios, int[] poolValues = null)
{
this.poolRatios = poolRatios;
this.poolValues = poolValues;

RefillPoolIfNecessary();
}

public int GetPoolSize()
{
if(this.pool != null)
{
return this.pool.Length;
}

return 0;
}

/// <summary>
/// Refills the pool only if necessary.
/// </summary>
public void RefillPoolIfNecessary()
{
if (pool == null || pool.Length == 0)
{
RefillPool();
}
}

/// <summary>
/// Dumps the old pool data and fills it anew.
/// </summary>
public void RefillPool()
{
if (poolRatios != null)
{
pool = new int[poolRatios.Sum()];

int index = 0;
for (int i = 0; i < poolRatios.Length; ++i)
{
for (int r = 0; r < poolRatios[i]; ++r)
{
if (poolValues != null)
{
pool[index] = poolValues[i];
}
else
{
pool[index] = i;
}
index++;
}
}

Shuffle();
}
else
{
#if UNITY_EDITOR
if (UnityEditor.EditorApplication.isPlaying)
{
#endif
Debug.LogWarning("Called refresh on a pool without ratios. Will do nothing!");
#if UNITY_EDITOR
}
#endif
}
}

public void Shuffle()
{
int n = pool.Length;
while (n > 1)
{
int k = UnityEngine.Random.Range(0, n);
n--;
var value = pool[k];
pool[k] = pool[n];
pool[n] = value;
}
}

/// <summary>
/// Returns the next value in the pool. Refills the pool if necessary.
/// </summary>
/// <returns></returns>
public int GetNext()
{
return GetNext(1, int.MinValue, null);
}

/// <summary>
/// Checks if the next values is one of the allowed values.
/// If it is not then it shuffles the pool and tries "attempts" times.
/// If it still does not find one randomly then it will search
/// for it and return the first valid value (if one exists).
///
/// If no valid value could be retrieved then failValue is returned.
/// </summary>
/// <param name="attempts"></param>
/// <param name="allowedValues">If it is null or empty then it assumes that all values are allowed.</param>
/// <returns></returns>
public int GetNext(int attempts, int failValue, params int[] allowedValues)
{
RefillPoolIfNecessary();

while (attempts > 0)
{
attempts--;
if (pool != null && pool.Length > 0)
{
int nextValue = pool[pool.Length - 1];
if(allowedValues == null || allowedValues.Length == 0 || allowedValues.Contains(nextValue))
{
Array.Resize(ref pool, pool.Length - 1);
return nextValue;
}
else
{
if(attempts == 1)
{
// skrew randomness, let's search
for (int i = 0; i < pool.Length; i++)
{
if (allowedValues == null || allowedValues.Length == 0 || allowedValues.Contains(pool[i]))
{
nextValue = pool[i];
int[] newPool = new int[pool.Length - 1];
Array.Copy(pool, 0, newPool, 0, i);
if (pool.Length - i - 1 > 0)
{
Array.Copy(pool, i + 1, newPool, i, pool.Length - i - 1);
}
pool = newPool;
return nextValue;
}
}
}
else
{
Shuffle();
}
}
}
}

Debug.LogWarning("PseudoRandomPool.GetNext() could not find a fitting value.");
return failValue;
}

/// <summary>
/// A very bad "checksum" immplementation.
/// </summary>
/// <returns></returns>
public int Sum()
{
if (pool == null)
return 0;
return pool.Sum();
}

/// <summary>
/// A helper to convert enums to integer values. Use this to fill the values array.

/// Example:

///  PseudoRandomPool.EnumToInts<YourEnumType>();
/// </summary>
/// <param name="type"></param>
/// <returns>An array of integers (the enum values).</returns>
public static int[] EnumToInts<T>() where T : Enum
{
var type = typeof(T);
var array = Enum.GetValues(type);
int[] enumValues = new int[array.Length];
for (int i = 0; i < array.Length; i++)
{
enumValues[i] = (int) array.GetValue(i);
}
return enumValues;
}
}
}
``````

I find that quite often pure random numbers are the opposite of what I really want.

1 Like

You could always do a random runner

RandomChance as int
+= 1 per frame

if application frame rate > 100 fps and is consistent you can iterate your random chance in the same way as Doom or Duke Nukem 3D.

Of course it wouldn’t be random if it resulted every frame. But if it was a function called when an AI does something for example( fires a projectile. Then it can be random enough for purpose

in case you want half of 1% just do it with floats += 0.1f

Wow! Thanks everyone for posting great insights, videos, and code! I really appreciate all the responses and am able to see what is wrong.

@Bunny83 Thanks for the video! I just kind of thought, how is it possible something spawned twice in a row with such a low percentage.

Also yes, it is 5% not 0.05%.

@_geo1 @AnimalMan Thanks for the code and different methods!

Random.value is a shortcut for Random.Range(0.0f, 1.0f);. It’s commonly used for percentages: Random.value<0.25f for 25%, and so on. You can even write it as 0.30f (for 30%) to make it easier to read.

Once you get used to it, expressing percents in a 0-1 range is more natural. If you have an 80% chance of getting 6 gold, that’s an average of 0.86 gold. Or 83% bonus crit damage means the crit multiplier is (1+0.83). If fog reduces range to 75% the real range is maxRange0.75f. So in your game you’ve got float critBonus=1.83f, rangePctChange=0.75f;. Recognizing 0.05% is 0.0005f even feels natural after using this style for a bit – you automatically see the first 2 places as the 0-100 percent and write in “0.00” to start.

2 Likes

Right, when doing statistics and probability this is usually the norm anyways ^^.

I’ve already linked quite a few over the years. I think the one I linked the most is the Quaternion video (by Dr. James Grime) I think it’s one of the most concise. It leaves out quite a bit of details but just enough to go from zero up to a point where you can at least start to understand how they work internally and how the maths behind them work on a numeric level.

Anyways we went a bit off topic ^^. I think it’s important to note that computers can not really generate true random numbers. Some modern PCs may have a piece of hardware that is able to generate true random numbers (or at least unpredictable enough for cryptography), though for the most part computers can only generate sequences of numbers which appear random but actually are not. Those are called pseudo random numbers. Unity uses an implementation of the xorshift128 generator which is a quite fast and has a decent “quality” and a long period.

1 Like

Ah, I think I remember that one. This is where I learned about that famous stone bride . Didn’t 3b1b also have a nice quaternion video series. … Aaand I am off to Youtube (again), LFSR for random number generators on Computerphile.

1 Like