We know that there is no true random number. The best we get is pseudo-random number, which comes from a seed value. I was wondering if I could get a random number without using Math.random() function of JavaScript. So I developed an algorithm of my own, which is a few lines of code that works by keeping, updating, and shifting a seed array in the state. This ensures nearly equal distribution of the generated values for larger and larger test set.

Below is the code with explanation, followed by its performance test and comparison with Math.random().

## The Code

```
const getDateIntArr = () => Date.now().toString().split("").reverse().map(d => parseInt(d))
class MyRandom {
constructor() {
this.seed = getDateIntArr();
}
generate() {
const dateIntArr = getDateIntArr();
this.seed = dateIntArr.map((d, index) => {
let newVal = d + this.seed[index];
if (newVal > 9) {
newVal = newVal % 10;
}
return newVal;
});
this.seed.push(this.seed.shift());
return this.seed[0];
}
};
```

### The Code Explained

- We start with creating a class
`MyRandom`

and assigning`this.seed`

a value, which is`Date.now()`

in reversed array form and converted to integer. (Example:`Date.now()`

=`1654202603378`

,`this.seed`

=`[ 8, 7, 3, 3, 0, 6, 2, 0, 2, 4, 5, 6, 1 ]`

.) - The
`generate`

method creates a similar reversed array in`dateIntArr`

. We then add the same indexes values of`this.seed`

&`dateIntArr`

. If the sum is greater than 9 we consider the remainder after`%10`

. We assign the resulting array to`this.seed`

. - Lastly, shuffle
`this.seed`

array to the left by one, and return the`0`

index value, which will be anything from 0 to 9. The shuffled`this.seed`

is now the new seed value to be used in the next`generate`

call.

### Reasoning

- Reversing the arrays
`this.seed`

&`dateIntArr`

is not necessary, but then we’ll need to return`this.seed[this.seed.length]`

instead of`this.seed[0]`

. (also, we may want to change the array shift to the opposite direction.) - We return the first element because that’s the one most frequently changed in Date.now() after reversing, followed by second, third and so on. After addition to the previous seed and shifting, this should be the most unpredictable number out of the lot.

### Time Complexity

The time complexity will be O(1) i.e. constant, because though there are loops used but they won’t be dynamic and only go so far as 13, which is the length of `Date.now()`

.

## Performance Test and Comparison with Math.Random()

A reasonably good (pseudo)random generator needs to generate the numbers within a given range almost equally. If we generate a random number from 1 to 10 (like we did above), the percentage of each value from 1 to 10 should be roughly 10%. That is, on generating a random number, say, a 1000 times, every number should ideally appear a 100 times.

To compare the performance of both, let’s run our own custom generator and `Math.random()`

for 10, 100, 1000, 10000, and 100000 times, and get the percentage of the occurrences of 0 to 9. The script below gets us the percentage of each occurrence, given the number of times a random number is generated. (Change the value of TOTAL for the number of times you want to run the generators):

```
const sortObject = o => Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {});
const mathRandomHash = {};
const customRandomHash = {}
const TOTAL = 100;
const rand = new MyRandom();
for (let i = 0; i < TOTAL; i++) {
const customRand = rand.generate();
const mathRand = (Math.floor(Math.random() * 10));
if (!customRandomHash[customRand]) {
customRandomHash[customRand] = 1
}
else {
customRandomHash[customRand] += 1;
}
if (!mathRandomHash[mathRand]) {
mathRandomHash[mathRand] = 1
}
else {
mathRandomHash[mathRand] += 1;
}
}
const convertToPercentage = (hash, total) => {
return Object.keys(hash).forEach(e => {
hash[e] = Math.floor((hash[e] / total) * 100);
})
}
convertToPercentage(mathRandomHash, TOTAL);
convertToPercentage(customRandomHash, TOTAL);
console.log("Math Rand");
console.log(sortObject(mathRandomHash));
console.log("My Rand");
console.log(sortObject(customRandomHash));
```

### 10 Times

```
Math Rand
{ '1': 30, '2': 20, '3': 20, '4': 20, '5': 10 }
My Rand
{ '0': 20, '1': 10, '4': 10, '5': 10, '6': 30, '8': 10, '9': 10 }
```

### 100 Times

```
Math Rand
{
'0': 7,
'1': 10,
'2': 10,
'3': 10,
'4': 9,
'5': 13,
'6': 12,
'7': 10,
'8': 10,
'9': 9
}
My Rand
{
'0': 11,
'1': 7,
'2': 7,
'3': 9,
'4': 10,
'5': 11,
'6': 10,
'7': 11,
'8': 12,
'9': 12
}
```

### 1000 Times

```
Math Rand
{
'0': 9,
'1': 8,
'2': 11,
'3': 11,
'4': 9,
'5': 10,
'6': 10,
'7': 10,
'8': 9,
'9': 10
}
My Rand
{
'0': 9,
'1': 11,
'2': 9,
'3': 11,
'4': 8,
'5': 10,
'6': 9,
'7': 10,
'8': 9,
'9': 10
}
```

### 10000 Times

```
Math Rand
{
'0': 10,
'1': 9,
'2': 9,
'3': 9,
'4': 9,
'5': 9,
'6': 10,
'7': 10,
'8': 10,
'9': 10
}
My Rand
{
'0': 10,
'1': 11,
'2': 10,
'3': 9,
'4': 9,
'5': 9,
'6': 11,
'7': 9,
'8': 9,
'9': 9
}
```

### 100000 Times

```
Math Rand
{
'0': 9,
'1': 9,
'2': 10,
'3': 10,
'4': 9,
'5': 9,
'6': 10,
'7': 10,
'8': 10,
'9': 9
}
My Rand
{
'0': 9,
'1': 10,
'2': 10,
'3': 9,
'4': 9,
'5': 10,
'6': 9,
'7': 9,
'8': 10,
'9': 9
}
```

From the above data, we can observe that both `Math.random()`

and our custom random number generator start off with pretty uneven distribution of generated numbers from 1 to 10, when run for merely 10 or 100 times. But as we keep on increasing the number, the distribution gets even. For 100,000 runs, each number from 1 to 10 appears almost 10% of the time.

#### See also

- SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.
- Yup Date Format Validation With Moment JS
- Yup Number Validation: Allow Empty String
- Exactly Same Query Behaving Differently in Mongo Client and Mongoose
- JavaScript Unit Testing JSON Schema Validation
- Reduce JS Size With Constant Strings
- JavaScript SDK