Error with calendar class and generating numbers - java

I'm trying to do random number generation using the given date to create good pseudo-random numbers in Java. I decided to use the Calendar class and to count milliseconds in the day. This worked to some extent, but I can only get a different random value when I start the program. Running it any more times in the program will just give me the same number. I'm using Thread.sleep() to make sure that there is an actual difference on time, but I still get the same numbers.
Heres my method that I'm calling (from another class)
public long genRNG()
{
long mask = 0xFFFF000000000000L;
long randomValue = seed & mask;
seed = 0x5D588B656C078965L * cal.get(Calendar.MILLISECOND) + 0x0000000000269EC3;
return randomValue;
}
and here's my main method
public static void main(String[] args) throws InterruptedException
{
Seed key = new Seed();
for (int x = 0; x <=10; x++)
{
Thread.sleep(200);
System.out.println(key.genRNG());
}
}
and the given output:
-7389844038561562624
-7389844038561562624
-7389844038561562624
-7389844038561562624
-7389844038561562624
-7389844038561562624
-7389844038561562624
-7389844038561562624
-7389844038561562624

It seems you are setting mask to the same value each time and seed to the same value each time, so seed & mask yields the same value each time. A Calendar object does not automatically change its value after it has been instantiated — in other words it keeps the time it got when you constructed it (typically the time when it was constructed) until you explicitly change it. So one suggestion is to do reinitialize cal in each call to genRNG().
Here I have changed cal to a local variable:
long mask = 0xFFFF000000000000L;
long randomValue = seed & mask;
Calendar cal = Calendar.getInstance();
seed = 0x5D588B656C078965L * cal.get(Calendar.MILLISECOND) + 0x0000000000269EC3;
return randomValue;
Now I can get output like:
0
8430738502437568512
-2453898846963499008
2916080758722396160
3291568377654411264
-1326873040214032384
-951385421282017280
1212312724692795392
-3406128693175648256
-1298444067566256128
-5916885485434699776
The initial 0 comes from seed not having been initialized. I gather it’s not a problem in your code.
I don’t think you’re there yet, though. Calendar.get(Calendar.MILLISECOND) always returns a value in the interval 0 through 999, so you are getting up to 1000 different “random” values. Not a lot for storing in a long. You may get more for instance like this:
seed = 0x5D588B656C078965L * System.currentTimeMillis() + 0x0000000000269EC3;
If for some reason you want an object for the current time rather than just the long you get from System.currentTimeMillis(), if you can use Java 8, I suggest Instant.now().toEpochMilli(). It gives you the same long value, but Instant.now() gives you an object representing the current time, much like Calendar.getInstance(), only more modern and more versatile if you want to use it for other purposes.
Another issue is because of the mask your value will always end in 48 zeroes in binary representation (it’s easy to see all the values are even). Maybe this is as designed?
Also I suppose there is a reason why you are not just using java.util.Random.

Related

Add float to Java Calendar

I'm currently developing some functionality that needs to either subtract or add time to a Calendar class instance. The time I need to add/sub is in a properties file and could be any of these formats:
30,sec
90,sec
1.5,min
2,day
2.333,day
Let's assume addition for simplicity. I would read those values in a String array:
String[] propertyValues = "30,sec".split(",");
I would read the second value in that comma-separated pair, and map that to the relevant int in the Calendar class (so for example, "sec" becomes Calendar.SECOND, "min" becomes Calendar.MINUTE):
int calendarMajorModifier = mapToCalendarClassIntValues(propertyValues[1]);
To then do the actual operation I would do it as simple as:
cal.add(calendarMajorModifier, Integer.parseInt(propertyValues[0]));
This works and it's not overly complicated. The issue is now floating values (so 2.333,day for eaxmple) - how would you deal with it?
String[] propertyValues = "2.333,day".split(",");
As you can imagine the code becomes quite hairy (I haven't actually written it yet, so please ignore syntax mistakes)
float timeComponent = Float.parseFloat(propertyValues[0]);
if (calendarMajorModifier == Calendar.DATE) {
int dayValue = Integer.parseFloat(timeComponent);
cal.add(calendarMajorModifier, dayValue);
timeComponent = (timeComponent - dayValue) * 24; //Need to convert a fraction of a day to hours
if (timeComponent != 0) {
calendarMajorModifier = Calendar.HOUR;
}
}
if (calendarMajorModifier == Calendar.HOUR) {
int hourValue = Integer.parseFloat(timeComponent);
cal.add(calendarMajorModifier, hourValue);
timeComponent = (timeComponent - hourValue) * 60; //Need to convert a fraction of an hour to minutes
if (timeComponent != 0) {
calendarMajorModifier = Calendar.MINUTE;
}
}
... etc
Granted, I can see how there may be a refactoring opportunity, but still seems like a very brute-forceish solution.
I am using the Calendar class to do the operations on but could technically be any class. As long as I can convert between them (i.e. by getting the long value and using that), as the function needs to return a Calendar class. Ideally the class also has to be Java native to avoid third party licensing issues :).
Side note: I suggested changing the format to something like yy:MM:ww:dd:hh:mm:ss to avoid floating values but that didn't pan out. I also suggested something like 2,day,5,hour, but again, ideally needs to be format above.
I'd transform the value into the smallest unit and add that:
float timeComponent = Float.parseFloat(propertyValues[0]);
int unitFactor = mapUnitToFactor(propertyValues[1]);
cal.add(Calendar.SECOND, (int)(timeComponent * unitFactor));
and mapUnitToFactor would be something like:
int mapUnitToFactor(String unit)
{
if ("sec".equals(unit))
return 1;
if ("min".equals(unit))
return 60;
if ("hour".equals(unit))
return 3600;
if ("day".equals(unit))
return 24*3600;
throw new InvalidParameterException("Unknown unit: " + unit);
}
So for example 2.333 days would be turned into 201571 seconds.

