How can I make sure my RNG numbers are unique?

117 Views Asked by At

I'm trying to select 2 random items out of a list using the RNG class. The problem is occasionally I get the same 2 numbers and I'd like them to be unique. I tried using a while loop to get another number if the it's the same as the last one but adding even a simple while loop results in an "Exceeded prepaid gas" error. What am I not understanding?

//simplified for posting question
var lengthOfList = 10
var numItemsWanted = 2
//Get more rng numbers than I need incase of duplicates
const rng = new RNG<u32>(lenghtOfList, lengthOfList)

for(let i = 0; i < numItemsWanted; i++) {
    var r = rng.next()
    while (r == rng.last()) {
        r = rng.next()
    }
    newList.push(oldList[r])
}

Working:

//simplified for posting question
var lengthOfList = 10
var numItemsWanted = 2
//Get more rng numbers than I need incase of duplicates
const rng = new RNG<u32>(lenghtOfList, lengthOfList)
let r = rng.next()
let last = r + 1
for(let i = 0; i < numItemsWanted; i++) {
    newList.push(oldList[r])
    last = r
    r = rng.next()
    while (r == last) {
        r = rng.next()
    }
}

2

There are 2 best solutions below

2
On BEST ANSWER

this is about near-sdk-as, the smart contract development kit for AssemblyScript on the NEAR platform

you can see how RNG is used in this example https://github.com/Learn-NEAR/NCD.L1.sample--lottery/blob/ff6cddaa8cac4d8fe29dd1a19b38a6e3c7045363/src/lottery/assembly/lottery.ts#L12-L13

class Lottery {
  private chance: f64 = 0.20

  play(): bool {
    const rng = new RNG<u32>(1, u32.MAX_VALUE);
    const roll = rng.next();
    logging.log("roll: " + roll.toString());
    return roll <= <u32>(<f64>u32.MAX_VALUE * this.chance);
  }
}

and how the constructor is implemented here: https://github.com/near/near-sdk-as/blob/f3707a1672d6da6f6d6a75cd645f8cbdacdaf495/sdk-core/assembly/math.ts#L152

the first argument is the length of the buffer holding random numbers generated from the seed. you can use the next() method to get more numbers from this buffer with each call

export class RNG<T> {
  constructor(len: u32, public max: u32 = 10_000) {
    let real_len = len * sizeof<T>();
    this.buffer = math.randomBuffer(real_len);
    this._last = this.get(0);
  }

  next(): T {}
}
1
On

If you remove the item from oldList once picked, it would be imposible to picked it again.

Another aproach is to shuffle your oldList and then pick the first two items.