How to generate a random `bigint` number between two `bigint` numbers in TypeScript?

230 Views Asked by At

I have saw this but it does not provide an answer.

I would want something like this:

let randomBigInt = generateRandomBigInt(0, 920392947394829032030943);
console.log(randomBigInt); // 4893848594594
3

There are 3 best solutions below

4
fillybon On

The Math.random() static method returns a floating-point, pseudo-random number that's greater than or equal to 0 and less than 1, with approximately uniform distribution over that range — which you can then scale to your desired range. mdn web docs

You can generate a random float, which will be between 0 and 1. You can then multiply it to be in the BigInt limits. Passing it in the const bigIntValue = BigInt(yourRandomMultipliedValue) will convert it into a bigint type.

0
spender On

Here's a solution using crypto.getRandomValues as the random number source to generate random bigint over a nearly arbitrarily large (524288 bit) range without losing precision.

Of course, it rapidly becomes infeasible to test that it is non-skewed.

function mask(arr: Uint8Array, numBits: number) {
    let idx = 0;
    let b = numBits;
    while (idx < arr.length) {
        arr[idx] = 
            b <= 0 ? 0 : b > 0 && b < 8 ? arr[idx] & ((1 >> b) - 1) : arr[idx];
        ++idx;
        b -= 8;
    }
}
function getBitCount(v: bigint) {
    let x = v;
    let c = 0;
    do {
        ++c;
        x >>= 1n;
    } while (x > 0n)
    return c;
}
function bigRandomInRange(v1: bigint, v2: bigint) {
    const min = v1 > v2 ? v2 : v1;
    const r = v1 - v2;
    const range = r < 0 ? -r : r;
    if (range === 0n) {
        return min;
    }
    const bitCount = getBitCount(range);
    const byteCount = Math.ceil(bitCount / 8);
    const arr = new Uint8Array(byteCount);
    for (; ;) {
        crypto.getRandomValues(arr);
        mask(arr, bitCount);
        const v = arr.reduce(
            (acc, curr, i) => (1n << (BigInt(i) * 8n)) * BigInt(curr) + acc, 0n);
        if (v < range) {
            return min + v;
        }
    }
}

Playground Link

0
Dimava On
function randomBigInt(max: bigint | number | string): bigint;
function randomBigInt(min: bigint | number | string, max: bigint | number | string): bigint;
function randomBigInt(min: bigint | number | string, max?: bigint | number | string): bigint {
    if (max === undefined) return randomBigInt(0n, min);
    // choose if you want to allow not-bigints yourself
    // this wrapper allows `bigint | number | string` (you may want to keep at least `bigint | number`
    min = BigInt(min); 
    max = BigInt(max);

    // choose what happens when input order is invalid (e.g. throw error)
    // this wrapper keeps `min` in and `max` out of generatable numbers
    if (min <= max)
      return min + _generateRandomBigInt(max - min);
    else 
      return max - 1 - _generateRandomBigInt(min - max);
}

/**
 * This is tha basic function to generate random bigint.
 * @param max exclusive maximum
 * (c) <CC-BY-SA-4.0 or Unlicense> Dimava, 2023
 */
function _generateRandomBigInt(max: bigint): bigint {
    if (max < 0) throw new Error("generateRandomBigInt cannot generate negative BigInt");
    if (max === 0n) return 0n;

    const POWER = 64n;
    const FILTER = (1n << POWER) - 1n;

    // 1. Create an BigInt64Array representation of max number
    let num = max;
    const maxList: bigint[] = [];
    while (num) {
        maxList.unshift(num & FILTER);
        num >>= POWER;
    }
    const maxArray = BigInt64Array.from(maxList);

    // 2. Generate the random number
    const rndArray = crypto.getRandomValues(maxArray.slice());

    // 3. Trim the random number highest bits 
    // to reduce failure rate to <50%
    let trim = 1n;
    while (maxArray[0] > trim) trim <<= 1n;
    trim--;
    rndArray[0] &= trim;

    // 4. Convert to bigint
    let rnd = 0n;
    for (let i = 0; i < rndArray.length; i++) {
        rnd <<= POWER;
        rnd += rndArray[i];
    }

    // 5. Check for failure (the random number being higher then max)
    // and retry if needed
    if (rnd >= max) return _generateRandomBigInt(max);
    return rnd;
}

console.log(randomBigInt(1000n))
console.log(randomBigInt(1000n, 2000n))
console.log(randomBigInt(-1000n, 0n))