Distributing Items Evenly Across a Number of Days - c#

I need to distribute a remainder of items over a fixed number of days. e.g. say I have 197 items and 75 days to distribute them over. If I display 2 items for 75 days then I have 47 items not displayed. How do I distribute the remaining 47 items over the 75 days evenly so that I will get a sequence like 2,3,3,2,3,3,2,2,3.... i.e. the 3's are evenly distributed over the whole display. This is what I have so far
const double num = (double)197/75;
double temp = num;
var list = new List<int>();
for (var t = 1; t <= days; t++)
{
var intPart = int.Parse(Math.Truncate(temp).ToString());
list.Add(intPart);
var remainder = num - intPart;
temp = num + remainder;
}
but I only end up with 187 items displayed. I'm missing 10.

The original answer that turned out to give suboptimal results in some cases, as noted by AlexD:
int numItems = 197;
int numDays = 75;
var list = new List<int>();
for (int t = 0; t < numDays; t++)
{
int numItemsInDay = ((t+1)*numItems+numDays/2)/numDays - (t*numItems+numDays/2)/numDays;
list.Add(numItemsInDay);
}
The idea is that every item goes into the day defined by the rounded value of itemIndex*numDays/numItems, and you can calculate the number of items in a day directly, without keeping track of previous items.
Another approach.
To spread numItems items among numDays days as evenly as possible, you need to group the days into spans of days with floor(numItems/numDays) items and spans of days with ceil(numItems/numDays) items, spreading the spans as evenly as possible. So the problem can be solved by recursively reducing the number of days to the number of spans.
static List<int> Spread(int numDays, int numItems)
{
List<int> result = new List<int>();
if (numDays <= 1)
{
if (numDays == 1)
result.Add(numItems);
return result;
}
int numItemsInDayLower = numItems/numDays;
int numItemsInDayHigher = numItemsInDayLower+1;
int numDaysHigher = numItems - numItemsInDayLower * numDays;
int numDaysLower = numDays-numDaysHigher;
int numSpansLower = numDaysLower > numDaysHigher ? numDaysHigher + 1 : numDaysLower;
int numSpansHigher = numDaysHigher > numDaysLower ? numDaysLower + 1 : numDaysHigher;
bool isStartingFromSpanLower = numDaysLower > numDaysHigher;
List<int> spanLehgthsLower = Spread(numSpansLower, numDaysLower);
List<int> spanLehgthsHigher = Spread(numSpansHigher, numDaysHigher);
for (int iSpan = 0; iSpan < spanLehgthsLower.Count + spanLehgthsHigher.Count; iSpan++)
{
if ((iSpan % 2 == 0) == isStartingFromSpanLower)
{
for (int i = 0; i < spanLehgthsLower[iSpan/2]; i++)
result.Add(numItemsInDayLower);
}
else
{
for (int i = 0; i < spanLehgthsHigher[iSpan/2]; i++)
result.Add(numItemsInDayHigher);
}
}
return result;
}

Related

How can I make so that my Values dont repeat with Random.Range()? [duplicate]

