Generate big numbers with Math random in Javascript - javascript

I need to generate 26 digit numbers with Math.random, but when I use this:
Math.floor(Math.random() * 100000000000000000000000000) + 900000000000000000000000000
I gets 9.544695043285823e+26

Modern browsers support BigInt and bigint primitive type and we can combine it with a random generated array containing 8 bytes (the sizeof bigint is 8 bytes (64 bits)).
1. Generating Random BigInt Performance Wise
We can generate a random hex string of 16 characters length and apply it directly to BigInt:
const hexString = Array(16)
.fill()
.map(() => Math.round(Math.random() * 0xF).toString(16))
.join('');
const randomBigInt = BigInt(`0x${hexString}`);
// randomBigInt will contain a random Bigint
document.querySelector('#generate').addEventListener('click', () => {
const output = [];
let lines = 10;
do {
const hexString = Array(16)
.fill()
.map(() => Math.round(Math.random() * 0xF).toString(16))
.join('');
const number = BigInt(`0x${hexString}`);
output.push(`${
number.toString().padStart(24)
} : 0x${
hexString.padStart(16, '0')
}`);
} while (--lines > 0);
document.querySelector('#numbers').textContent = output.join('\n');
});
<button id="generate">Generate</button>
<pre id="numbers"><pre>
2. Generating Random BigInt from random bytes array
If we want to use Uint8Array or if we want more control over the bits manipulation, we can combine Array.prototype.fill with Array.prototype.map to generate an array containing 8 random byte number values (beware this is around 50% slower than the above method):
const randomBytes = Array(8)
.fill()
.map(() => Math.round(Math.random() * 0xFF));
// randomBytes will contain something similar to this:
// [129, 59, 98, 222, 20, 7, 196, 244]
Then we use Array.prototype.reduce to initialize a BigInt of zero value and left shift each randum byte value its position X 8 bits and applying bitwise or to the current value of each reduce iteration:
const randomBigInt = randomBytes
.reduce((n, c, i) => n | BigInt(c) << BigInt(i) * 8n, 0n);
// randomBigInt will contain a random Bigint
Working example generating 10 random BigInt values
document.querySelector('#generate').addEventListener('click', () => {
const output = [];
let lines = 10;
do {
const number = Array(8)
.fill()
.map(() => Math.round(Math.random() * 0xFF))
.reduce((n, c, i) => n | BigInt(c) << BigInt(i) * 8n, 0n);
output.push(`${
number.toString().padStart(24)
} : 0x${
number.toString(16).padStart(16, '0')
}`);
} while (--lines > 0);
document.querySelector('#numbers').textContent = output.join('\n');
});
<button id="generate">Generate</button>
<pre id="numbers"><pre>

Floating point numbers in JavaScript (and a lot of other languages) can contain only about 15.955 digits without losing precision. For bigger numbers you can look into JS libraries, or concatenate few numbers as strings. For example:
console.log( Math.random().toString().slice(2, 15) + Math.random().toString().slice(2, 15) )

Your question seems to be an XY problem where what you really want to do is generate a sequence of 26 random digits. You don't necessarily have to use Math.random. Whenever one mentions randomness it's important to specify how that randomness is distributed, otherwise you could end up with a paradox.
I'll assume you want each of the 26 digits to be independently randomly chosen uniformly from each of the 10 digits from 0 to 9, but I can also see a common interpretation being that the first digit must not be 0, and so that digit would be chosen uniformly from numbers 1 to 9.
Other answers may tempt you to choose a random bigint value using what amounts to random bits, but their digits will not be randomly distributed in the same way, since their maximum value is not a power of 10. For a simple example consider that a random 4 bit binary value in decimal will range from 00 to 15, and so the second digit will have a 12/16(=75%) chance of being 0 to 5, though it should be 60%.
As for an implementation, there's many ways to go about it. The simplest way would be to append to a string 26 times, but there are more potentially efficient ways that you could investigate for yourself if you find the performance isn't adequate. Math.random has a roughly uniform distribution from 0 to 1, but by being double precision it only has 15 or so significant decimal digits to offer us, so for each call to Math.random we should be able to retrieve up to 15 out of 26 decimal digits. Using this fact, I would suggest the following compromise on ease of readability and efficiency:
function generate26Digits() {
const first13 = Math.floor(Math.random() * Math.pow(10, 13)).toFixed(0).padStart(13, "0");
const next13 = Math.floor(Math.random() * Math.pow(10, 13)).toFixed(0).padStart(13, "0");
return first13 + next13;
}
console.log(generate26Digits())
This solution is not cryptographically secure however, and so I will direct readers to use Crypto.getRandomValues if you need more security for this for some reason.
If you then want to do math on this number as well without losing precision, you will have to use bigint types as others have suggested.

