Why multiply Random.Next() by a Constant? - c#

I recently read an article explaining how to generate a weighted random number, and there's a piece of the code that I don't understand:
int r = ((int)(rand.Next() * (323567)) % prefix[n - 1]) + 1;
Why is rand.Next being multiplied by a constant 323567? Would the code work without this constant?
Below is the full code for reference, and you can find the full article here: https://www.geeksforgeeks.org/random-number-generator-in-arbitrary-probability-distribution-fashion/
Any help is appreciated, thank you!!
// C# program to generate random numbers
// according to given frequency distribution
using System;
class GFG{
// Utility function to find ceiling
// of r in arr[l..h]
static int findCeil(int[] arr, int r,
int l, int h)
{
int mid;
while (l < h)
{
// Same as mid = (l+h)/2
mid = l + ((h - l) >> 1);
if (r > arr[mid])
l = mid + 1;
else
h = mid;
}
return (arr[l] >= r) ? l : -1;
}
// The main function that returns a random number
// from arr[] according to distribution array
// defined by freq[]. n is size of arrays.
static int myRand(int[] arr, int[] freq, int n)
{
// Create and fill prefix array
int[] prefix = new int[n];
int i;
prefix[0] = freq[0];
for(i = 1; i < n; ++i)
prefix[i] = prefix[i - 1] + freq[i];
// prefix[n-1] is sum of all frequencies.
// Generate a random number with
// value from 1 to this sum
Random rand = new Random();
int r = ((int)(rand.Next() * (323567)) % prefix[n - 1]) + 1; // <--- RNG * Constant
// Find index of ceiling of r in prefix array
int indexc = findCeil(prefix, r, 0, n - 1);
return arr[indexc];
}
// Driver Code
static void Main()
{
int[] arr = { 1, 2, 3, 4 };
int[] freq = { 10, 5, 20, 100 };
int i, n = arr.Length;
// Let us generate 10 random numbers
// according to given distribution
for(i = 0; i < 5; i++)
Console.WriteLine(myRand(arr, freq, n));
}
}
UPDATE:
I ran this code to check it:
int[] intArray = new int[] { 1, 2, 3, 4, 5 };
int[] weights = new int[] { 5, 20, 20, 40, 15 };
List<int> results = new List<int>();
for (int i = 0; i < 100000; i++)
{
results.Add(WeightedRNG.GetRand(intArray, weights, intArray.Length));
}
for (int i = 0; i < intArray.Length; i++)
{
int itemsFound = results.Where(x => x == intArray[i]).Count();
Console.WriteLine($"{intArray[i]} was returned {itemsFound} times.");
}
And here are the results with the constant:
1 was returned 5096 times.
2 was returned 19902 times.
3 was returned 20086 times.
4 was returned 40012 times.
5 was returned 14904 times.
And without the constant...
1 was returned 100000 times.
2 was returned 0 times.
3 was returned 0 times.
4 was returned 0 times.
5 was returned 0 times.
It completely breaks without it.

