Function that does not return the same numbers twice

Greetings,

Say we have to x;y values:

{1;-1}
{-1;1}

I need a function that, if I insert both values, does not have the same return.

E.g.

f(1, -1) ~= f(-1, 1)

What exactly you want the function to do?
Your explanation is not enough.
This example function will return false for everything but -1, 1 values so it actually does what you said, however, probably not what you need.

local function f(a, b)
  return a == -1 and b == 1
end
1 Like

We are doing a random generation system. For memory optimisation, it runs client-sided. To ensure all clients get the same random generation, we use the Random.new() and the random:NextInteger() functions.

We want the seed of Random.new() to be the same for every chunk for every client.

We archived this with the following formula: ((ChunkPosX/50) + (ChunkPosY/50) * 1).

Although that created infinitely repeating chunks with coordinates like this:
Bild_2022-06-06_170435982

Now we need a different solution.

1 Like

What’s preventing you from using perlin noise? You can also set the randomseed using math.randomseed so if both seeds are the same it returns the same value.

We did try math.randomseed. However, it did not work and caused clients to get different generations.

We are not using Perlin noise because we are only generating on a 2D grid.

Perlin noise generates 2D if only 2 arguments and a seed is provided.

1 Like

So instead of generating the numbers on the server, you prefer to generate them locally for every single player and make the server ensure they have the same seed? Is it correct?

The idea is terrible. It’s overcomplicated, vulnerable, hard to maintain and modify, and doesn’t optimize anything.
This is why server scripts exist.

If you want to discover yourself how much does it costs the server to generate a 1000 random values, try running this test:

local start = tick()

for _ = 1, 1000 do
  math.random(1, 100)
end

warn(tick() - start)

You’ll see a warning in the console saying how much time did it take to run these operations, in seconds.

You have a good point, I will try using that instead. I will keep you updated.

This isn’t a terrible idea. Sever to client sync is prevalent in many games — in and out of Roblox. This reduces server strain is is crucial to the responsiveness of the game. Network connection can also be slower because of the constant streaming. Keep in mind this generates a random number and I’m assuming (not 100% sure) generating other objects as well.

1 Like
local Random = Random.new()

local NumberCache = {}

local function UniqueRNG()
	local Number
	repeat
		Number = Random:NextInteger(1, 20) --Define the number's limits here.
	until not NumberCache[Number] --Make sure the number hasn't been generated before.
	NumberCache[Number] = true --Cache the generated number.
	return Number
end

for _ = 1, 20 do
	print(UniqueRNG())
end

image

Notice the lack of repeating numbers.

If so, the numbers should be generated on the server and the objects loaded locally…

If you can do the calculations within the client that is guaranteed to sync, it’s probably better to do so (there are edge cases).

1 Like

Trying to make all the clients generate the same random value is rather stupid when you have the server scripts just for this.

And I don’t see how it optimizes anything (check the test-code above)

try using bit32.bxor and bit32.rrotate, these are very commonly used in simple and advanced hash generation techniques. floor dividing by 50 will still get you your chunk value but you will get unique results from bit32.bxor(x/50, y/50) I would recommend multiplying by prime numbers as well, the reason they should be prime is to reduce repeating binary numbers, for example multiplying by 2 would never have the first bit set.

Others have brought up math.noise, if you can’t use this I would still recommend using what ever random number you create and feed it into the roblox Random.new. PRNG is hard to create and you will run into weird edge cases like looping numbers or plain old bad distribution, please make use of expert’s work.

If all numbers are cached, then there are no more possible numbers to chose from. You could probably cache the last few numbers, or clear the cache once every number is taken.

print(not bit32.btest(x, y))

One could also use the inverse of bit32.btest to determine if two numbers are different.

this would almost always return false, btest alone isn’t very useful for random numbers. Wikipedia has some implementation details in C on how an Xorshift PRNG works. I will copy an example into lua

local function xorshift32(x: number): number
    x = bit32.bxor(x, bit32.lrotate(x, 13))
    x = bit32.bxor(x, bit32.rrotate(x, 17))
    x = bit32.bxor(x, bit32.lrotate(x, 5))
    return x
end

This example only takes one number but spits out a entirely different number, optimally it would use larger prime numbers for 13, 17, and 5. using xor and left/right rotates generates a very unique pattern that spans many numbers and is foreign to humans and the decimal number system.

local Table = {False = 0, True = 0}

for Number = 1, 100 do
	local Result = not bit32.btest(50, Number)
	if Result then Table.True += 1 else Table.False += 1 end
end

for Bool, Count in pairs(Table) do
	print(Bool, Count) --85 false, 15 true.
end

For me, 15% would be more than large enough of a pool of numbers to choose from. The ‘50’ value would also be non-uniform (as it would instead be the last generated number).

not bit32.btest(50, Number) would be true for any number in binary where xx00xx0x x could be 1 or 0. The 2s place accounts for half of your false results, 16 and 32s place are going to be far more common in numbers from 16 to 63, which is likely the other half of your false results. And it only returns 1 or 0, a terribly small number range compared to most random number generators.

We are now changing our generation system to implement math.noise().

Thanks to everyone who tried helping!

1 Like