Is this JS Unique ID Generator Unreliable? (Getting collisions) - javascript

I am using the following JS function to generate unique IDs, which I got from another StackOverflow thread:
function generateUniqueID() {
return Math.round(new Date().getTime() + (Math.random() * 100));
}
I see that it combines the current Date/Time with an additional Randomizer.
Nonetheless, I verified that I'm getting collisions on every 4th or 5th operation of quickly adding items with IDs.
The function is called inside a JS loop to generate IDs from the list of current elements.
jQuery.each(mainEvents, function(index, item) {
// ...
// Generate gaps
gapEvents.push({"gapEventID" : "event-GAP" + generateUniqueID(),
"other" : other });
}
Is this function unreliable? Could it allow collisions in quick JS loop iterations?
I've pretty much ruled out "outside causes" (i.e. that this function isn't the culprit but something else could be), but if that's the case, I can't understand why Math.random() wouldn't keep me safe.

Very much so. You can use new Date().getTime() to get a unique id under the assumption that it takes longer than 1ms for each iteration. As you can tell from your data, that is false. Combined with an RNG that uses Math.floor, it's very possible to get repeated values. You will get repeat times whenever the interval is < 1ms. If you want unique IDs based around the concept of an RNG, I'd say just using Math.random() to the 10^15 is a better choice. 10^15 is the max size integer digit length that will never go past Number.MAX_SAFE_INTEGER.
Math.floor(Math.random() * Math.pow(10, 15))

Is this function unreliable?
In my opinion it really is.
Infact new Date().getTime() is an integer number that increases by 1 each millisecond, while Math.random() * 100 is a pseudo random number that gives a number in a range from 0 to 99.
So their sum might really repeat often if the function is called many times rapidly.
Think if the function is called two times per millisecond, it becomes very likely to have the same number twice. It has 1/100 of probability to happen (and these are pretty much the results I'm getting considering that I'm generating a list of 10.000 ids in about 1 second using that function and getting ~100 duplicates, which is pretty consistent as order of magnitude)

A random value is never a unique value. Even with a timestamp you can't guarantee that the outcome is 100% unique. However, you could minimize this by generating a larger (random) value.
Using a timestamp however, you cover yourself when there are multiple pushes at the same time. Using an extra random will create an almost unique value which, in most use cases is unique.
I'd suggest though to make that random value longer. Or create a GUID instead.
Create GUID / UUID in JavaScript?

Based on the responses the following compares the suggested methods.
But I think I'm going in the wrong direction for my needs. I will be using the ID/Sequence on the server-side to ensure uniqueness.
function run() {
var nums1 = new Set(), nums2 = new Set(), nums3 = new Set();
for (var i = 0; i < 10000; i++) {
nums1.add(originalMethod());
}
for (var i = 0; i < 10000; i++) {
nums2.add(concatMethod());
}
for (var i = 0; i < 10000; i++) {
nums3.add(random10To18thMethod());
}
console.clear();
console.log('Original Method set: ' + nums1.size);
console.log('Concat Method set: ' + nums2.size);
console.log('Math.Random 10^18 set: ' + nums3.size);
function originalMethod() {
return Math.round(new Date().getTime() + (Math.random() * 100));
}
function concatMethod() {
return Math.round(new Date().getTime() + '' + (Math.random() * 100));
}
function random10To18thMethod() {
return Math.random() * Math.pow(10, 18);
}
}
<button onclick="run()">Run Algorithms</button>

Related

Get a specific percentage chance of running something?