The constant does serve a purpose in some environments, but I don't believe this code is correct for C#.
To explain, let's look at the arguments to the function. The first sign something is off is passing n as an argument instead of inferring it from the arrays. The second sign is it's poor practice in C# to deal with paired arrays rather than something like a 2D array or sequence of single objects (such as a Tuple). But those are just indicators something is odd, and not evidence of any bugs.
So let's put that on hold for a moment and explain why a constant might matter by looking a small example.
Say you have three numbers (1, 2, and 3) with weights 3, 2, and 2. This function first builds up a prefix array, where each item includes the chances of finding the number for that index and all previous numbers.
We end up with a result like this: (3, 5, 7). Now we can use the last value and take a random number from 1 to 7. Values 1-3 result in 1, values 4 and 5 result in 2, and values 6 and 7 result in 3.
To find this random number the code now calls rand.Next(), and this is where I think the error comes in. In many platforms, the Next() function returns a double between 0 and 1. That's too small to use to lookup your weighted value, so you then multiply by a prime constant related the platform's epsilon value to ensure you have a reasonably large result that will cover the entire possible range of desired weights (in this case, 1-7) and then some. Now you take the remainder (%) vs your max weight (7), and map it via the prefix array to get the final result.
So the first error is, in C#, .Next() does not return a double. It is documented to return a non-negative random integer between 0 and int.MaxValue. Multiply that by 323567 and you're asking for integer overflow exceptions. Another sign of this mistake is the cast to int: the result of this function is already an int. And let's not even talk the meaningless extra parentheses around (323567).
There is also another, more subtle, error.
Let's the say the result of the (int)(rand.Next() * 323567) expression is 10. Now we take this value and get the remainder when dividing by our max value (%7). The problem here is we have two chances to roll a 1, 2, or 3 (the extra chance is if the original was 8, 9, or 10), and only once chance for the remaining weights (4-7). So we have introduced unintended bias into the system, completely throwing off the expected weights. (This is a simplification. The actual number space is not 1-10; it's 0 * 323567 - 0.999999 * 323567. But the potential for bias still exists as long that max value is not evenly divisible by the max weight value).
It's possible the constant was chosen because it has properties to minimize this effect, but again: it was based on a completely different kind of .Next() function.
Therefore the rand.Next() line should probably be changed to look like this:
int r = rand.Next(prefix[n - 1]) +1;
or this:
int r = ((int)(rand.NextDouble() * (323567 * prefix[n - 1])) % prefix[n - 1]) + 1;
But, given the other errors, I'd be wary of using this code at all.
For fun, here's an example running several different options:
https://dotnetfiddle.net/Y5qhRm
The original random method (using NextDouble() and a bare constant) doesn't fare as badly as I'd expect.

Related

Why do I need +-1 in a Binary Search?

So I'm curious.. Why is it that I need to do +1 and -1 when truncating a side of the array.
I get that an array is index based and starts at 0 but is that really the reason to why I need to do it? What's the actual logic behind it? I've noticed that if I don't do it, it just never exists the loop because it gets to a point where it just keeps dividing the values to the same value over and over again.
private static int[] values = { 1, 3, 5, 7, 10, 13, 15, 17 };
public static int FindValue(int valueToFind)
{
int l = 0;
int r = values.Length - 1;
while (l <= r)
{
var mid = (l + r) / 2;
if (values[mid] == valueToFind)
return mid;
if (values[mid] < valueToFind)
l = mid + 1;
else
r = mid - 1;
}
return -1;
}
If instead of l = mid + 1; we would have l = mid; then a problem arises when the l and r differ by at most 1 (so there are at most two array values in the running). In that case (l + r) / 2 == l, so that mid will be equal to l. Now let's suppose the value we look for is greater than values[mid], then the if block will execute and l will be assigned mid. But it already had that value, so nothing changes! The next iteration will start with exactly the same state as the previous one, and we'll loop without end.
If you would replace r = mid - 1; with just r = mid, then a similar problem can arise when there is just one value in the array left, i.e. l and r are equal. If the value we look for is less than that only value values[mid], then r will be assigned mid, but again, it already had that value. Nothing changes, and the looping goes on for ever.
The reasoning to have the +1 and -1 in those assignments is that:
it ensures that the interval will get smaller in each iteration, and so it will be impossible to have an infinite loop
it excludes mid from the reduced range, which makes sense, as with the first if we already compared with the value at that index, so it is no longer a candidate.
taking in your last comment, I would assume that it's a rounding issue. It's rounding up and the next calculated number is still higher than the target value. I would add some console logging to printout the value as it's searching.

How does prime factorization guarantee that the factors will be prime?

In this particular code how does it not output 6 1, because 6 gives no remainder dividing 18 by it.
int n = 18;
int[] fact = new int[100];
int[] pow = new int[100];
int d = 0;
for (int i = 2; i <= n; i++)
{
int s = 0;
while(n % i == 0)
{
s++;
n /= i;
}
if (s > 0)
{
fact[d] = i;
pow[d] = s;
d++;
}
}
Every numbers is either a prime, or a multiple of a prime. Look at: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
If it is not divisible by a prime, it can not be divisible by any multiple of that prime either. As 18 can be divided by 2 - 4, 6, 8, 10 and everything else never needs to be tested. As 19 can not be divided by 2, 4, 6, 8 and 10 never need to be tested. That 20 is divisible by 10, never needs to be tested. It already divides by 2 so we excluded half the possible numbers back then.
We do that exclusion of divisors instinctively, but usually only for 2 - testing only odd numbers - but you can drive that way further. As the result of a division must also be a prime or multiple of a prime, we even can stop once we passed the Squareroot.
All you really need is a unbroken list of primes, going to up to whatever Prime is > Squarroot(PrimeCandidate). Short of reading it from memory, that is the quickest way I know off.
I actually wrote something on the topic a while back, about the 5-ish ways of checking for a prime: https://social.msdn.microsoft.com/Forums/en-US/85fc2406-d2e9-495a-bea7-e516661f8b40/primal-issues-multithreading-lists-in-memory-and-checking-for-prime-number?forum=csharpgeneral