Basically I'm creating a program to randomly generate 6 unique lottery numbers so there is no duplicates in the same line, here is the code I have so far...
//Generate 6 random numbers using the randomiser object
int randomNumber1 = random.Next(1, 49);
int randomNumber2 = random.Next(1, 49);
int randomNumber3 = random.Next(1, 49);
int randomNumber4 = random.Next(1, 49);
int randomNumber5 = random.Next(1, 49);
int randomNumber6 = random.Next(1, 49);
textBox1.Text = randomNumber1.ToString();
textBox2.Text = randomNumber2.ToString();
textBox3.Text = randomNumber3.ToString();
textBox4.Text = randomNumber4.ToString();
textBox5.Text = randomNumber5.ToString();
textBox6.Text = randomNumber6.ToString();
}
I'm getting random numbers but sometimes there is the same number on the same line, how do I make each number unique????
Thanks in advance
You need to store them in a collection and each time you pick a new number you need to make sure it's not present already, otherwise you need to generate a new number until you find a unique number.
Instead of this, I would generate a sequence between 1 and 49, shuffle them and pick 6 number out of the sequence, for example:
var rnd = new Random();
var randomNumbers = Enumerable.Range(1,49).OrderBy(x => rnd.Next()).Take(6).ToList();
You can't. You've only specified that each number be a random number from 1 to 49, not that it shouldn't match any duplicates.
Since you've got a relatively small set of numbers, your best bet is probably to draw the random numbers, put them into a HashSet, then if you need more, pull more. Something like this:
HashSet<int> numbers = new HashSet<int>();
while (numbers.Count < 6) {
numbers.Add(random.Next(1, 49));
}
Here you're taking advantage of the HashSet's elimination of duplicates. This won't work with a List or other collection.
Returning repeat values is a necessity in order for a generator to satisfy a necessary statistical property of randomness: the probability of drawing a number is not dependent on the previous numbers drawn.
You could shuffle the integers in the range 1 to 49 and return the first 6 elements. See http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle for more details on such a shuffler.
However, I think you get a slight statistical bias by doing this.
The best way is probably to use random.Next(1, 49); and reject any repeat. That will be free from statistical bias and the fact that you're only wanting 6 from 49 possibilities, the number of collisions will not slow the algorithm appreciably.
Using this extension method for reservoir sampling:
public static IList<T> TakeRandom<T>(
this IEnumerable<T> source, int count, Random random)
{
var list = new List<T>(count);
int n = 1;
foreach (var item in source)
{
if (list.Count < count)
{
list.Add(item);
}
else
{
int j = random.Next(n);
if (j < count)
{
list[j] = item;
}
}
n++;
}
return list;
}
You can sample your collection like this:
var random = new Random();
var numbers = Enumerable.Range(1, 49).TakeRandom(6, random);
numbers.Shuffle(random);
Note the returned numbers will be uniformly sampled out of all (49 choose 6) possibilities for a set of 6 numbers out of {1, 2, ..., 49}, but they will neither remain in order nor be uniformly shuffled. If you want to have the order randomized as well, you can easily do a standard Fisher-Yates shuffle afterwards.
public static void Shuffle<T>(this IList<T> list, Random random)
{
for (int i = 0; i < list.Count; i++)
{
int j = random.Next(i, list.Count);
T temp = list[j];
list[j] = list[i];
list[i] = temp;
}
}
Note a more heavily optimized version of Fisher-Yates shuffle can be found in this answer: Randomize a List<T>
List<int> aux = new List<int>();
while(aux.Count < 6)
{
int rnd = random.Next(1,49);
if(!aux.Contains(rnd))aux.add(rnd);
}
if you put all Texbox in the same panel you can do that
int j = 0;
foreach(Control x in MyPanel.Controls)
{
if(x is TexBox)
{
x.Text = aux[j].toString();
j++;
}
}
It's my solution: generate array of number
/// <summary>
/// auto generate a array with number element and max value is max
/// </summary>
/// <param name="number">number element of array</param>
/// <param name="max">max value of array</param>
/// <returns>array of number</returns>
public static int[] createRandomArray(int number, int max)
{
List<int> ValueNumber = new List<int>();
for (int i = 0; i < max; i++)
ValueNumber.Add(i);
int[] arr = new int[number];
int count = 0;
while (count < number)
{
Random rd = new Random();
int index = rd.Next(0,ValueNumber.Count -1);
int auto = ValueNumber[index];
arr[count] = auto;
ValueNumber.RemoveAt(index);
count += 1;
}
return arr;
}
It's too late but I use a Method named M_Randomizer created by me. It may look as too much work, but it's technique is different from traditional which is based on generating a random number and checking the previously generated list for uniqueness. This code while generating a new random number, never looks for the previously generated. And if we talk about touching all combinations, I have tested this method till 9 factorial, maybe little bias for some but it touches all.
using System;
class Randomizer
{
public int[] M_Randomizer(int x)
{
bool b = false;
if (x < -1)
{
b = true;
x = -1 * x;
}
if(x == -1)
x = 0;
if (x < 2)
return new int[x];
int[] site;
int k = new Random(Guid.NewGuid().GetHashCode()).Next() % 2;
if (x == 2)
{
site = new int[2];
site[0] = k;
site[1] = 1 - site[0];
return site;
}
else if (x == 3)
{
site = new int[3];
site[0] = new Random(Guid.NewGuid().GetHashCode()).Next(0, 3);
site[1] = (site[0] + k + 1) % 3;
site[2] = 3 - (site[0] + site[1]);
return site;
}
site = new int[x];
int a = 0, m = 0, n = 0, tmp = 0;
int[] p = M_Randomizer(3);
int[] q;
if (x % 3 == 0)
q = M_Randomizer(x / 3);
else
q = M_Randomizer((x / 3) + 1);
if (k == 0)
{
for (m = 0; m < q.Length; m++)
{
for (n = 0; n < p.Length && a < x; n++)
{
tmp = (q[m] * 3) + p[n];
if (tmp < x)
{
site[a] = tmp;
a++;
}
}
}
}
else
{
while (n < p.Length)
{
while (a < x)
{
tmp = (q[m] * 3) + p[n];
if (tmp < x)
{
site[a] = tmp;
a++;
}
m = m + k;
if (m >= q.Length)
break;
}
m = m % q.Length;
n++;
}
}
a = (new Random(Guid.NewGuid().GetHashCode()).Next() % 2) + 1;
k = new Random(Guid.NewGuid().GetHashCode()).Next() % 10;
if (k > 5)
for (int i = a; i < k; i++)
while (a < site.Length)
{
if (k % (a + 1) == 0)
{
tmp = site[a - 1];
site[a - 1] = site[a];
site[a] = tmp;
}
a = a + 2;
}
k = new Random(Guid.NewGuid().GetHashCode()).Next() % 10;
if (k > 5)
{
n = x / 2;
k = 0;
if (x % 2 != 0)
k = (new Random(Guid.NewGuid().GetHashCode()).Next() % 2);
p = new int[n + k];
m = (x - n) - k;
for (a = 0; m < x; a++, m++)
p[a] = site[m];
m = n + k;
for (a = (x - m) - 1; a >= 0; a--, m++)
site[m] = site[a];
for (a = 0; a < p.Length; a++)
site[a] = p[a];
}
int[] site2;
int[] site3 = new int[x];
if (b)
return site;
else
site2 = M_Randomizer(-1 * x);
for (a = 0; a < site.Length; a++)
site3[site2[a]] = site[a];
return site3;
}
public int[] M_Randomizer(int x, int start)
{
int[] dm = M_Randomizer(x);
for(int a = 0; a < x; a++)
dm[a] = dm[a] + start;
return dm;
}
}
Look at using an array to hold your 6 numbers.
Each time you generate one, loop through the array to make sure it is not already there. If it is, then generate another & loop again until you have a non-match.
It's so easy with array and OOP (Object Oriented Programming). Before you start you have to add Linq (using System.Linq) library to your project.
Random random = new Random();
int[] array = new int[6];
int number;
for (int i = 0; i < 6; i++)
{
number = random.Next(1, 50);
if (!array.Contains(number)) //If it's not contains, add number to array;
array[i] = number;
else //If it contains, restart random process
i--;
}
for (int i = 1; i < 7; i++)
{
foreach (Control c in this.Controls) //Add random numbers to all Textboxes
{
if (c is TextBox && c.Name.EndsWith(i.ToString()))
{
c.Text = array[i - 1].ToString();
}
}
}
A functional approach could be to generate an infinite sequence of random numbers, filter out non-unique numbers and take the number of unique numbers you need.
For example:
private IEnumerable<int> RandomDigitStream(int seed)
{
Random random = new Random(seed);
while (true)
{
yield return random.Next(DIGIT_MIN, DIGIT_MAX);
}
}
private List<int> GenerateUniqueRandomNumbers(int seed, int count)
{
// Assert that DIGIT_MAX - DIGIT_MIN > count to ensure
// algorithm can finish
return RandomDigitStream(seed)
.Distinct()
.Take(count)
.ToList();
}
The efficiency of this algorithm is mainly dependent on how Distinct is implemented by the .NET team. Its memory usage would grow with the number of digits you require and the range of digits produced by the random function. It also has an unpredictable running time as it depends on the probability distribution of the random function. In fact it is possible for this algorithm to get stuck in an infinite loop if the range of digits produced by the random algorithm is less than the number of digits you require.
Looking at it practically however, it should be fine for a small amount of digits but if you are looking at a large number (100 +) you might want to look at other methods.
It would be more efficient to craft a random algorithm that only produces unique numbers in the first place if that is even possible without using a lookup table.
Here is a small program using recursion to generate number lines, and also uses recursion to randomize and get unique numbers.
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static Random random;
public static List<int> lottoNumbers = Enumerable.Range(1, 49).ToList();
public static void Main()
{
random = new Random((int)DateTime.Now.Ticks);
var LinesToGenerate = 10;
GenerateNumbers(LinesToGenerate);
}
public static void GenerateNumbers(int LineCount)
{
int[] SelectedNumbers = new int[6];
for (var i = 0; i < 6; i++)
{
var number = GetRandomNumber(lottoNumbers.ToArray());
while (SelectedNumbers.Contains(number))
number = GetRandomNumber(lottoNumbers.ToArray());
SelectedNumbers[i] = number;
}
var numbersOrdered = SelectedNumbers.OrderBy(n => n).Select(n => n.ToString().PadLeft(2, '0'));
Console.WriteLine(string.Join(" ", numbersOrdered));
if (LineCount > 1)
GenerateNumbers(--LineCount);
}
//Recursively and randomly removes numbers from the array until only one is left, and returns it
public static int GetRandomNumber(int[] arr)
{
if (arr.Length > 1)
{
//Remove random number from array
var r = random.Next(0, arr.Length);
var list = arr.ToList();
list.RemoveAt(r);
return GetRandomNumber(list.ToArray());
}
return arr[0];
}
}
Yes. Use array.
Loop how many times you want:
Generate a random number,
Loop through array and compare all with the generated number.
If there's a match then loop again till there's no match.
Then store it.
Done:)

