vendredi 8 décembre 2017

How to deterministically combine ints in a way that it always generates unique values?

I have a set of randomly generated ints, and an evolve(set) function that takes a set, removes two ints and inserts 4 new ints. I want those to always be unique; that is, no matter how many many times I apply evolve(evolve(...evolve(set))), it should never have duplicated values.

What is a simple choice of combine(int x, int y) -> int that guarantees that property with high probability?

Example:

// Helpers.
var range = n => {var a=[]; for(var i=0;i<n;++i)a[i]=i; return a};
var random = () => (Math.random() * Math.pow(2,31) | 0);
var removeRandom = set => set.splice(random() % set.length, 1)[0];
var hasRepeated = set => !set.sort((a,b) => a - b).reduce((a,b) => a && a !== b && b || 0, -1);

// Receives two parent ints, return 4 unique ints.
// Can't use random() here; it must generate 4 ints
// from simple functions (multiplication, bitwise
// operations, etc.) of `a` and `b`.
function combine(a, b) {
  return [
    a + b + 0,
    a + b + 1,
    a + b + 2,
    a + b + 3
  ];
};

// Removes two ints from set, inserts 4 ints.
function evolve(set) {
  var a = removeRandom(set);
  var b = removeRandom(set);
  set.push(...combine(a,b));
};

// Random initial set of ints.
var set = range(64).map(random);

// No matter how many times `evolve` is applied, 
// the set should never have repeated ints.
for (var i = 0; i < 50000; ++i) {
  var a = removeRandom(set);
  var b = removeRandom(set);
  set.push(...combine(a,b));
}

// Prints `true` if algorithm is correct
// (no repeated number was generated).
console.log(!hasRepeated(set));

Notice that, here, combine(a,b) just adds them together. This is a bad choice; with as few as 50000 calls to evolve, it already fails the test. I could use a linear congruential generator, but I wonder if it can be simpler than that under those conditions. Perhaps some use of XOR?




Aucun commentaire:

Enregistrer un commentaire