Given an array of integers, how can I find all common multiples up to a maximum number?

This is my first question on this site. I am practicing on a problem on Hackerrank that asks to find numbers "Between two Sets". Given two arrays of integers, I must find the number(s) that fit the following two criteria:
1) The elements in the first array must all be factors of the number(s)
2) The number(s) must factor into all elements of the second array
I know that I need to find all common multiples of every element in the first array, but those multiples need to be less than or equal to the minimum value of the second array. I first sort the first array then find all the multiples of ONLY the largest number in the first array (again, up to a max of the second array's minimum) and store those multiples in a list. Then, I move on to the second largest element in the first array and test it against the array of existing multiples. All elements in the list of existing multiples that isn't also a multiple of the second largest element of the first array is removed. I then test the third largest value of the first array, all the way to the minimum value. The list of existing multiples should be getting trimmed as I iterate through the first array in descending order. I've written a solution which passes only 5 out of the 9 test cases on the site, see code below. My task was to edit the getTotalX function and I created the getCommonMultiples function myself as a helper. I did not create nor edit the main function. I am not sure why I am not passing the other 4 test cases as I can't see what any of the test cases are.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
class Solution {
/*
* Complete the getTotalX function below.
*/
static int getTotalX(int[] a, int[] b) {
//get minimum value of second array
int b_min = b.Min();
//create List to hold multiples
List<int> multiples = getCommonMultiples(a, b_min);
//create List to hold number of ints which are in solution
List<int> solutions = new List<int>();
foreach(int x in multiples)
{
foreach(int y in b)
{
if (y % x == 0 && !solutions.Contains(x))
{
solutions.Add(x);
}
else
{
break;
}
}
}
return solutions.Count;
}
static List<int> getCommonMultiples(int[] array, int max)
{
//make sure array is sorted
Array.Sort(array);
int x = array.Length - 1; //x will be the last # in array -- the max
int y = 1;
//find all multiples of largest number first and store in a list
int z = array[x] * y;
List<int> commonMultiples = new List<int>();
while(z <= max)
{
commonMultiples.Add(z);
y++;
z = array[x] * y;
}
//all multiples of largest number are now added to the list
//go through the smaller numbers in query array
//only keep elements in list if they are also multiples of smaller
//numbers
int xx = array.Length - 2;
for(int a = array[xx]; xx >= 0; xx--)
{
foreach(int b in commonMultiples.ToList())
{
if (b % a != 0)
{
commonMultiples.Remove(b);
}
else
{
continue;
}
}
}
return commonMultiples;
}
static void Main(string[] args) {
TextWriter tw = new StreamWriter(#System.Environment.GetEnvironmentVariable("OUTPUT_PATH"), true);
string[] nm = Console.ReadLine().Split(' ');
int n = Convert.ToInt32(nm[0]);
int m = Convert.ToInt32(nm[1]);
int[] a = Array.ConvertAll(Console.ReadLine().Split(' '), aTemp => Convert.ToInt32(aTemp))
;
int[] b = Array.ConvertAll(Console.ReadLine().Split(' '), bTemp => Convert.ToInt32(bTemp))
;
int total = getTotalX(a, b);
tw.WriteLine(total);
tw.Flush();
tw.Close();
}
}
Again, I can't see the test cases so I do not know what exactly the issue is. I went through the code line by line and can't find any OutOfBoundExceptions or things of that sort so it has to be a logic issue. Thanks for the help!
A typical sample involves 3 lines of input. The first line has 2 integers which gives the length of the first array and the second array, respectively. The second line will give the integers in the first array. The third line will give the integers in the second array. The output needs to be the total number of integers "in between" the two arrays. It will looks like this:
Sample Input
2 3
2 4
16 32 96
Sample Output
3
Explanation: 2 and 4 divide evenly into 4, 8, 12 and 16.
4, 8 and 16 divide evenly into 16, 32, 96.
4, 8 and 16 are the only three numbers for which each element of the first array is a factor and each is a factor of all elements of the second array.
I see two issues with the code you posted.
Firstly, as #Hans Kesting pointed out, a = array[xx] is not being updated each time in the for loop. Since the variable a is only used in one spot, I recommend just replacing that use with array[xx] and be done with it as follows:
for(int xx = array.Length - 2; xx >= 0; xx--)
{
foreach(int b in commonMultiples.ToList())
{
if (b % array[xx] != 0)
{
commonMultiples.Remove(b);
For your understanding of for loops: to properly increment a each time you'd write the for loop like this:
for(int xx = array.Length - 2, a = array[xx]; xx >= 0; xx--, a = array[xx])
The first part of the for loop (up until ;) is the initialization stage which is only called before the entering the loop the first time. The second part is the while condition that is checked before each time through loop (including the first) and if at any time it evaluates to false, the loop is broken (stopped). The third part is the increment stage that is called only after each successful loop.
Because of that in order to keep a up to date in the for loop head, it must appear twice.
Secondly, your solutions in getTotalX is additive, meaning that each multiple that works for each value in array b is added as a solution even if it doesn't fit the other values in b. To get it to work the way that you want, we have to use a Remove loop, rather than an Add loop.
List<int> multiples = getCommonMultiples(a, b_min);
//create List to hold number of ints which are in solution
List<int> solutions = multiples.ToList();
foreach(int x in multiples)
{
foreach(int y in b)
{
if (y % x != 0)
{
solutions.Remove(x);
break;
}
}
}
You could also use LINQ to perform an additive solution where it takes into account All members of b:
//create List to hold number of ints which are in solution
List<int> solutions = multiples.Where((x) => b.All((y) => y % x == 0)).ToList();

How do I generate random number between 0 and 1 in C#?

I want to get the random number between 1 and 0. However, I'm getting 0 every single time. Can someone explain me the reason why I and getting 0 all the time?
This is the code I have tried.
Random random = new Random();
int test = random.Next(0, 1);
Console.WriteLine(test);
Console.ReadKey();
According to the documentation, Next returns an integer random number between the (inclusive) minimum and the (exclusive) maximum:
Return Value
A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values includes minValue but not maxValue. If minValue equals maxValue, minValue is returned.
The only integer number which fulfills
0 <= x < 1
is 0, hence you always get the value 0. In other words, 0 is the only integer that is within the half-closed interval [0, 1).
So, if you are actually interested in the integer values 0 or 1, then use 2 as upper bound:
var n = random.Next(0, 2);
If instead you want to get a decimal between 0 and 1, try:
var n = random.NextDouble();
You could, but you should do it this way:
double test = random.NextDouble();
If you wanted to get random integer ( 0 or 1), you should set upper bound to 2, because it is exclusive
int test = random.Next(0, 2);
Every single answer on this page regarding doubles is wrong, which is sort of hilarious because everyone is quoting the documentation. If you generate a double using NextDouble(), you will not get a number between 0 and 1 inclusive of 1, you will get a number from 0 to 1 exclusive of 1.
To get a double, you would have to do some trickery like this:
public double NextRandomRange(double minimum, double maximum)
{
Random rand = new Random();
return rand.NextDouble() * (maximum - minimum) + minimum;
}
and then call
NextRandomRange(0,1 + Double.Epsilon);
Seems like that would work, doesn't it? 1 + Double.Epsilon should be the next biggest number after 1 when working with doubles, right? This is how you would solve the problem with ints.
Wellllllllllllllll.........
I suspect that this will not work correctly, since the underlying code will be generating a few bytes of randomness, and then doing some math tricks to fit it in the expected range. The short answer is that Logic that applies to ints doesn't quite work the same when working with floats.
Lets look, shall we? (https://referencesource.microsoft.com/#mscorlib/system/random.cs,e137873446fcef75)
/*=====================================Next=====================================
**Returns: A double [0..1)
**Arguments: None
**Exceptions: None
==============================================================================*/
public virtual double NextDouble() {
return Sample();
}
What the hell is Sample()?
/*====================================Sample====================================
**Action: Return a new random number [0..1) and reSeed the Seed array.
**Returns: A double [0..1)
**Arguments: None
**Exceptions: None
==============================================================================*/
protected virtual double Sample() {
//Including this division at the end gives us significantly improved
//random number distribution.
return (InternalSample()*(1.0/MBIG));
}
Ok, starting to get somewhere. MBIG btw, is Int32.MaxValue(2147483647 or 2^31-1), making the division work out to:
InternalSample()*0.0000000004656612873077392578125;
Ok, what the hell is InternalSample()?
private int InternalSample() {
int retVal;
int locINext = inext;
int locINextp = inextp;
if (++locINext >=56) locINext=1;
if (++locINextp>= 56) locINextp = 1;
retVal = SeedArray[locINext]-SeedArray[locINextp];
if (retVal == MBIG) retVal--;
if (retVal<0) retVal+=MBIG;
SeedArray[locINext]=retVal;
inext = locINext;
inextp = locINextp;
return retVal;
}
Well...that is something. But what is this SeedArray and inext crap all about?
private int inext;
private int inextp;
private int[] SeedArray = new int[56];
So things start to fall together. Seed array is an array of ints that is used for generating values from. If you look at the init function def, you see that there is a whole lot of bit addition and trickery being done to randomize an array of 55 values with initial quasi-random values.
public Random(int Seed) {
int ii;
int mj, mk;
//Initialize our Seed array.
//This algorithm comes from Numerical Recipes in C (2nd Ed.)
int subtraction = (Seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(Seed);
mj = MSEED - subtraction;
SeedArray[55]=mj;
mk=1;
for (int i=1; i<55; i++) { //Apparently the range [1..55] is special (All hail Knuth!) and so we're skipping over the 0th position.
ii = (21*i)%55;
SeedArray[ii]=mk;
mk = mj - mk;
if (mk<0) mk+=MBIG;
mj=SeedArray[ii];
}
for (int k=1; k<5; k++) {
for (int i=1; i<56; i++) {
SeedArray[i] -= SeedArray[1+(i+30)%55];
if (SeedArray[i]<0) SeedArray[i]+=MBIG;
}
}
inext=0;
inextp = 21;
Seed = 1;
}
Ok, going back to InternalSample(), we can now see that random doubles are generated by taking the difference of two scrambled up 32 bit ints, clamping the result into the range of 0 to 2147483647 - 1 and then multiplying the result by 1/2147483647. More trickery is done to scramble up the list of seed values as it uses values, but that is essentially it.
(It is interesting to note that the chance of getting any number in the range is roughly 1/r EXCEPT for 2^31-2, which is 2 * (1/r)! So if you think some dumbass coder is using RandNext() to generate numbers on a video poker machine, you should always bet on 2^32-2! This is one reason why we don't use Random for anything important...)
so, if the output of InternalSample() is 0 we multiply that by 0.0000000004656612873077392578125 and get 0, the bottom end of our range. if we get 2147483646, we end up with 0.9999999995343387126922607421875, so the claim that NextDouble produces a result of [0,1) is...sort of right? It would be more accurate to say it is int he range of [0,0.9999999995343387126922607421875].
My suggested above solution would fall on its face, since double.Epsilon = 4.94065645841247E-324, which is WAY smaller than 0.0000000004656612873077392578125 (the amount you would add to our above result to get 1).
Ironically, if it were not for the subtraction of one in the InternalSample() method:
if (retVal == MBIG) retVal--;
we could get to 1 in the return values that come back. So either you copy all the code in the Random class and omit the retVal-- line, or multiply the NextDouble() output by something like 1.0000000004656612875245796924106 to slightly stretch the output to include 1 in the range. Actually testing that value gets us really close, but I don't know if the few hundred million tests I ran just didn't produce 2147483646 (quite likely) or there is a floating point error creeping into the equation. I suspect the former. Millions of test are unlikely to yield a result that has 1 in 2 billion odds.
NextRandomRange(0,1.0000000004656612875245796924106); // try to explain where you got that number during the code review...
TLDR? Inclusive ranges with random doubles is tricky...
You are getting zero because Random.Next(a,b) returns number in range [a, b), which is greater than or equal to a, and less than b.
If you want to get one of the {0, 1}, you should use:
var random = new Random();
var test = random.Next(0, 2);
Because you asked for a number less than 1.
The documentation says:
Return Value
A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values
includes minValue but not maxValue. If minValue equals maxValue,
minValue is returned.
Rewrite the code like this if you are targeting 0.0 to 1.0
Random random = new Random();
double test = random.NextDouble();
Console.WriteLine(test);
Console.ReadKey();

How to get count of numbers in int and how to split a number without making a string

I have a number like 601511616
If all number's length is multiple of 3, how can a split the number into an array without making a string
Also, how can I count numbers in the int without making a string?
Edit: Is there a way to simply split the number, knowing it's always in a multiple of 3... good output should look like this: {616,511,601}
You can use i % 10 in order to get the last digit of integer.
Then, you can use division by 10 for removing the last digit.
1234567 % 10 = 7
1234567 / 10 = 123456
Here is the code sample:
int value = 601511616;
List<int> digits = new List<int>();
while (value > 0)
{
digits.Add(value % 10);
value /= 10;
}
// digits is [6,1,6,1,1,5,1,0,6] now
digits.Reverse(); // Values has been inserted from least significant to the most
// digits is [6,0,1,5,1,1,6,1,6] now
Console.WriteLine("Count of digits: {0}", digits.Count); // Outputs "9"
for (int i = 0; i < digits.Count; i++) // Outputs "601,511,616"
{
Console.Write("{0}", digits[i]);
if (i > 0 && i % 3 == 0) Console.Write(","); // Insert comma after every 3 digits
}
IDEOne working demonstration of List and division approach.
Actually, if you don't need to split it up but only need to output in 3-digit groups, then there is a very convenient and proper way to do this with formatting.
It will work as well :)
int value = 601511616;
Console.WriteLine("{0:N0}", value); // 601,511,616
Console.WriteLine("{0:N2}", value); // 601,511,616.00
IDEOne working demonstration of formatting approach.
I can't understand your question regarding how to split a number into an array without making a string - sorry. But I can understand the question about getting the count of numbers in an int.
Here's your answer to that question.
Math.Floor(Math.Log10(601511616) + 1) = 9
Edit:
Here's the answer to your first question..
var n = 601511616;
var nArray = new int[3];
for (int i = 0, numMod = n; i < 3; numMod /= 1000, i++)
nArray[i] = numMod%1000;
Please keep in mind there's no safety in this operation.
Edit#3
Still not perfect, but a better example.
var n = 601511616;
var nLength = (int)Math.Floor(Math.Log10(n) + 1)/ 3;
var nArray = new int[nLength];
for (int i = 0, numMod = n; i < nLength; numMod /= 1000, i++)
nArray[i] = numMod%1000;
Edit#3:
IDEOne example http://ideone.com/SSz3Ni
the output is exactly as the edit approved by the poster suggested.
{ 616, 511, 601 }
Using Log10 to calculate the number of digits is easy, but it involves floating-point operations which is very slow and sometimes incorrect due to rounding errors. You can use this way without calculating the value size first. It doesn't care if the number of digits is a multiple of 3 or not.
int value = 601511616;
List<int> list = new List<int>();
while (value > 0) // main part to split the number
{
int t = value % 1000;
value /= 1000;
list.Add(t);
}
// Convert back to an array only if it's necessary, otherwise use List<T> directly
int[] splitted = list.ToArray();
This will store the splitted numbers in reverse order, i.e. 601511616 will become {616, 511, 601}. If you want the numbers in original order, simply iterate the array backwards. Alternatively use Array.Reverse or a Stack
Since you already know they are in multiples of 3, you can just use the extracting each digit method but use 1000 instead of 10. Here is the example
a = 601511616
b = []
while(a):
b.append(a%1000)
a = a//1000
print(b)
#[616, 511, 601]

Categories