I read some threads on the site about getting percentages of running a function, but none of them guide me how to get a specific decimal percentage of running a function.
I tried something like:
Math.floor(Math.random() * 100) + 1 + '%'
However it doesn't return decimals.
What i'd like is for it to let's say, have a 0.5% / 100% chance of running console.log("0.5%"), is there any way to do this? Thank you very much!
Math.random() returns a random number between 0 and 1.
A percentage is just a fraction out of 100. Divide by 100 to get a number between 0 and 1.
So, to get a block of code that runs a certain percentage of the time, take the desired percentage, divide it by 100, and run the code if a random number is less than it.
if( Math.random() < 0.5/100) {
/* code that runs 0.5% of the time */
}
Here is what you are asking for.
The percentageFromRandom is explained in my comment above.
The runWithProbability function calls a given function with a certain probability, when this function is called.
The logWithProbability uses the runWithProbability function, but with the custom console.log functionality as your answer question for.
The init function shows an example of usage of the function, by running it 30 times with 30 random probability. In most cases it would log the larger %'s as they are more likely to have the console.log function be called.
//convert the random value into human readable percentage
function percentageFromRandom(value, fractionalDigits = 2){
return (value*100).toFixed(fractionalDigits)+'%';
}
//take a function and probability of running it
//if the probability is met, call the function.
function runWithProbability(fn, probability){
if(probability >= 1 || Math.random() < probability){
return fn(probability);
}
return false;
}
//make a console log with a certain probability,
//log the percentage probability if called
function logWithProbability(probability){
runWithProbability(()=>
console.log(percentageFromRandom(probability))
, probability);
}
// See console logs and their probability as
// a percentage of running.
const init = () => {
for(let i = 0; i < 30; i++){
logWithProbability(Math.random());
}
}
init();
The issue with the Math.floor(Math.random()) example is that Math.floor() removes all of the fractional values of a number. To get precision to a certain fixed point, multiply by the maximum whole number wanted, then adjust to a fixed decimal.
for (var i = 0; i < 10; i++) {
var num = 10 * Math.random(); // Max 10.000...
console.log(num, num.toFixed(1) + '%') // Fix (and round) the first decimal
}

How to create a unique value each time when ever I run the java-script code?

I am using Math.random to create a unique value.
However , it looks like after some days , if i run the same script it produces the same value that created earlier.
Is there any way to create unique value every time when ever i run the script.
Below is my code for the random method.
var RandomNo = function (Min,Max){
return Math.floor(Math.random() * (Max - Min + 1)) + Min;
}
module.exports = RandomNo;
The best way to achieve a unique value is to use Date() as milliseconds. This increasing time representation will never repeat.
Do it this way:
var RamdomNo = new Date().getTime();
Done.
Edit
If you are bound to length restrictions, the solution above won't help you as repetition is predictable using an increasing number the shorter it gets.
Then I'd suggest the following approach:
// turn Integer into String. String length = 36
function dec2string (dec) {
return ('0' + dec.toString(36)).substr(-2);
}
// generate a 20 * 2 characters long random string
function generateId () {
var arr = new Uint8Array(20);
window.crypto.getRandomValues(arr);
// return 5 characters of this string starting from position 8.
// here one can increase the quality of randomness by varying
// the position (currently static 8) by another random number <= 35
return Array.from(arr, this.dec2string).join('').substr(8,5);
}
// Test
console.log(generateId());
This pair of methods generates a 40 characters long random string consisting of letters and digits. Then you pick a sequence of 5 consecutive characters off it.

Timestamp with Random Number in JS

