Random occurrences - c#

I am not quite sure how to go about this.
I need to generate 14,296 random numbers with different levels of probability.
so for example I need an array containing the numbers 18, 1, and 17. Each number has a different percent probability of occuring. So:
55% = 18
(7,862.8 times)
30% = 1
(4,288.8 times)
15% = 17
(2,144.4 times)
the result would be something like new Array () { 18, 18, 1, 17, 1, 18...}

If you'll always have the values as integer percentages, I'd fill a 100-element array with values according to the probability, so in this case your array would have 55 occurrences of 18, 30 occurrences of 1, and 15 occurrences of 17. Then you just need to pick 14,296 random values from that array. (i.e. pick an integer in the range [0, 100) and take that element.)
For different ways of expressing the probabilities, there are different approaches, of course. But if you're given integer percentages, this is an easily-understood option. (Another way is to scale all of the probabilities by the total, i.e. into a range of [0, 1), and then take a random double in that range.)

Divide the range of random generator into proportional segments, and, judging to which segment the next random number has fallen into, select the corresponding number from your set.
Something like (simplified):
const int numbers[3] = { 1, 17, 18 };
const int borders[2] = { 0.30*MAX_RANDOM, (0.30 + 0.15) * MAX_RANDOM };
int i = random.next(), num;
if (i < borders[0]) num = number[0];
else if (i < borders[0]) num = number[1];
else num = number[2];
Of course, if there's more numbers than three, it's better to use a loop.
Note: unlike Jon Skeet's solution, this one can provide any desired granularity up to 1/(MAX_RANDOM+1) (which is often up to 2^32 on 32-bit machines), rather than strictly 1%.

Random r = new Random();
// for each number to generate
int nextNumber;
double probability = r.NextDouble();
if (probability < 55.0 / 100.0)
nextNumber = 18;
else if (probability < (55.0 + 30.0) / 100.0)
nextNumber = 1;
else
nextNumber = 17;

How about something like this (untested):
struct np
{
int n;
int p;
}
Create a List<np> and will it with value/percentage pairs (e.g. n = 18, p = 55).
Then just do the following to pick a number:
List<np> npl = new List<np>();
// (fill the list here)
int r = rnd.next(total_of_all_p_values); // get random number
int res = 0; // result
for(int i = 0; i < npl.Length(); r -= npl[i++].n)
{
if(r < npl[i].p) // remaining vlaue is smaller than current percentage
{
res = npl[i].n;
break;
}
}

You could populate a List<T> with the appropriate number of each of the 3 numbers, and then randomize the List.

Related

How exactly does one make random numbers add up to a declared sum?