If I use a 6 decillion LG about 72.9 million out of 300 but when I switch to 10 centillion it comes like this 10 ^ 999 - 99999 + 26 just like how 9 - 9 - 9 - 9 - googolplex is equal to 0 googolplex

Related

How to generate a fixed-length code from a set of integers of a specific bit count in JavaScript

Generate string from integer with arbitrary base in JavaScript received the following answer:
function parseInt(value, code) {
return [...value].reduce((r, a) => r * code.length + code.indexOf(a), 0);
}
function toString(value, code) {
var digit,
radix= code.length,
result = '';
do {
digit = value % radix;
result = code[digit] + result;
value = Math.floor(value / radix);
} while (value)
return result;
}
console.log(parseInt('dj', 'abcdefghijklmnopqrstuvwxyz0123456789+-'));
console.log(toString(123, 'abcdefghijklmnopqrstuvwxyz0123456789+-'));
console.log(parseInt('a', 'abcdefghijklmnopqrstuvwxyz0123456789+-'));
console.log(toString(0, 'abcdefghijklmnopqrstuvwxyz0123456789+-'));
I am interested something slightly different. Whereas this will generate the shortest code for the number, I would like to now generate a constant-length code based on the number of bits. I am not sure if this is also a complex radix solution as well.
Say I want to generate 8-bit codes using a 16-character alphabet. That means I should be able to take the first 4 bits to select 1 character, and the next 4 bits to select the second character. So I might end up with MV if my 16 character set was ABDHNMOPQRSTUVYZ. Likewise if I had a 16-bit range, I would have 4 character code, and 32-bit range would be an 8-character code. So calling code32(1, 'ABDHNMOPQRSTUVYZ') would give an 8 letter code, while code8(1, 'ABDHNMOPQRSTUVYZ') would give a 2 digit code.
How could that be implemented in JavaScript? Something along these lines?
code8(i, alpha) // 0 to 255 it accepts
code16(i, alpha) // 0 to 65535 it accepts
code32(i, alpha) // 0 to 2^32-1 it accepts
Likewise, how would you get the string code back into the original number (or bit sequence)?
This really comes down to changing toString so that:
It only accepts a code that has a length of a power of 2
It pads the result to a given number of "digits" (characters)
The actual number of digits you would use for a 16 bit number depends on the size of the code. If the code has 16 characters, then it can cover for 4 bits, and so an output of 4 characters would be needed. If however the code has 4 characters, then the output would need 8 characters. You can have cases where the match is not exact, like when you would have a code with 8 characters. Then the output would need 6 characters.
Here I have highlighted the changes to the toString method. My personal preference is to also put the value as last parameter to toString.
function toString(digitCount, code, value) { // <-- add argument digitCount
// Perform a sanity check: code must have a length that is power of 2
if (Math.log2(code.length) % 1) throw "code size not power of 2: " + code.length;
var digit,
radix = code.length,
result = '';
do {
digit = value % radix;
result = code[digit] + result;
value = Math.floor(value / radix);
} while (value)
return result.padStart(digitCount, code[0]); // Pad to the desired output size
}
console.log(toString(4, 'abcdefghijklmnop', 123));
console.log(toString(4, 'abcdefghijklmnop', 0));
console.log(toString(4, 'abcdefghijklmnop', 0xFFFF));
// You could define some more specific functions
const code8 = (code, value) => toString(Math.ceil(8 / Math.log2(code.length)), code, value);
const code16 = (code, value) => toString(Math.ceil(16 / Math.log2(code.length)), code, value);
console.log(code16('abcdefghijklmnop', 123));
console.log(code16('abcdefghijklmnop', 0));
console.log(code16('abcdefghijklmnop', 0xFFFF));
console.log(code8('abcdefghijklmnop', 123));
console.log(code8('abcdefghijklmnop', 0));
console.log(code8('abcdefghijklmnop', 0xFF));
EDIT: I just noticed that you required a decoder as well. It is easy to implement a non-optimal version too, while an optimal one can be implemented via go through each letter and accumulate their value times their weighs.
Is this what you want? I tested this code for bit=16 and bit=8, but when bit=32 the count of codewords becomes too large and hangs the devtools of the browser. It's only a demonstrative code and may need optimization if need to be applied in practical use...
function genCode(len, alpha){
let tmp = [...alpha];
for(let i = 1; i != len; ++i){
const ttmp = [];
tmp.forEach(te => {
[...alpha].forEach(e => {
ttmp.push(te + e);
});
});
tmp = ttmp;
}
return tmp;
}
function code(bits, i, alpha){
const len = Math.ceil(bits / Math.floor(Math.log2(alpha.length)));
return genCode(len, alpha)[i];
}
function decode(bits, c, alpha){
const len = Math.ceil(bits / Math.floor(Math.log2(alpha.length)));
const codes = genCode(len, alpha);
return codes.indexOf(c);
}
console.log(code(16, 2, "ABDHNMOPQRSTUVYZ"));
console.log(decode(16, "AAAD", "ABDHNMOPQRSTUVYZ"));
console.log(code(8, 255, "ABDHNMOPQRSTUVYZ"));
console.log(decode(8, "ZZ", "ABDHNMOPQRSTUVYZ"));