System.currentTimeMillis() to int returns negative value

I am developing an app in which I am adding views dynamically and assigning an unique id using but it is returning negative value:
long time = System.currentTimeMillis();
view.setId((int)time);
So I searched on google and found another solution but however it also doesn't work. It also returns negative value:
Long time = System.currentTimeMillis();
view.setId(time.intValue());
So how can I convert long value returned by System.currentTimeMills() to int safely?
System.currentTimeMills() returns 1505645107314 while converting to int returns -1888413582.
The value will be changed when a large long value is casted to int. You may want to divided the time by 1000 to get time in seconds, or subtract the value from time of 1 day ago (depending upon the uniqueness you prefer) and use it as the id of the view.
view.setId((int)(time/1000)); //this gives unique id for every second.
Edit
Use following code to get unique id for every millisecond:
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, -1);
long yesterday = calendar.getTimeInMillis();
int uniqueId = (int) (yesterday - System.currentTimeMillis());
currentTimeMillis returns a long, which can sometimes not fit into an int. Therefore, I don't think you should use this as a way to set unique int ids for views.
If you want a unique int for each of the views you create, try this approach, create a static counter thingy:
static int nextViewID = 0;
When you create a new view, you just
view.setId(nextViewID);
nextViewID++;
If you want a random unique integer, you can do something like this:
Create a static Set
Generate a random integer using java.util.Random
Try to add the number to the set
Keep generating another integer until you successfully added the number to the set
This is normal. Casting from a large data type (such as long) to a smaller data type (like int), in some cases, might lead to a change in value. You can devise other means by dividing by a certain preset constant (say 10,000). For example:
Long t = System.currentTimeMillis() / 10000;
view.setId((int) t);
This would surely give you a positive value!
You can't do it. Why do you use time as unique id? Bad approach.
For instance, 2147483648 would be represented as -2147483648. For small values, casting is enough:
long l = 42;
int i = (int) l;
However, a long can hold more information than an int, so it's not possible to perfectly convert from long to int, in the general case.
You can use Hash values within integer limit as unique id.

How can I maintain probability across multiple executions in Java

Firstly I am not the greatest with Math, so please excuse any ignorance relating to that. I am trying to maintain probability based randomness across multiple executions but I am failing. I have this input in a JSONObject
{
"option1": 25,
"option2":25,
"option3" :10,
"option4" :40
}
This is my function that selects a value from the above JSONObject based on the probability assigned:
public static String selectRandomoptions(JSONObject options) {
String selectedOption = null;
if (options != null) {
int maxChance = 0;
for (String option : options.keySet()) {
maxChance += options.getInt(option);
}
if (maxChance < 100) {
maxChance = 100;
}
Random r = new Random();
Integer randomValue = r.nextInt(maxChance);
int chance = 0;
for (String option : options.keySet()) {
chance += options.getInt(option);
if (chance >= randomValue) {
selectedOption = options.toLowerCase();
break;
}
}
}
}
the function behaves within a reasonable error margin if I call it x amount of times in a single execution ( tested 100+ calls), the problem is that I am running this every hour to generates some sample data in an event-driven app to verify our analytics process/data but we need it to be somewhat predictable, at least within a reasonable margin?
Has anyone any idea how I might approach this? I would rather not have to persist anything but I am not opposed to it if it makes sense or reduces complexity/time.
The values returned by Random.nextInt() are uniformly distributed, so that shouldn't be a problem.
I you would like to make random results repeatable, then you may want to use Random with seed.
Rather than create a new Random() object each time you want a new random number, just create the Random object once per run, and use the Random.nextInt() object once per run.
Looking at the documentation of Random() constructor,
This constructor sets the seed of the random number generator to a
value very likely to be distinct from any other invocation of this
constructor.it only guarantees it to be different
that's a bit of a weaker contract than the number you get from nextInt().
If you want to get the same sequence of numbers on each run, use the Random(long seed) or the setSeed(long seed) method of the random object. Both these methods set the seed of the generator. If you used the same seed for each invocation it's guaranteed that you will get the same sequence of numbers from the generator.
Random.setSeed(long).

Boolean expression -- Java