I'm confused as to how exactly I would make 9 random numbers add to whatever number the user may input. Let's say the user inputs "200" as the number, how do I make it so that I could get 9 random numbers add up exactly to 200?
Obviously, the code below doesn't work the way I want it to because it's literally just 9 random numbers that don't add up to a specific number. I just have no idea how to get this built properly.
public static void RandomStats()
{
Random RandomClass = new Random();
int[] intRandomStats = {
RandomClass.Next(0, 101),
RandomClass.Next(0, 101),
RandomClass.Next(0, 101),
RandomClass.Next(0, 101),
RandomClass.Next(0, 101),
RandomClass.Next(0, 101),
RandomClass.Next(0, 101),
RandomClass.Next(0, 101),
RandomClass.Next(0, 101)
};
// ...
}
I think your question is more of math question than a code question.
It sounds like what you are looking for is a multinomial distribution. A very naive way of generating a distribution like that would be to think of it like throwing dice. Imagine that you have 200 dice with 9 sides each. Roll all of them. Count all the ones that ended up with the 1 side up, that would be your first number. Then count the ones that ended up with the 2 side up, that would be your second number. Continue until all dice are counted. There are 200 dice, so the sum of the counts will be 200. Each count would have the same probability distribution.
The above pseudo-algorithm would not be so efficient, basically looping over each die. Maybe efficiency is not so important in your case, (and 200 is a small number, so it does not matter) so feel free to write this algorithm.
If efficiency matters, try to find an existing implementation in a library. Maybe the MathNet library would work for you? See the Sample method if you are interested. At the very least, now that you know the term "multinomial distribution" it should be a bit easier to google for inspiration.
Imagine you have a bag of 200 coins. You need to divvy those coins into 9 random piles. A pile can have all the coins in the bag, some of the coins in the bag, or no coins.
Each time you allocate coins for a pile, the number of coins in the bag gets smaller (unless you grabbed 0 coins in which case it stays the same). This new count is referenced for the next pile allocation.
var rand = new Random();
var amount = 200;
var targetOutputValueCount = 9;
var outputValues = new List<int>();
for (int i = 1; i < targetOutputValueCount; i++) // 1 less than all groups
{
var groupAmount = rand.Next(0, amount);
amount -= groupAmount;
outputValues.Add(groupAmount);
}
// for the last group, it's whatever is left over
outputValues.Add(amount);
foreach (var outputValue in outputValues)
{
Console.WriteLine(outputValue);
}
An example output would be
148
28
0
2
12
2
1
6
1
The advantage of this approach is that you are always guaranteed to have positive output numbers.
Just generate eight numbers and compute the ninth as the missing difference:
int theSum = 200;
var randomNumbers = new int[9];
for(int i = 0; i < 8; i)
{
randomNumbers[i] = random.Next(0, theSum);
}
randomNumbers[8] = theSum - randomNumbers.Sum();
The methods proposed so far are workable, but tend to produce results that are skewed. For example, forcing the last number to give the correct sum can give you a value that is a long way off the other values (and possibly negative, which might be a problem in some cases). Calculating random values in the range from zero up to the remaining sum will give you a series of numbers that rapidly approach zero.
Instead, to generate n random numbers from 0 to total, I would suggest picking n-1 random values in the range from 0 to total (inclusive). Consider each of these values as the location of a bookmark in a deck of total cards. If the deck is then separated into n piles at these bookmarks, then the number of cards in each pile will give you a uniformly distributed set of values that sum to total.
Here's some code to illustrate the idea (in C, sorry):
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int cmp(const void *a, const void *b) {
return *((int*)a) - *((int*)b);
}
int main(int argc, char *argv[]) {
int nterms, total, x, i, checksum;
int *array;
srand(time(0));
if (argc != 3) return puts("Require 2 arguments: <nterms> and <total>");
nterms = atoi(argv[1]); /* N.B. Input value checks omitted. */
total = atoi(argv[2]); /* Avoid large or negative values! */
/* We want to generate nterms intervals across the range from 0 to */
/* total (inclusive), so we need an array of nterms+1 values to mark */
/* the start and end of each interval. */
array = malloc((nterms+1) * sizeof(int));
/* The first and last items in this list must be zero and total (to */
/* ensure that the list of numbers add up to the correct amount) */
array[0] = 0;
array[nterms] = total;
/* Fill the rest of the array with random values from 0 to total. */
for (i=1; i<nterms; i++) {
array[i] = rand() % (total+1);
}
/* Sort these values in ascending order. */
qsort(array, nterms+1, sizeof(int), cmp);
/* Our list of random numbers can now be calculated from the */
/* difference between each pair of values in this list. */
printf("Numbers:");
for (i=checksum=0; i<nterms; i++) {
x = array[i+1] - array[i];
checksum += x;
printf(" %d", x);
}
printf("\nTotal: %d\n", checksum);
return 0;
}
You could also repeatedly generate 9 random numbers until they sum up to the desired sum. The optimal range for the random numbers is twice the target sum (200) divided by the number of random numbers (9) because then their average will be close to 200/9.
var random = new Random();
var randomNumbers = new int[9];
int input = 200;
int optimalRange = 2 * input / randomNumbers.Length;
int iterations = 0;
do {
for (int i = 0; i < randomNumbers.Length; i++) {
randomNumbers[i] = random.Next(optimalRange);
}
iterations++;
} while (randomNumbers.Sum() != input);
Console.WriteLine($"iterations = {iterations}");
Console.WriteLine($"numbers = {String.Join(", ", randomNumbers)}");
Example output:
iterations = 113
numbers = 2, 24, 39, 28, 6, 28, 34, 17, 22
In a test I repeated one million times I got these # of iterations:
average = 98.4
min = 1
max = 1366
And in 10170 cases I got it right at the first iteration.
Thanks for the hint #DrPhil. Here's a method using linq.
Random rnd = new Random();
int[] dice = new int[200];
var sidesUp = dice.Select(x => rnd.Next(1, 10));
List<int> randomNumbers = sidesUp.GroupBy(p => p).Select(x => x.Count()).ToList();
My approach ain't too different from others, but here it is:
Started by declaring an integer with the total value;
Subtracted the user input;
Used a for loop to iterate 8 times;
Each time get the division from remaining total and remaining iterations;
Option 1:
Used previous division as the maximum random value if remaining total is less than the max value;
Option 2:
Used previous division as the maximum random value;
The 9th number is the remaining total.
//run: RandomStats(0,101,200) for your particular example.
public static void RandomStats(int min, int max, int total)
{
Random randomClass = new Random();
int[] randomStats = new int[9];
int value = 0;
int totalValue = total;
bool parsed = false;
while (!parsed)
{
Console.WriteLine("Please enter a number:");
if (int.TryParse(Console.ReadLine(), out value))
{
parsed = true;
totalValue -= value;
for (int i = 0; i < randomStats.Length-1; i++)
{
//option 1
int remainMax = (int) Math.Floor((float) totalValue / (randomStats.Length - 1 - i));
int randomValue = randomClass.Next(min, totalValue < max ? remainMax : max);
//option 2
//max = (int) Math.Floor((float) totalValue / (randomStats.Length - 1 - i));
//int randomValue = randomClass.Next(min, max);
totalValue -= randomValue;
randomStats[i] = randomValue;
}
randomStats[8] = totalValue;
}
else
{
Console.WriteLine("Not a valid input");
}
}
Console.WriteLine($"min value: {min}\tmax value: {max}\ttotal value: {total}");
int testValue = value;
Console.WriteLine("Input value - " + value);
for (int i = 0; i < randomStats.Length; i++)
{
testValue += randomStats[i];
int randomIndex = i + 1;
Console.WriteLine(randomIndex + " Random value - " + randomStats[i]);
}
Console.WriteLine("test value - " + testValue);
Console.ReadKey();
}
option 1 output:
min value: 0 max value: 101 total value: 200
Input value - 10
1 Random value - 13
2 Random value - 2
3 Random value - 95
4 Random value - 10
5 Random value - 0
6 Random value - 15
7 Random value - 10
8 Random value - 10
9 Random value - 35
test value - 200
option 2 output:
min value: 0 max value: 67* total value: 200
Input value - 10
1 Random value - 1
2 Random value - 16
3 Random value - 5
4 Random value - 29
5 Random value - 17
6 Random value - 7
7 Random value - 48
8 Random value - 19
9 Random value - 48
test value - 200
*this was still 101 but became irrelevant since I divided remaining totals by the iterations

