Java: Generating a random numbers with a logarithmic distribution - java

I am attempting to generate a random numbers with a logarithmic distribution.
Where n=1 occurs half of the time, n=2 occurs a quarter of the time, n=3 occurs an eighth of the time, etc.
int maxN = 5;
int t = 1 << (maxN); // 2^maxN
int n = maxN -
((int) (Math.log((Math.random() * t))
/ Math.log(2))); // maxN - log2(1..maxN)
System.out.println("n=" + n);
Most of the time, I am getting the result I need, however once every so often, I get a value of n that is larger than maxN.
Why is this so? The way I see it, the max value of Math.random() is 1.0;
therefore the max value of (Math.random() * t)) is t;
therefore the max value of log2(t) is maxN, since t = 2^maxN;
Where has my logic gone off track?
Thanks

logarithm of numbers less than 1.0 is negative. When the random number generated is such that it is less than 1.0, the expression ((int) (Math.log(Math.random() * t) / Math.log(2))) is a negative number and hence maxN - (the negative number) is more than maxN.
The following expression should give correct distribution.
n = Math.floor(Math.log((Math.random() * t) + 1)/Math.log(2))
Note that:
0.0 <= Math.random() <= 1.0
0.0 <= Math.random() * t <= t
1.0 <= (Math.random() * t) + 1 <= t + 1.0
0.0 <= Math.log((Math.random() * t) + 1) <= Math.log(t + 1.0)
0.0 <= Math.log((Math.random() * t) + 1)/Math.log(2) <= Math.log(t + 1.0)/Math.log(2)
Since t = 2^maxN,
Math.log(t + 1.0)/Math.log(2) is slightly larger than maxN.
So do a Math.floor and you get the correct result:
0.0 <= Math.floor(Math.log((Math.random() * t) + 1)/Math.log(2)) <= maxN

If Math.random()*t is less that 1, then you will get a negative answer when you take Math.log(Math.random()*t), by the rules of Logarithms. This means that you will get a negative answer when you divide by Math.log(2) because that is 0.69314718055994530941723212145818. This is a negative number divided by a positive number. The answer is negative. maxN - a negative number = maxN + something positive, so n is greater than maxN. To fix this cast Math.random()*t to an int and add 1:
int n = maxN -
((int) (Math.log((int)((Math.random() * t)+1))
/ Math.log(2))); // maxN - log2(1..maxN)
Notice the cast inside the log, and the add of 1.
The purpose of adding one would be to avoid the 0. Can't take a log of 0. Also, without adding 1, you could never get maxN inside the log, because Math.random() never produces 1. This way, instead of getting 1 half, 2, a fourth, 3, and eighth, it just starts at 0. This gives 0, a half, 1 a fourth, 2 an eighth, etc.

The problem is in the other end of the scale.
Consider what would happen if you get a very small random number.

Related

Generate Random X.XXX numbers between [-2, 2]

x = rd.nextInt((4000) - 2000) / 1000.0;
This is generating numbers between [0, 2] to the thousandth decimal which is what I want, I just also need negative number so the range of numbers generated is between [-2, 2].
The problem you are facing is integer arithmetic, which truncates the fractional part of the result. Use rd.nextDouble() instead so the arithmetic results are double, which retains the fractional part.
However, to round to 1/100ths, you can use integer arthmetic to your advantage.
Your question has the text to the hundredth decimal, so here's that solution:
x = (int)(((rd.nextDouble() * 4) - 2) * 100) / 100d;
But your title mentions X.XXX, so here's that solution:
x = (int)(((rd.nextDouble() * 4) - 2) * 1000) / 1000d;
To unravel what's going on here:
generate random double between 0.000000 and 1.000000
multiply by the scale of the range, so we get a number between 0.000000 and 4.000000
subtract 2, so we get a number between -2.000000 and 2.000000
multiply by 1000, so we get a number between -2000.000000 and 2000.000000
cast to int to truncate the fraction, so we get a int between -2000 and 2000
divide by 1000d (which is a double), so we get a double between -2.000 and 2.000
Floating point numbers do not always have a precise number of digits. Also, the third decimal place is called thousandths (the hundredth decimal would be the second digit after the .). I think you know this because you are dividing by a thousand. So, step one: generate a single random value between 0 and 4 as a double. Step two: Subtract two and convert to a formatted String (so you can control the number of digits).
double d = (rd.nextDouble() * 4) - 2;
String x = String.format("%.3f", d);
You can generate a random float and then use a modulo to truncate it to the hundredth decimal place.
int min = -2;
int max = 2;
Random rand = new Random();
float ret = rand.nextFloat() * (max - min) + min;
return ret - (ret % 0.01);
You can generate random number on a range [0,4] and then simply subtract 2 from the result:
x = (rd.nextInt(4000) / 1000.0) - 2;