This is a follow-up question to the one I asked previously, Is this JS Unique ID Generator Unreliable? (Getting collisions).
In the scriptlet below I'm generating 10000 random numbers using 2 methods. Method 1 is a straight random number up to 10^6, while Method 2 concatenates that random number up to 10^6 (same idea as in [1]) with the current JS Date().time() timestamp. Also there's Method [3] which only does the Math.round on the RNG rather than the whole concatenated result.
My question is, if you keep clicking the test button, you see that [1] always produces 10000 unique numbers but [2] produces ~9500 no matter what. [3] produces ~9900 but never the max either. Why is that? The chances of getting a +/-1 from a previous random number in [0..10^6] and having that mixed with the timestamp of exactly the opposite +/-1 for the timestamp concatenation are impossible. We are generating pretty much on the same millisecond in a loop. 10^6 is a huge limit, much bigger than in my original question, and we know that's true because Method [1] works perfectly.
Is there truncation of some kind of going on, which trims the string and makes it more likely to get duplicates? Paradoxically, a smaller string works better than a larger string using the same RNG inside it. But if there's no truncation, I would expect results to be 100% as in [1].
function run() {
var nums1 = new Set(), nums2 = new Set(), nums3 = new Set();
for (var i = 0; i < 10000; i++) {
nums1.add(random10to6th());
}
for (var i = 0; i < 10000; i++) {
nums2.add(random10to6th_concatToTimestamp());
}
for (var i = 0; i < 10000; i++) {
nums3.add(random10to6th_concatToTimestamp_roundRNGOnly());
}
console.clear();
console.log('Random 10^6 Unique set: ' + nums1.size);
console.log('Random 10^6 and Concat to Date().time() Unique set: ' + nums2.size);
console.log('Random 10^6 and Concat to Date().time(), Round RNG Only Unique set: ' + nums3.size);
function random10to6th() {
return Math.random() * Math.pow(10, 6);
}
function random10to6th_concatToTimestamp() {
return Math.round(new Date().getTime() + '' + (Math.random() * Math.pow(10, 6)));
}
}
function random10to6th_concatToTimestamp_roundRNGOnly() {
return new Date().getTime() + '' + Math.round(Math.random() * Math.pow(10, 6));
}
<button onclick="run()">Run Algorithms</button>
<p>(Keep clicking this button)</p>
Is there truncation of some kind of going on, which trims the string
and makes it more likely to get duplicates?
Yes, simply by rounding a random number, you cut off the fractional digits. This reduces the number of possible outcomes compared to the non-rounded random number.
In addition to that, you concatenate a timestamp (13 digits) with a value between 0 and 1000000 (1 to 7 digits). So your concatenated result will have a total number of 14 to 20 digits, but JavaScript's number datatype is of limited precision and represents integers faithfully up to about 16 digits only (see Number.MAX_SAFE_INTEGER).
Example: Let's assume the timestamp is 1516388144210 and you append random numbers from 500000 to 500400:
+'1516388144210500000' == 1516388144210500000
+'1516388144210500100' == 1516388144210500000
+'1516388144210500200' == 1516388144210500000
+'1516388144210500300' == 1516388144210500400
+'1516388144210500400' == 1516388144210500400
You can see that, when converting those strings to numbers, they get rounded to the nearest available IEEE-754 double-precision (64 bit) number. This is because 1516388144210500000 > Number.MAX_SAFE_INTEGER.
I think there are a number of issues in play here. I don't know which or to what degree each of the items below contribute to the observed difference, just that they are things that might explain the results.
One is because you're concatenating a number with a string with a number and then coercing the value back to a number as part of rounding the result. It would be very easy to feed unexpected results into the Round function (which in itself might cause collisions due to floating precision and such outlined below)
Second, I think that you actually reduce the randomness of the resulting number when you concatenate the timestamp. The function is likely to be called many, many times every second; if it's invoked at a rate > Date.getTime() precision the value returned will be identical to one generated in a previous loop iteration.
Third, unless I missed something, have you considered that the random number gen is only guaranteed to be pseudo-random? Precision and digit limits play a factor when dealing with large values like in the code you posted. Since the random-est part of the number is tacked onto the least significant part, it is more likely to be truncated, chopped, or modified.
Try inverting your concatenation and see the results (there's only about 4 or so collisions). The collisions are accounted for by the reasons outlined by me and #le_m's answers.
function run() {
var nums1 = new Set(), nums2 = new Set()
for (var i = 0; i < 10000; i++) {
nums1.add(random10to6th());
}
for (var i = 0; i < 10000; i++) {
nums2.add(random10to6th_concatToTimestamp());
}
console.clear();
console.log('Random 10^6 Unique set: ' + nums1.size);
console.log('Random 10^6 and Concat to Date().time() Unique set: ' + nums2.size);
function random10to6th() {
return Math.random() * Math.pow(10, 6);
}
function random10to6th_concatToTimestamp() {
return Math.round((Math.random() * Math.pow(10, 6)) + '' + new Date().getTime());
}
}
<button onclick="run()">Run Algorithms</button>
<p>(Keep clicking this button)</p>

Time Complexity - Bad Recursion - British Change Combinations

I recently came up with a naive (+ poor) solution to the British Change Problem (i.e. how many combinations of coins can generate a given total). I have a better solution now, but was still interested in solving the time and space complexity of the two solutions below.
Worst Solution
This solution recursively tries combining every number against itself and every other number, resulting in a lot of duplicate work. I believe it's O(n^n) time and not sure how to measure space complexity (but it's huge, since we're storing every result). Thoughts?
var makeChange = function(total){ // in pence
var allSets = new Set();
var coins = [1,2,5,10,20,50,100,200];
var subroutine = (arr, total) => {
if(total < 0){ return; }
if(total === 0){
allSets.add(''+arr);
} else {
// increase each coin amount by one and decrease the recursive total by one
for(var i = 0; i<coins.length; i++){
if((total - coins[i]) >= 0){
subroutine(arr.slice(0,i).concat(arr[i]+1).concat(arr.slice(i+1)), (total - coins[i]))
}
}
}
};
var zeros = new Array(coins.length).fill(0);
subroutine(zeros, total);
return allSets.size;
};
Improved Solution
This solution still has massive space complexity but I believe the time complexity has -improved- to O(n!) since we're recursing on smaller subsets of coins each time.
var makeChange = function(total){ // in pence
var allSets = new Set();
var coins = [1,2,5,10,20,50,100,200];
var subroutine = (arr, total, start) => {
if(total < 0){ return; }
if(total === 0){
console.log(''+arr);
allSets.add(''+arr);
} else {
// only solve for coins above start, since lower coins already solved
for(var i = start; i<coins.length; i++){
if((total - coins[i]) >= 0){
subroutine(arr.slice(0,i).concat(arr[i]+1).concat(arr.slice(i+1)), (total - coins[i]), i);
}
}
}
};
var zeros = new Array(coins.length).fill(0);
for(let i = 0; i<coins.length; i++){
subroutine(zeros, total, i);
}
return allSets.size;
};
Please help me to understand if my time/space complexity estimates are correct, and how to better estimate future problems like these. Thanks!
The complexity of the first algorithm is not actually an O(n^n). N is a variable which represents your input. In this case, I will refer to the variable "total" as your input, so N is based on total. For your algorithm to be O(n^n), it's recurrence tree would have to have a depth of N and a branching factor of N. Here, your depth of your recurrence is based on the smallest variable in your coins array. There is one branch of your recursion tree where you simply subtract that value off every time and recurse until total is zero. Given that that value is constant, it is safe to say your depth is n. Your branching factor for your recursion tree is also based off of your coins array, or the number of values in it. For every function call, you generate C other function calls, where C is the size of your coins array. That means your function is actually O(n^c) not O(n^n). Your time and space complexities are both based off of the size of your coins array as well as your input number.
The space complexity for your function is O(n^c * c). Every time you call your function, you also pass it an array of a size based on your input. We already showed that there are O(n^c) calls, and each call incorporates an array of size c.
Remember when analyzing the complexity of functions to take into account all inputs.

Javascript Convert numbers to different formats or string alternative

UPDATED:
Using javascript or jQuery, how can I convert a number into it's different variations:
eg:
1000000
to...
1,000,000 or 1000K
OR
1000
to...
1,000 or 1K
OR
1934 and 1234
to...
1,934 or -2K (under 2000 but over 1500)
or
1,234 or 1k+ (over 1000 but under 1500)
Can this is done in a function?
Hope this make sense.
C
You can add methods to Number.prototype, so for example:
Number.prototype.addCommas = function () {
var intPart = Math.round(this).toString();
var decimalPart = (this - Math.round(this)).toString();
// Remove the "0." if it exists
if (decimalPart.length > 2) {
decimalPart = decimalPart.substring(2);
} else {
// Otherwise remove it altogether
decimalPart = '';
}
// Work through the digits three at a time
var i = intPart.length - 3;
while (i > 0) {
intPart = intPart.substring(0, i) + ',' + intPart.substring(i);
i = i - 3;
}
return intPart + decimalPart;
};
Now you can call this as var num = 1000; num.addCommas() and it will return "1,000". That's just an example, but you'll find that all the functions create will involve converting the numbers to strings early in the process then processing and returning the strings. (The separating integer and decimal part will probably be particularly useful so you might want to refactor that out into its own method.) Hopefully this is enough to get you started.
Edit: Here's how to do the K thing... this one's a bit simpler:
Number.prototype.k = function () {
// We don't want any thousands processing if the number is less than 1000.
if (this < 1000) {
// edit 2 May 2013: make sure it's a string for consistency
return this.toString();
}
// Round to 100s first so that we can get the decimal point in there
// then divide by 10 for thousands
var thousands = Math.round(this / 100) / 10;
// Now convert it to a string and add the k
return thousands.toString() + 'K';
};
Call this in the same way: var num = 2000; num.k()
Theoretically, yes.
As TimWolla points out, it requires a lot of logic.
Ruby on Rails have a helper for presenting time with words. Have a look at the documentation. The implementation for that code is found on GitHub, and could give you some hint as how to go about implementing this.
I agree with the comment to reduce the complexity by choosing one format.
Hope you find some help in my answer.

Categories