Can you replace ints in int Arrays?

I'm looking for a way to replace every int that is greater than 9 in an array with the sum of the digits. I'm gonna demonstrate an example below.
I have this array:
int[] ints2 = new int[] { ints[0] * 2, ints[1], ints[2] * 2, ints[3], ints[4] * 2, ints[5], ints[6] * 2, ints[7], ints[8] * 2, ints[9]};
And I want to replace the first int to whatever the sum is of the two digits.
Lets say ints[0] = 9. And ints[0]*2 = 18. The sum should be 1+8 = 9.
Can I use some kind of replace method for ints? Or do you guys have any better ideas of how to deal with this issue?
I tried it like this, but obviously I'm wrong:
foreach (int number in ints)
{
int newNumberInt;
if (number > 9)
{
string newNum = number.ToString();
char[] charNum = newNum.ToCharArray();
int[] intNum = Array.ConvertAll(charNum, n => (int)Char.GetNumericValue(n));
newNumberInt = intNum[0] + intNum[1];
}
newNumberInt = number;
}
You are close. Your mistakes are an incorrect addition of digits, and failure to replace the values in the array. Here is a one line method that adds all the digits in an integer (using System.Linq):
//Adds up all the digits in a number
private static int AddDigits(int number) => number.ToString().Sum(digitCharacter => int.Parse(digitCharacter.ToString()));
For replacing the values, a foreach loop directly on the array won't work, due to changing the contents of the array during enumeration. A simple solution is to use a for loop.
for (int i = 0; i < ints.Length; i++)
{
var value = ints[i];
ints[i] = value > 9 ? AddDigits(value) : value;
}
Note: the AddDigits function I wrote only works for positive integers

Calculation position in Fibonacci order