Java - Else If loop will NOT work with instance variable

I'm attempting to calculate how much tax a user should pay, based on wage. For example, calculating 20% in the first if loop, this will be saved in generaltax:
int generalTax = 0;
int userGrossPay = 50000;
if (userGrossPay <= 10600) {generalTax += 0;}
else if (((userGrossPay >= 10600) && (userGrossPay <= 31785))) { generalTax = ((20/100) * userGrossPay); }
else if (((userGrossPay >= 31786) && (userGrossPay <= 150000))) { generalTax = ((40/100) * userGrossPay); System.out.println(generalTax);}
else if (userGrossPay > 150001) {generalTax = ((45/100) * userGrossPay); }
else{System.out.println("error");};
userGrossPay -= generalTax;
System.out.println(userGrossPay);
However generalTax pay is for some reason always stuck as 0 and is not properly updating on each iteration.
Your problem is that you are always adding 0 or assigning 0 to generalTax.
For example, (20/100) * userGrossPay is 0, since 20/100 is 0 due to int division. Change it too 0.2 * userGrossPay or 20.0/100 * userGrossPay. Similarly change all other places where you divide two integers.
This is caused by integer division.
If you mix floating data type with integer, it will give you a data conversion by promotion.
For example:
int num = 5;
System.out.println(num / 2); //Gives you 2
System.out.println(num / 2.0); //Gives you 2.5
System.out.println(num * 2); //Gives you 10
System.out.println(num * 2.0); //Gives you 10.0
System.out.println(num + 2.5); //Gives you 7.5
If all operands in the operation are integers, your output will be integer as well. This is how you got into an integer division accidentally.
((20/100) * userGrossPay);
I see that you already accepted Eran's answer and understood what went wrong, I am here to give you some additional information.

math.random always give 0 result

I am using Ubuntu 14.04.3 LTS and I am studying Java from the book. I tried to follow one example on the book with Ubuntu Terminal and I'm using Sublime Text. Here is the code
import java.util.Scanner;
public class RepeatAdditionQuiz{
public static void main(String[] args){
int number1 = (int)(Math.random()%10);
int number2 = (int)(Math.random()%10);
Scanner input = new Scanner(System.in);
System.out.print(
"What is "+number1+" + "+number2+"?");
int answer = input.nextInt();
while(number1+number2 != answer){
System.out.print("Wrong answer. Try again. What is "
+number1+" + "+number2+"? ");
answer = input.nextInt();
}
System.out.println("You got it!");
}
}
But the problem is, when I compiled it and executed it. It gives me result
what is 0 + 0?_
every time. It suppose to give me random number, and yes, it can be 0. But I tried to run it more than 10 times, it keeps giving me 0 + 0, when it's suppose to random from 0-9.
The result of 0 + 0 is fine when I typed 0 as a result, it gets me out of the loop
Did I miss something to make the math library works? How can I fix the randomize issue?
Math.random() returns a double value between 0 (inclusive) and 1 (exclusive). It does not return an integer value. Therefore, when you take the number produced by Math.random() modulo 10, it returns the same double value. The final cast to int makes that value always 0.
Run the following code to see for yourself:
double random = Math.random();
System.out.println(random); // for example 0.5486395326203879
System.out.println(random % 10); // still 0.5486395326203879
System.out.println((int) (random % 10)); // outputs 0
What you really want is to use a Random object and use Random.nextInt(bound). To have a random integer between 0 and 9, you can use:
Random random = new Random();
int value = random.nextInt(10);
Math.random();
Returns a double value with a positive sign, greater than or equal to 0.0 and less than 1.0.
This is the problem. You need to then multiply with 10, so you get a number between 0 and 10 and after that you can cast to int.
public static double random()
Returns a double value with a positive sign, greater than or equal to 0.0 and less than 1.0. Returned values are chosen pseudorandomly with (approximately) uniform distribution from that range.
(source)
It returns a double that satisfies 0.0 <= double < 1.0, therefore taking the modulo will always result in zero. I recommend the java.util.Random class to achieve what you need. Specifically Random.nextInt(int).
The Math.random() method returns a random double that is from 0 (inclusive) to 1 (exclusive). Performing % 10 doesn't affect this value, but casting it to an int truncates any decimal portion, always yielding 0.
If you want a random number from 0-9, you can multiply Math.random() by 10, instead of taking the remainder when divided by 10.
Alternatively, you can create a java.util.Random object and call nextInt(10).
Read the doc :
Returns a double value with a positive sign, greater than or equal to
0.0 and less than 1.0
Therefore, you will always get less than 1, and the cast to int will round it down to 0. Use, for example, a Random object which has a nextInt(int max) method :
Random rd = new Random();
int number1 = rd.nextInt(10);
int number2 = rd.nextInt(10);
The Math.random() method returns a Double value between 0 and 1, so you will never get a number greater or equal than 1, you will ever get a value that could be 0, but never 1. And as you are taking the residual from this value over 10, you will ever get a 0 as result.
Math.random() % 10 will always be 0, because the random method gives you 0 <= value < 1, and when the % 10 operation takes place, you will get 0.
Check here for more details (oracle documentation)
Math.random() returns a number greater or equal than 0 and less than 1.
What you are looking for is either
int number1 = (int)(Math.random()*10);
int number2 = (int)(Math.random()*10);
or
Random rand = new Random();
int number1 = rand.nextInt(10);
int number2 = rand.nextInt(10);
Also, to get random number from given range, use this for Math.random()
int number 3 = min + (int)(Math.random() * ((max - min) + 1))
and for random.nextInt()
int number 4 = random.nextInt(max - min) + min;
Math.random gives a double value between 0 (incl.)-1 (excl.)!