How to count each number that is generated randomly using array in C#

How to count each number that gets generated randomly using array in C#?
Output will be as below:
2
3
5
3
5
Number 1 : 0
Number 2 : 1
Number 3 : 2
Number 4 : 0
Number 5 : 2
I did come out with random numbers but then I'm stuck to figure out how to count each number.
int[] randNum;
randNum = new int[5];
Random randNum2 = new Random();
for (int i = 0; i < randNum.Length; i++)
{
randNum[i] = randNum2.Next(0, 9);
Console.Writeline(randNum[i]);
}
Console.WriteLine();
Usually, we use Dictionary for such problems:
// We build dictionary:
Dictionary<int, int> counts = new Dictionary<int, int>();
// 1000 random numbers
for (int i = 0; i < 1000; ++i) {
int random = randNum2.Next(0, 9);
if (counts.TryGetValue(random, out int count))
counts[random] = count + 1;
else
counts.Add(random, 1);
}
// Then query the dictionary, e.g.
// How many times 4 appeared?
int result = counts.TryGetValue(4, out int value) ? value : 0;
However, if numbers are of a small range (say, 0..8, not -1000000..1000000000) we can use arrays:
int numbersToGenerate = 5;
int max = 9;
int[] counts = new int[max];
for (int i = 0; i < numbersToGenerate; ++i) {
int random = randNum2.Next(0, max);
counts[random] += 1;
}
// Output:
for (int i = 0; i < counts.Length; ++i)
Console.WriteLine($"Number {i} : {counts[i]}");
If i understood you correctly you want an extra array with the counting output of the other array.
Then i think this is a simple solution:
int[] arrResult = new int[9];
foreach(int number in randNum){
if(arrResult[number] == null){
arrResult[number] = 0;
}
arrResult[number] = arrResult[number] + 1;
}
if as i am reading in your code the numbers are from 0 to 8 so 9 numbers, this will output an Array where if the random numbers for example are 0 1 0 2 3 1 0
arrResult[0] == 3
arrResult[1] == 2
arrResult[3] == 1
there probably is a more efficent way with linq and different uses but this should be a solution which would work for your problem