I want to programm a level system for a small game.
The level system would be tied to the score and the levels would get further appart
only 2 score values are given
lvl, score
0, 50 (from 0 - 50)
1, 100 (from 51 to 100)
2, 150
3, 250
4, 400
5, 650
...
How could I elegantly calculate witch level I am in with a given score and 2 start values (50 and 100 in the example)
Or is it best to just calculate the score values in a list or array
With out any formula you can simply compute the whole table in a flash (under 0.0002 sec on a core2). Summing int is pretty fast. That's only 36 computation before hitting the max on int32.
var scoreTable = new []{50, 100, 150, 250, 400, 650, 1050, 1700, 2750, 4450, 7200, 11650, 18850,
30500, 49350, 79850, 129200, 209050, 338250, 547300, 885550, 1432850, 2318400, 3751250,
6069650, 9820900, 15890550, 25711450, 41602000, 67313450, 108915450, 176228900,
285144350, 461373250, 746517600, 1207890850, 1954408450};
For the math to create the table, let's be simple:
var thresholds = new List<int> {50, 100};
var index = 1;
while(true){
var temp = thresholds[cpt] + thresholds[cpt - 1];
if (temp < 0) break;
thresholds.Add(temp);
}
And to know the level:
var score = 51;
// Index is Zero-based numbering. Count is One-based. Index = Count -1;
var level = scoreTable.Where(x => x < score ).Count() - 1;
Binary.Search:
public class SupComparer : IComparer
{
public int Compare(object x, object y)
{
var t1 = UInt64.Parse(x.ToString());
var t2 = UInt64.Parse(y.ToString());
return t1.CompareTo(t2) > 0 ? 1 : 0;
}
}
var cp = new SupComparer();
var level = Array.BinarySearch(scoreTable, score, (IComparer)cp);
There's actually a formula to calculate Fibonacci numbers. That can be transformed into an algorithm to find the index of any given Fibonacci number. There's an example of how to do this in C# here.
You need to adapt that formula for use with your initial conditions of 50 and 100.
I asked a question over on Mathematics SE for help adjusting the original forumula and they suggested using
It's pretty easy to implement this as a C# method.
public int GetScoreIndex(int score)
{
const double phi = 1.61803398875;
const double rad5 = 2.2360679775;
var preLog = (score / 50) * rad5 + (1/2);
var log = Math.Log(preLog, phi);
var floor = (int) Math.Floor(log);
var index = floor - 1;
return index;
}

C# Random algorithm until fulfills condition

I have the following scenario:
Generate n numbers of random number from a certain range
Sum all the numbers
Check if the sum == x (x is a number set by user)
if sum != x then keep running the loop
If sum == x, then display the list of random numbers that sum up to x
Based on this logic, I was able to do so but it takes forever to achieve the result, is there any better / way to solve this problem?
static void Main(string[] args)
{
Console.WriteLine("starting...");
List<int> checkList = generate(5);
while(!checkSum(checkList, 100))
{
checkList = generate(5)
}
Console.WriteLine("done!");
}
private static bool checkSum(List<int> n, int sum)
{
if(n.Sum() == sum)
{
return true;
}
else
{
return false;
}
}
public static List<int> generate(int n)
{
Random rng = new Random();
List<int> list = new List<int>();
for (int i = 0; i < 5; i++)
{
//int ran = some random number
list.Add(ran);
}
return list;
}
EDIT
My scenario here is to obtain n combinations of random integer that sums up to 100. The number of combinations is taken from input by user. So the program will give the n number of possible combinations that sums up to 100.
Possible combinations:
25+37+9+20+9 = 100
46+21+13+8+12 = 100
If you have a fixed sum and a fixed number of elements you want, look at the problem as partitioning. For example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PartitionAndAllocateTEst
{
class Program
{
static void Main(string[] args)
{
var rand = new Random();
int desiredTotal = 100;
int partitions = 5;
for (int i = 0; i < 10; i++)
{
List<int> result = GetRandomCollection(rand, desiredTotal, partitions);
Console.WriteLine(string.Join(", ", result.Select(r => r.ToString()).ToArray()));
}
}
private static List<int> GetRandomCollection(Random rand, int desiredTotal, int partitions)
{
// calculate the weights
var weights = new List<double>();
for (int i = 0; i < partitions; i++)
{
weights.Add(rand.NextDouble());
}
var totalWeight = weights.Sum();
// allocate the integer total by weight
// http://softwareengineering.stackexchange.com/questions/340393/allocating-an-integer-sum-proportionally-to-a-set-of-reals/340394#340394
var result = new List<int>();
double allocatedWeight = 0;
int allocatedCount = 0;
foreach (var weight in weights)
{
var newAllocatedWeight = allocatedWeight + weight;
var newAllocatedCount = (int)(desiredTotal * (newAllocatedWeight / totalWeight));
var thisAllocatedCount = newAllocatedCount - allocatedCount;
allocatedCount = newAllocatedCount;
allocatedWeight = newAllocatedWeight;
result.Add(thisAllocatedCount);
}
return result;
}
}
}
Example output:
30, 6, 19, 15, 30
36, 8, 22, 10, 24
2, 25, 32, 21, 20
22, 7, 30, 12, 29
36, 21, 22, 0, 21
24, 24, 2, 29, 21
18, 13, 10, 39, 20
11, 19, 20, 27, 23
24, 19, 7, 25, 25
24, 14, 27, 18, 17
You could try using a genetic algorithm. Start with a population of, say, 100 sets of five random integers. Sum each set. Select 50 of the better sets, the ones with a sum closer to 100. Keep the better 50, dump the other 50 and replace them with tweaked versions of the best 50. The tweak is replacing one integer in the set with another random integer.
Whenever a member of the population sums to exactly 100, pull it out into your output array and make up the population with a newly generated set of five integers.
Genetic algorithms work a lot faster than brute force.
I don't see why you're being downvoted. Your problem is clear, and you already knew you had a bad algorithm.
I would solve this in two passes. Pass 1 is figuring out how many ways there are to get from each partial point you could be to a final answer. Pass 2 is picking a random path.
For pass 1 you build up an array of arrays. By number of numbers left to pick, by what you need them to sum to, how many ways are there to get that answer? (Search for dynamic programming for more on how to do this.)
In pass 2 you go forward. You know at each step how many ways there are to complete your task, and how many there are for each value you can pick. You therefore know the probability of picking each value. So pick a random value in (0, 1), walk through the answers, pick your next number, and proceed.
This general strategy is not just for numbers. It can be used to generate a random sample of anything that you can count using dynamic programming techniques.