Sum the first N numbers in java

Hello, I am new to programming and I am trying to write a small program where it will calculate the sum for first N numbers. The problem is it does not work for even numbers. I have not managed to figure out why.
My code is as follow:
int n = Integer.parseInt(args[0]);
int sum = (1+n)/2*n;
System.out.println(sum + " is the sum of first " + n + " numbers");
It doesn't work for even n because (n+1)/2 is truncated to an int.
This means that if, for example, n=4, (n+1)/2 results in 2 instead of 2.5, so when you multiply it by n, you get 8 instead of the desired 10.
You can overcome this problem simply by changing the order of the operations. If you first multiply n by (n+1), the result is guaranteed to be even, so dividing it by 2 will produce the correct answer.
int sum = n*(1+n)/2;
You have integer division with (1+n)/2. If your number is even, then (1+n) is odd and the division by 2 will truncate any decimal result, so that an int divided by an int is still an int.
Multiply by n first, then divide by 2. This ensures that the product is even before dividing, so the result is correct.
int sum = (1+n) * n / 2;
One can use ((n * (n + 1)) / 2). But I think the following will work without overflow errors for a few additional values of n:
if ((n & 1) == 0) {
sum = ((n >> 1) * (n + 1));
} else {
sum = (n * ((n + 1) >> 1));
}

generating function arguments in java

I'm very new to java and am working on my first Android app. I am using the webview demo as a template. I am trying to generate a random integer between 1 and 12 and then call a certain javascript function based on the result. Here's what I have:
int number = 1 + (int)(Math.random() * ((12 - 1) + 1));
number = (int) Math.floor(number);
String nextQuote = "javascript:wave" + number + "()";
mWebView.loadUrl(nextQuote);
So mWebView.loadUrl(nextQuote) will be the same as something like mWebView.loadUrl("javascript:wave1()")
I just want to know if what I have here is correct and will work the way I think it will. The application isn't responding as expected and I suspect this bit of code is the culprit.
The key statement are as follows:
int number = 1 + (int)(Math.random() * ((12 - 1) + 1));
number = (int) Math.floor(number);
The first statement gives the answer you need, but in a rather cumbersome way. Lets step through what happens:
((12 - 1) + 1) is 12. (This is evaluated at compile time ... )
Math.random() gives a double in the range 0.0D <= rd < 1.0D.
Math.random() * 12 gives a double in the range 0.0D <= rd < 12.0D.
The (int) cast converts the double to an int by rounding towards zero. In other words (int)(Math.random() * 12) will be a integer in the range 0 <= ri <= 11.
Finally you add 1 giving an integer in the range 1 <= ri <= 12.
W**5 :-)
A simpler and clearer version would be:
private static Random rand = new Random();
...
int number = 1 + rand.nextInt(12);
The second statement is (as far as I can tell) a noop. It implicitly converts an int to a double, gets the double form of largest integer that is less or equal to that double, and converts that back to an int. The result will always be identical to the original int.
Documention of Java Random Class
http://java.sun.com/j2se/1.4.2/docs/api/java/util/Random.html
A good way to do this.
Random rand = new Random(); // does not have to be static but can be.
int number = rand.nextInt(12) + 1; // 1 to 12 Must use 12
// Range is 0-11 add 1: 1-12
String nextQuote = "javascript:wave" + number + "()";
mWebView.loadUrl(nextQuote);
** from Java doc ** Method: public int nextInt(int n)
"Returns a pseudorandom, uniformly distributed int value between 0 (inclusive) and the specified value (exclusive)"

Categories