How to loop random number to make result equal user input summary [duplicate]

This question already has answers here:
How to generate random 5 digit number depend on user summary
(2 answers)
Closed 4 years ago.
i have some problem like this.
user(A) enter 500000 to my application and then i want to generate 50 random 5 digit number and when summary 50 random number need to equal 500000, How to do like i tried already but it's not working this is my code
int balane = 500000;
int nums = 50;
int max = balane / nums;
Random rand = new Random();
int newNum = 0;
int[] ar = new int[nums];
for (int i = 0; i < nums - 1; i++)
{
newNum = rand.Next(max);
ar[i] = newNum;
balane -= newNum;
max = balane / (nums - i - 1);
ar[nums - 1] = balane;
}
int check = 0;
foreach (int x in ar)
{
check += x;
}
the result that i tell you not working because in my array list i have value more than 5 digit but the result equal 500000.
How to solve this issue ? Thank you very much.
This works for me
public static void test(int balance = 500000, int nums = 50, int max_digits = 5)
{
int rest = balance;
var max_value = (int)Math.Pow(10, max_digits) - 1;
var rand = new Random();
int[] ar = new int[nums];
for (int i = 0; i < nums - 1; i++)
{
var max = rest / (nums - i);
if (max > max_value) max = max_value;
var newNum = rand.Next(max);
ar[i] = newNum;
rest -= newNum;
}
while (rest > max_value)
{
var all_values_are_max = true;
for (int i = 0; i < nums - 1; i++)
{
if (ar[i] < max_value)
{
var d = (int)((max_value - ar[i]) / 10.0); // 10% increase
ar[i] += d;
rest -= d;
all_values_are_max = false;
}
}
if (all_values_are_max)
throw new Exception("This is not possible at all!");
}
while (rest < 0)
{
for (int i = 0; i < nums - 1; i++)
{
if (ar[i] > 0)
{
var d = (int)(ar[i] / 20.0); // %5 decrease
ar[i] -= d;
rest += d;
}
}
}
ar[nums - 1] = rest;
int check_sum = 0;
foreach (int x in ar)
{
check_sum += x;
if (x > max_value || x < 0)
MessageBox.Show("wrong value");
}
if (check_sum != balance)
MessageBox.Show("Error: sum is " + check_sum);
MessageBox.Show("ok");
}
For example, test(500000,50) works fine all times, but test(500000, 5) throw an exception, because is not possible
Perhaps try this:
var rnd = new Random();
var numbers = Enumerable.Range(0, 50).Select(x => rnd.Next(500_000)).OrderBy(x => x).ToArray();
numbers = numbers.Skip(1).Zip(numbers, (x1, x0) => x1 - x0).ToArray();
numbers = numbers.Append(500_000 - numbers.Sum()).ToArray();
Console.WriteLine(numbers.Count());
Console.WriteLine(numbers.Sum());
This outputs:
50
500000
This works by generating 50 random numbers between 0 and 499,999 inclusively. It then sorts them ascendingly and then gets the difference between each successive pair. This by definition produces a set of 49 values that almost adds up to 500,000. It's then just a matter of adding the one missing number by doing 500_000 - numbers.Sum().

