1

I found this which I made into this below, for 8 and 16 bit numbers in JavaScript:

const fetch = (x, o) => {
  if (x >= o) {
    return x
  } else {
    const v = (x * x) % o
    return (x <= o / 2) ? v : o - v
  }
}

const fetch16 = (x) => fetch(x, 65519)
const fetch8 = (x) => fetch(x, 251)

// the last number can be anything.
const build16 = (x, o) => fetch16((fetch16(x) + o) ^ 42703)
const build8 = (x, o) => fetch8((fetch8(x) + o) ^ 101)

let i = 0
let invalid = []
while (i < 255) {
  let j = 0
  while (j < 255) {
    let x = build8(i, j)
    if (x > 255) {
      invalid.push([ i, j, x ])
    }
    j++
  }
  i++
}

console.log(JSON.stringify(invalid))

However, while the fetch8 and fetch16 functions properly cycle through the entire set of numbers before repeating, the build8 and build16 functions don't, they go outside of the desired range, see the output from the above code. For example, when i = 11 and j = 184, x = 340, which is > 255.

However, the output from these build8 and build16 functions is fantastic. It appears entirely random and doesn't repeat any values before going through the whole set.

How can I modify these build8 and build16 functions so that they only include numbers within the set (0-255, or 0-65535), yet appear entirely random like they do here, and yet never repeat a value before going through all of them?

I'm not entirely sure how the author of the post landed on fetch8((fetch8(x) + o) ^ 101) for example, doing that XOR and passing in values like this. But the end result appears very random. I just would like to make it so the output is:

  • Within the desired range of values.
  • Doesn't repeat any value until all values have been enumerated.
  • Appears entirely random like build8 does.
Lance
  • 75,200
  • 93
  • 289
  • 503
  • I find it confusing that `buildXX` takes two arguments and that you vary them both, giving many more combination possibilities that if can generate results. Why do you have 2 dimensions here and not 1? – trincot Jan 23 '21 at 20:38
  • "*I'm not entirely sure how the author of the post landed on `fetch8((fetch8(x) + o) ^ 101)` for example*" - you mean the author of [that post](https://preshing.com/20121224/how-to-generate-a-sequence-of-unique-random-integers/)? I can't find any code resembling `build8` on that page. – Bergi Jan 04 '22 at 19:55

1 Answers1

1

The reason you get out of range values is that although fetchXX will produce a value in range, the + o spoils this property. The XOR operation may sometimes bring it back in range, but not always.

So you should take the modulo of the value after + o. The XOR operation will never bring it out of range, so that can stay as it is.

Secondly, to test whether no duplicates are generated, you would need to fix one of the two arguments passed to the buildXX function and only vary the other. It seems more logical to me to freeze the second argument.

So this is what it would look like:

const fetch = (x, o) => {
  if (x >= o) {
    return x
  } else {
    const v = (x * x) % o
    return (x <= o / 2) ? v : o - v
  }
}

const fetch16 = (x) => fetch(x, 65519)
const fetch8 = (x) => fetch(x, 251)

// the last number can be anything.
const build16 = (x, o) => fetch16((fetch16(x) + o) % 65536 ^ 42703)
const build8 = (x, o) => fetch8((fetch8(x) + o) % 256 ^ 101)

const j = 115; // If you don't want duplicates, either i or j should stay fixed
let i = 0
let invalid = [];
let valid = new Set;
while (i <= 255) { // <-- small fix here!
    let x = build8(i, j); // To test, you can swap i and j here, and run again.
    if (x > 255) {
        invalid.push([ i, j, x ]);
    } else {
        valid.add(x);
    }
    i++;
}

console.log("invalid:", JSON.stringify(invalid));
console.log("count of valid:", valid.size);
trincot
  • 317,000
  • 35
  • 244
  • 286
  • is it possible to write a function that computes the inverse/reverse of `build8`? https://stackoverflow.com/questions/68850952/how-can-you-reverse-this-pseudo-prng-to-get-back-the-original-number?noredirect=1#68850952 – Lance Aug 19 '21 at 18:15
  • For some reason replacing the max from 65536 to a different value (`(32 * 16) ** 3`), and changing the other values causes it to produce tons of duplicates. I have no idea why. https://gist.github.com/lancejpollard/24b6c611d4d159b39a80314709550f5e Wondering if this only works when everything is a clean power of 2. – Lance Jan 12 '22 at 10:15
  • The values 65519 and 251 in this code should be primes, and best the largest primes below the range. The value 65536 should indeed be a power of 2 when you are going to XOR the result of the `%` operator. – trincot Jan 12 '22 at 10:22
  • I think I missed the fact that the prime must also satisfy `p≡3mod4`, going to take a look. Thanks. – Lance Jan 12 '22 at 10:39
  • @Lance How do you test if a prime satifsies `p≡3mod4`? – Nick Strupat Aug 01 '23 at 04:12
  • 1
    @NickStrupat [`p % 4 === 3`](https://github.com/lancejpollard/quadratic-residue-prng.js) – Lance Aug 01 '23 at 05:46