I am writing a Java code with displays a histogram of rainfall (in millimeter) on days 0 to 6, ie. day0 is Monday and day6 is Sunday. The code is I have written for the average rainfall over 7 days is the following:
public float averageRain() {
int d;
float total = 0.0f;
for (d = 0; d < days; d++)
total=total + rain[d];
return(total / days);
}
But I am stymied on how to solve the following: If you enter a day number which is less than 0 or greater than 6, you get an error message. My task is to modify a method called "addRain" to check whether the day is in range (i.e. 0 or greater, but less than constant days). If the day is valid, add RAINFALL to the total for the day and return true. Otherwise return false. By doing so, when I run the program, the invalid days are ignored. Also, I have to modify the code so that NEGATIVE rainfall values (eg. -3 mm) are NOT accepted. Could anyone tell me how to solve this matter? All I have done so far is this:
public boolean addRain(int day, float rainfall) {
rain[day] += rainfall;
return(true);
}
I assume days is a constant with value 7. Replace it with:
private static final int DAYS_IN_WEEK = 7;
Do you want to simply ignore invalid day numbers or rain values? Is this an interactive program? And, do you want to let people enter rain values twice for a single day? I can see someone entering 10mm for Wednesday and then trying to correct it and enter 5mm. Currently, there is no way to do so. If you know how to use exceptions, you can have your method throw java.lang.IllegalArgumentException and report it to the user so he can try again:
public void setRainForDay(int day, float rainfall) {
if (day < 0 || day >= DAYS_IN_WEEK) {
// Throw an IAE.
}
if (rainfall < 0.0f) {
// Throw an IAE.
}
rain[day] = rainfall; // Set, not add.
}
The calling routine catches the IAE, reports it to the user, and the user can try again.
Swing
I had not realized that the application was supposed to be a Swing application. In that case, throwing exceptions is not the appropriate method. Instead, one wants to have a model class, one that holds the data, and one wants to have the view classes, and one wants to have controller classes that do the actual work. And, one wants to use PropertyChangeListeners and VetoableChangeListeners. When you type -10 into the rainfall JFormattedTextField, it sends a property change event to the controller, whig must then refuse to set the model class, instead vetoing the change. Actually, it will be simpler to write and apply an InputValidator. It's best not to give the user the opportunity to err.

Java: Unique 10 digit ID

I need to generate a unique 10 digit ID in Java. These are the restrictions for this ID:
Only Numeric
Maximum 10 digits
Possible to create up to 10 different IDs per second
Has to be unique (even if the application re-starts)
Not possible to save a number in the Database
As fast as possible NOT to add much lattency to the system
The best solution I found so far is the following:
private static int inc = 0;
private static long getId(){
long id = Long.parseLong(String.valueOf(System.currentTimeMillis())
.substring(1,10)
.concat(String.valueOf(inc)));
inc = (inc+1)%10;
return id;
}
This solution has the following problems:
If for any reason there is a need to create more than 10 IDs per seccond, this solution won't work.
In about 32 years this ID could be repeated (This is probably acceptable)
Any other solution to create this ID?
Any other problem I haven't thought of with mine?
Thanks for your help,
This is a small enhancement to yours but should be resilient.
Essentially, we use the current time in milliseconds unless it hasn't ticked since the last id, in which case we just return last + 1.
private static final long LIMIT = 10000000000L;
private static long last = 0;
public static long getID() {
// 10 digits.
long id = System.currentTimeMillis() % LIMIT;
if ( id <= last ) {
id = (last + 1) % LIMIT;
}
return last = id;
}
As it is it should manage up to 1000 per second with a comparatively short cycle rate. To extend the cycle rate (but shorten the resolution) you could use (System.currentTimeMillis() / 10) % 10000000000L or (System.currentTimeMillis() / 100) % 10000000000L.
This may be a crazy idea but its an idea :).
First generate UUID and get a string representation of it with
java.util.UUID.randomUUID().toString()
Second convert generated string to byte array (byte[])
Then convert it to long buffer: java.nio.ByteBuffer.wrap( byte
digest[] ).asLongBuffer().get()
Truncate to 10 digits
Not sure about uniqueness of that approach tho, I know that you can rely on uniqueness of UUIDs but haven't checked how unique are they converted and truncated to 10 digits long number.
Example was taken from JavaRanch, maybe there is more.
Edit: As you are limited to 10 digits maybe simple random generator would be enough for you, have a look into that quesion/answers on SO: Java: random long number in 0 <= x < n range
private static AtomicReference<Long> currentTime = new AtomicReference<>(System.currentTimeMillis());
public static Long nextId() {
return currentTime.accumulateAndGet(System.currentTimeMillis(), (prev, next) -> next > prev ? next : prev + 1) % 10000000000L;
}
What means that it has to be unique? Even across more currently running instances? It break your implementation.
If it must be unique across universe, the best solution is to use UUID as it's mathematically proven identifier generator as it generates unique value per universe. Less accurate number brings you collisions.
When there is only one concurrent instance, you can take current time in millis and solve 10ms problem using incrementation. If you sacrifice proper number of last positions in the number you can get many number within one milliseconds. I would than define the precision - I mean how much unique numbers do you need per seconds. You will solve the issue without any persistence using this approach.

Categories