How to get specific sequence of array given the array size and the beginning

I have the following case :
a list or array .the size is a variable i get from the user between 1 to 12 .
if the size is 3 then the array {1,2,3}
if the size is 5 then the array {1,2,3,4,5}
and so on
Now the beginning is a variable also.
the sequence i wanna to get is :
if the size 12 for example ,and the beginning is 9 for example
i wanna the following result with this specific order.
9,10,11,12,1,2,3,4,5,6,7,8
I mean i begin with the given beginning until the last item then if the beginning not 1 then i continue with 1 until the beginning.
I did that but it was specific to the size 12:
with this code :
int[] arr = new int[12];
int month = 9;//input from the user
List<int> source = new List<int>();
while (month <= 12)
{
source.Add(month);
month++;
}
if (source.Count < 12)
{
for (int i = 1; i < source[0]; i++)
{
source.Add(i);
}
}
I wanna more general solution to allow variable size not just 12
I have tested it and it works
int arrsize = Convert.ToInt32(Console.ReadLine());
int[] arr = new int[arrsize];
int month = Convert.ToInt32(Console.ReadLine());//input from the user
List<int> source = new List<int>();
while (month <= arrsize)
{
source.Add(month);
month++;
}
if (source.Count < arrsize)
{
for (int i = 1; i < source[0]; i++)
{
source.Add(i);
}
}
foreach (int i in source)
Console.Write(i);
Please add some conditions like array size should not be less than month and user always input integer and do it in try catch for good practice... etc etc
After using some logic from other answers i think below code is much better.
int arrsize = Convert.ToInt32(Console.ReadLine());
int month = Convert.ToInt32(Console.ReadLine());//input from the user
List<int> source = new List<int>();
int temp = 0;
for (int i = 0; i < arrsize; i++)
{
temp = i + month;
if (temp != arrsize)
source.Add(((i + month) % arrsize));
else
source.Add(arrsize);
}
The second method has less complexity only O (n) because it uses only one loop rather 2.
Third Solution is even more simpler :)
for (int i = 0; i < size; i++)
{
if (i < month)
source.Add(i + month);
else
source.Add((i - month) + 1);
}
hope it helps.
How about something like this:
static IEnumerable<int> GetSequence(int size, int beginning)
{
return Enumerable.Range(beginning, size).Select(i => 1 + (i - 1) % 12);
}
You can change the number 12 into a third parameter of the method.
static int[] Sequence(int size, int start)
{
start--;
int[] result = new int[size];
for (int i = 0; i < size; i++)
{
result[i] = ((i + start) % size ) + 1;
}
return result;
}

