Generate a x random number: x ∈ [1,100].
- If x ∈ [1,30] - caught a blue ball
- if x ∈ [31,40] - caught a red ball
- get x ∈ [41,45] - got a gold ball
- get x ∈ [46,75] - got a black ball
- if x ∈ [76,100] - has taken a gray ball
C # implementation example.
The class BallsBag simulates the drawing of colored balls from a bag.
It simulates two behaviors depending on whether the removed ball comes back to the bag or not.
For each set of balls inserted in the bag ( InsertBallsByColor()
) a BallSet
is created that stores the color and an interval ( intervalLeft, intervalRight
).
This interval is calculated as a function of the number of balls already placed and the number of balls in that set.
private int IntervalLeft() => _ballsInBag + 1;
private int IntervalRight(int count) => _ballsInBag + count;
It is used to know if, due to the random value generated, a ball of this color was taken.
public bool WasPicked(int pick)
{
return pick >= _intervalLeft && pick <= _intervalRight;
}
The probability of leaving a ball of a certain color is the ratio of the number of balls of that color in the bag to the total amount of balls in the bag.
public class BallsBag
{
private static readonly int NUM_MAX_TIRAGENS = 5000;
private readonly bool _extractedBallReturnsToBag;
private int _ballsIn;
private readonly IEnumerator<int>_randomGenerator ;
private readonly HashSet<BallSet> _ballSetsInBag;
private int _ballsExtracted;
public BallsBag(int totalBalls, bool extractedBallReturnsToBag)
{
_extractedBallReturnsToBag = extractedBallReturnsToBag;
TotalBalls = totalBalls;
_ballSetsInBag = new HashSet<BallSet>();
_randomGenerator = getRandomGenerator(TotalBalls, extractedBallReturnsToBag);
}
public int TotalBalls { get; }
public int BallsInBag => _ballsIn - _ballsExtracted;
public void InsertBallsByColor(string color, int count)
{
if (count <= 0)
{
throw new ArgumentOutOfRangeException(nameof(count), $"{count} não é uma quantidade válida de bolas");
}
if (BallsExceedsMaximum(count))
{
throw new ArgumentOutOfRangeException(nameof(count), "Nº total de bolas excedido");
}
if(BagHaveThisBallColor(color))
{
throw new ArgumentOutOfRangeException(nameof(color), "O saco já tem bolas dessa cor");
}
_ballSetsInBag.Add(new BallSet(color, IntervalLeft, IntervalRight(count)));
_ballsIn = _ballsIn + count;
}
public string ExtractBall()
{
if (AllBallsNotInserted)
{
throw new InvalidOperationException("Ainda não colocou todas a bolas no saco");
}
if (_randomGenerator.MoveNext())
{
_ballsExtracted = _ballsExtracted + (_extractedBallReturnsToBag ? 0 : 1);
var pickedBall = _randomGenerator.Current;
return _ballSetsInBag.First(ball => ball.WasPicked(pickedBall)).Color;
}
throw new InvalidOperationException("O saco está vazio");
}
private bool BagHaveThisBallColor(string color) => _ballSetsInBag.Any(ball => ball.Color.Equals(color));
private bool BallsExceedsMaximum(int count) => _ballsIn + count > TotalBalls;
private int IntervalLeft => _ballsIn + 1;
private int IntervalRight(int count) => _ballsIn + count;
private bool AllBallsNotInserted => _ballsIn < TotalBalls;
private IEnumerator<int> getRandomGenerator(int totalBalls, bool extractedBallReturnsToBag)
{
return extractedBallReturnsToBag ? RandomGenerator(1, totalBalls).GetEnumerator()
: RandomGenerator(1, totalBalls).Distinct()
.Take(totalBalls)
.GetEnumerator();
}
private static IEnumerable<int> RandomGenerator(int minInclued, int maxInclued)
{
var rand = new Random();
var i = 1;
while (i <= NUM_MAX_TIRAGENS)
{
yield return rand.Next(minInclued, maxInclued + 1);
i++;
}
}
private class BallSet
{
public string Color { get; }
private readonly int _intervalLeft;
private readonly int _intervalRight;
public BallSet(string color, int intervalLeft, int intervalRight)
{
Color = color;
_intervalLeft = intervalLeft;
_intervalRight = intervalRight;
}
public bool WasPicked(int pick)
{
return pick >= _intervalLeft && pick <= _intervalRight;
}
public override bool Equals(object obj)
{
var ballSetObj = obj as BallSet;
return ballSetObj != null && Color.Equals(ballSetObj.Color);
}
public override int GetHashCode()
{
return Color.GetHashCode();
}
}
}
Example usage:
private static void Main(string[] args)
{
var ballsBag = new BallsBag(20, extractedBallReturnsToBag: false);
ballsBag.InsertBallsByColor("Azul",6);
ballsBag.InsertBallsByColor("Vermelha", 2);
ballsBag.InsertBallsByColor("Dourada", 1);
ballsBag.InsertBallsByColor("Preta",6);
ballsBag.InsertBallsByColor("Cinza", 5);
Console.WriteLine($"Balls in bag {ballsBag.BallsInBag}");
for (var i = 0; i < ballsBag.TotalBalls; i++)
{
Console.WriteLine($"Tiragem {i+1} - {ballsBag.ExtractBall()} - Balls in bag {ballsBag.BallsInBag}");
}
Console.ReadKey();
}