Evenly divide in c#

In c# how do I evenly divide 100 into 7?
So the result would be
16
14
14
14
14
14
14
The code below is incorrect as all 7 values are set to 15 (totalling 105).
double [] vals = new double[7];
for (int i = 0; i < vals.Length; i++)
{
vals[i] = Math.Ceiling(100d / vals.Length);
}
Is there an easy way to do this in c#?
Thanks
To get my suggested result of 15, 15, 14, 14, 14, 14, 14:
// This doesn't try to cope with negative numbers :)
public static IEnumerable<int> DivideEvenly(int numerator, int denominator)
{
int rem;
int div = Math.DivRem(numerator, denominator, out rem);
for (int i=0; i < denominator; i++)
{
yield return i < rem ? div+1 : div;
}
}
Test:
foreach (int i in DivideEvenly(100, 7))
{
Console.WriteLine(i);
}
Here you go:
Func<int, int, IEnumerable<int>> f = (a, b) =>
Enumerable.Range(0,a/b).Select((n) => a / b + ((a % b) <= n ? 0 : 1))
Good luck explaining it in class though :)
Since this seems to be homework, here is a hint and not the full code.
You are doing Math.Ceiling and it converts 14.28 into 15.
The algorithm is this
Divide 100 by 7, put the result in X
Get the highest even number below X and put this in Y.
Multiply Y by 7 and put the answer in Z.
Take Z away from 100.
The answer is then 6 lots of Y plus whatever the result of step 4 was.
This algorithm may only work for this specific instance.
I'm sure you can write that in C#
Not sure if this is exactly what you are after, but I would think that if you use Math.ceiling you will always end up with too big a total. Math.floor would underestimate and leave you with a difference that can be added to one of your pieces as you see fit.
For example by this method you might end up with 7 lots of 14 giving you a remainder of 2. You can then either put this 2 into one of your pieces giving you the answer you suggested, or you could split it out evenly and add get two pieces of 15 (as suggested in one of the comments)
Not sure why you are working with doubles but wanting integer division semantics.
double input = 100;
const int Buckets = 7;
double[] vals = new double[Buckets];
for (int i = 0; i < vals.Length; i++)
{
vals[i] = Math.Floor(input / Buckets);
}
double remainder = input % Buckets;
// give all of the remainder to the first value
vals[0] += remainder;
example for ints with more flexibility,
int input = 100;
const int Buckets = 7;
int [] vals = new int[Buckets];
for (int i = 0; i < vals.Length; i++)
{
vals[i] = input / Buckets;
}
int remainder = input % Buckets;
// give all of the remainder to the first value
vals[0] += remainder;
// If instead you wanted to distribute the remainder evenly,
// priority to first
for (int r = 0; r < remainder;r++)
{
vals[r % Buckets] += 1;
}
It is worth pointing out that the double example may not be numerically stable in that certain input values and bucket sizes could result in leaking fractional values.

Categories

Resources