Choose between multiple options with defined probability

I have a scenario where I need to show a different page to a user for the same url based on a probability distribution,
so for e.g. for 3 pages the distribution might be
page 1 - 30% of all users
page 2 - 50% of all users
page 3 - 20% of all users
When deciding what page to load for a given user, what technique can I use to ensure that the overall distribution matches the above?
I am thinking I need a way to choose an object at "random" from a set X { x1, x2....xn } except that instead of all objects being equally likely the probability of an object being selected is defined beforehand.
Thanks for the input everyone, after doing some prototyping, this is what I ended up using
private static int RandomIndexWithPercentage(Random random, int[] percentages) {
if (random == null) {
throw new ArgumentNullException("random");
}
if (percentages == null || percentages.Length == 0) {
throw new ArgumentException("percentages cannot be null or empty", "percentages");
}
if(percentages.Sum() != 100) {
throw new ArgumentException("percentages should sum upto 100");
}
if (percentages.Any(n => n < 0)) {
throw new ArgumentException("percentages should be non-negative");
}
var randomNumber = random.Next(100);
var sum = 0;
for (int i = 0; i < percentages.Length; ++i) {
sum += percentages[i];
if (sum > randomNumber) {
return i;
}
}
//This should not be reached, because randomNumber < 100 and sum will hit 100 eventually
throw new Exception("Unexpected");
}
Generate a number 0-9. If the number is less than 3, give them page one. If it's less than 8, give them page two, or else give them page three.
Some code, to get you started:
private int ChoosePage()
{
int[] weights = new int[] { 3, 5, 2 };
int sum = 0;
int i;
for (i = 0; i < weights.Length; i++)
sum += weights[i];
int selection = (new Random()).Next(sum);
int count = 0;
for (i = 0; i < weights.Length - 1; i++)
{
count += weights[i];
if (selection < count)
return i;
}
return weights.Length - 1;
}
Note that weights doesn't have to add up to anything in particular. If sum = 100, then weight[i] is th percent chance of getting page i. If it doesn't, however, it's just relative - if weight[i] is twice weight[j], then page i will get twice as many hits as page j. This is nice because you can arbitrarily increase or decrease page traffic without recalculating anything. Alternatively, you could make sure the sum is always N, and hardcode N in, rather than summing all the values every time. There are a lot more optimizations you could do, I'm sure.
This is my code and works carefully.
public static int GetRandomIndexByPercent(params byte[] percentages)
{
int randomNumber = new Random().Next(1, percentages.Sum(a => a) + 1);
for (int sum = 0, i = 0; i < percentages.Length - 1; i++)
{
sum += percentages[i];
if (randomNumber <= sum) return i;
}
return percentages.Length - 1;
}
Also for test it, you can use this code:
for (int j = 0; j < 5; j++)
{
var items = new int[] { 40, 20, 10, 30 };
var items2 = new int[items.Length];
for (int i = 1; i <= 100000; i++)
items2[GetRandomIndexByPercent(items)]++;
for (int i = 0; i < items2.Length; i++)
Console.WriteLine(items[i] + " > " + (items2[i] / 1000.0));
Console.WriteLine("\n\n");
}
Console.ReadKey();

Categories