Multiply points of a string

Let's say that I have a list of points declared in this format: x1,y1 x2,y2
listOfPoints : string = "12.2, 13.0 198.2, 141";
What could I do to multiply by 1.5, for example, every number of this string ?
Do I need to iterate over the listOfPoints and extract a string every time that there is a ', ' or ' ', convert that string into a number, multiply it by 1.5 and reconvert it into a string to finally put it into a new string (newListOfPoints) ?
Or is there a different way to that more efficiently ?
Thank you.
Use a regular expression with a replacer function to match digits, possibly with decimals, and replace with those digits multiplied by the number you want:
const listOfPoints = "12.2, 13.0 198.2, 141";
const multiplied = listOfPoints.replace(
/\d+(?:\.\d+)?/g,
match => match * 1.5
);
console.log(multiplied);
Due to floating-point issues, some of the resulting numbers may have trailing digits. If you don't want that, you can round the multiplied number to a certain number of decimal places:
const listOfPoints = "12.2, 13.0 198.2, 141";
const multiplied = listOfPoints.replace(
/\d+(?:\.\d+)?/g,
match => Math.round(1000 * match * 1.5) / 1000
);
console.log(multiplied);

How to represent a integer by a chain of 8 bit numbers

I am wondering how you can represent an integer as a chain of 8-bit numbers.
That is, (and I don't know how to actually calculate this so the example is wrong, but should demonstrate the basic idea), say you have an integer of arbitrary size, meaning it can be extremely large like Math.pow(100, 100) ≈ 1e+200 or some small number like 1 or 0. You want to be able to compute that number by using a chain of 8-bit values. The question is how to do that.
For example, I can represent 255 like this:
[ 255, 1 ]
That is, our representation has the first value be 8-bits, and then the second value is also 8-bits, and we multiply them together as 255 * 1 = 255. Another case would be 256. We could represent that like this:
[ 255, 1, 1 ]
Meaning (255 * 1) + 1 = 256, but now we don't necessarily know what to add vs. what to multiply. Imagine a large number like 1589158915. I have no idea what it's array of values would be, but say they ended up being like this:
[ 15, 12, 1, 141, 12, 250 ]
And say that from those numbers we could compute the value like this:
15 * 12 + 1 * 141 / 12 - 250
That wouldn't be very ideal, unless perhaps the system also stored the operation with the value, like this:
[ 15, 2, 12, 0, 1, 2, 141, 3, 12, 1, 250 ]
But I have no idea where to begin to figure out how to calculate the number like this, or how to say "given some integer, figure out how to break it apart into some equation that will generate its value". That is the basis of the question. I guess (actually) this would probably be the best approach.
As a sidenote, you can't take the integer 1589158915 and just represent it like this:
[ 1589158914, 1 ] == (1589158914 + 1)
because 1589158915 is not an 8-bit value. This is why it's tricky. In addition, we don't want to do it like this:
[ 255, 255, 255, 255, ... ]
==
255 + 255 + 255 + 255 + ...
Because that would be very inefficient. The solution should be a compact representation.
So the question is, how to take an integer and create an equation from it that will generate its value, where each component of the equation can be represented as an 8-bit integer.
So, here's the conversion to and from the 256 system:
const To256 = x => {
var res = [];
while (x > 0) {
res.push(x % 256);
x = Math.floor(x / 256)
}
return res;
}
const From256 = x => {
var res = 0;
while (x.length) res = 256 * res + x.pop();
return res;
}
var t = 9483593483;
console.log(t);
t = To256(t);
console.log(t);
t = From256(t);
console.log(t);
Note that the array is in reverse order.
It sounds like you're essentially describing base-256 arithmetic, although in a kind of circuitous way. Given some list of integers [a, b, c, d] you could compute the number they represent as (parentheses added for clarity):
a*(256**3) + b*(256**2) + c*(256**1) + d*(256**0)
The power to use for the 256 base is determined by the position of the integer in the list. Using this scheme, your example number of 15809158915 could be represented as:
[3, 174, 76, 159, 3]
# 3*256**4 + 174*256**3 + 76*256**2 + 159*256**1 + 3*256**0
This can of course extend to any arbitrarily-sized list. Of course, you could always write out the list of integers in their hexadecimal form instead of base-10:
[0x3, 0xae, 0x4c, 0x9f, 0x03]
Taking things further, you could write the list out as a string with a single 0x prefix to identify the entire string as a hexadecimal number:
0x3ae4c9f03
And voila: you've now duplicated python's hex() function!
>>> hex(15809158915)
'0x3ae4c9f03'
And since python already supports arbitrarily-sized integers, you can represent really, really big numbers this way:
hex(861324987629387561923756912874369128734619287346921367419273)
'0x89378c76db3e9b3e808eea75a42d7ff7d43840e0f41cf43989L'
The "L" at the end of the string indicates that this is a "long" integer, meaning python has engaged its arbitrarily-sized integer support behind the scenes.
In summary, all modern PCs represent numbers internally as a chain of 8-bit integers. 32-bit ints are a chain of 4 8-bit integers, 64-bit ints are a chain of 8 8-bit integers, etc.
If you get a tiny bit fancier, you can represent negative numbers using twos-complement, or floating point numbers using IEEE-754. (Note that I'm ignoring endianness for now.)

Can I accurately calculate an average from a medium size set of 2 d.p. numbers using JavaScript?

I need to find the average of a set of values and after doing some reading am not sure if JavaScript is able to produce an accurate result or not.
Each value has a precision of 2 d.p. and there could be up to 10000 of them between -100000.00 and 100000.00. The result also needs to be to 2 d.p.
From what I can see it is usually the figures around the 16th decimal place that are inaccurate which means that I would have to average an extremely large set before affecting my result. Is the best way of doing it to simply sum all of the values, divide by the total number and then use a toFixed(2)?
You could take advantage of your 2dp prevision, and multiply all your numbers by 100 first, then do the mathematics using integers. EG, a float error occurs in this simple average (I am just using 1dp for this example):
(0.1 + 0.2) / 2
0.15000000000000002
But this works:
(0.1*10 + 0.2*10) / (2*10)
0.15
Some good reading here:
http://floating-point-gui.de/basic/
and here:
How to deal with floating point number precision in JavaScript?
and a really precise fix to do it using decimals is to use this:
https://github.com/dtrebbien/BigDecimal.js
Example for 2 dp:
var numbers = [0.10, 0.20, 0.30]
var simple_average = numbers.reduce(function(a, b) {
return a + b
}) / numbers.length
console.log(simple_average)
var smart_average = numbers.map(function(a) {
return a * 100
}).reduce(function(a, b) {
return a + b
}) / (numbers.length * 100)
console.log(smart_average)
This demo can be run here -- http://repl.it/e1B/1

Compressing a Hex String in JavaScript/NodeJS

My app generates links, which contain hex string like: 37c1fbcabbc31f2f8d2ad31ceb91cd8d0d189ca5963dc6d353188d3d5e75b8b3e401d4e74e9b3e02efbff0792cda5c4620cb3b1f84aeb47b8d2225cd40e761a5. I would really like to make them shorter, like the solution mentioned for Ruby in Compressing a hex string in Ruby/Rails.
Is there a way to do this in JavaScript/NodeJS?
node int-encoder does this, using the strategy already mentioned.
it also supports large numbers
npm install int-encoder
var en = require('int-encoder');
//simple integer conversion
en.encode(12345678); // "ZXP0"
en.decode('ZXP0'); // 12345678
//convert big hex number using optional base argument
en.encode('e6c6b53d3c8160b22dad35a0f705ec09', 16); // 'hbDcW9aE89tzLYjDgyzajJ'
en.decode('hbDcW9aE89tzLYjDgyzajJ', 16); // 'e6c6b53d3c8160b22dad35a0f705ec09'
You could use toString and parseInt method, that basically are doing the same thing of the methods you mentioned in the link:
var hexString = "4b3fc1400";
var b36 = parseInt(hexString, 16).toString(36); // "9a29mgw"
And to convert it back, you just need to do the opposite:
hexString = parseInt(b36, 36).toString(16); // "4b3fc1400"
The only problem with your string, is that is too big to be threat as number in JavaScript. You should split them in chunk. JavaScript's numbers are accurate up to 2^53 (plus sign), so the max positive number you can handle is 0x20000000000000 (in hexadecimal, that is 9007199254740992 in decimal); you can use the accuracy to handle the chunk:
var hexString = "37c1fbcabbc31f2f8d2ad31ceb91cd8d0d189ca5963dc6d353188d3d5e75b8b3e401d4e74e9b3e02efbff0792cda5c4620cb3b1f84aeb47b8d2225cd40e761a5"
var b36 = "", b16 = "";
var chunk, intChunk;
// 14 is the length of 0x20000000000000 (2^53 in base 16)
for (var i = 0, max = 14; i < hexString.length; i += max) {
chunk = hexString.substr(i, max);
intChunk = parseInt(chunk, 16);
if (intChunk.toString(16) !== chunk) {
intChunk = parseInt(hexString.substr(i, max - 1), 16);
i -= 1;
}
b36 += intChunk.toString(36)
}
// 11 is the length of 2gosa7pa2gv (2^53 in base 36)
for (var i = 0, max = 11; i < b36.length; i += max ) {
chunk = b36.substr(i, max);
intChunk = parseInt(chunk, 36);
if (intChunk.toString(36) !== chunk) {
intChunk = parseInt(b36.substr(i, max - 1), 36);
i -= 1;
}
b16 += intChunk.toString(16)
}
console.log(hexString);
console.log(b36);
console.log(b16);
Update: You could also use a base 62 instead of 36 to compress more, but notice that JS supports up to base 36, so you need to implement that personal notation manually (I believe there are already some implementation around).
The simplest and fastest thing to do is define a set of 64 safe characters for use in the URL, such as A-Z, a-z, 0-9, _, and $. Then encode every three hex digits (4 bits each) into two safe characters (6 bits each). This requires no multiplication and division, and it can be used on arbitrarily long strings.
You will need to pick a 65th character to use at the end of the string to indicate if the last four-bit piece is used or not. Otherwise you will have an ambiguity for character strings with an even number of characters. Let's call it 2n. Then there are either 3n-1 or 3n hex digits encoded within, but there is no way to tell which. You can follow the sequence with a special character to indicate one of those cases. E.g. a '.' (period).
Note: The last few characters picked here for the set differ from Base64 encoding, since URLs have their own definition of safe punctuation characters. See RFC 1738.

Categories

Resources