Math.Random except some numbers

Is there a way to get a math.random number except some definited numbers?

I mean, NPC1 has code 1234 and NPC2 has code 5678

now when i give to NPCs the code the script just does

math.random (0,9999) but i don’t want duplicates, also because there will be more than 10 npc and codes and the possibilities are really low but not impossible

i need something like math.random(0,9999) except (1,6,777,3434)

Someone knows how to do this?
The only option i have in my mind is to do
if Npc1 == Npc2 or Npc2 == Npc3 and so on but will take infinity

2 Likes

Why not have a table in which you stored already random generated numbers and on each generation, you run a repeat until loop with a condition that the generated number doesn’t already exist in the table?

4 Likes
local numbersPicked = {}

local function addNpc()

	local code = nil
	while true do
		code = math.random(0, 9999)
		if numbersPicked[tostring(code)] then
			continue
		end
		break
	end
	numbersPicked[tostring(code)] = true

	-- npc adding code
end

Just be aware that if you have more then 9999 npcs then it will never find a code and give a error.

2 Likes

Ok i think i did it after some problems and a lot of functions but seems good!

Here is the script if someone else is having the same problem:

local NumberGot = {9999.5}
local position = 0
local esiste = false

function NumberAdder()
	local RandomNumber = math.random(1,5)
	
	for i,v in pairs(NumberGot) do
		if v == RandomNumber then
			print("Stop")
			esiste = true
			break
		else
			esiste = false
		end
	end
	
	if esiste == false then
		print("Go")
		position = position+1
		table.insert(NumberGot,position,RandomNumber)
	end
end




NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()
NumberAdder()

table.sort(NumberGot)
print(unpack(NumberGot))
print(table.getn(NumberGot))

Basically: RandomNumber stand for the number you will add, a if statement with for i,v in pairs will check if every value inside the table is equal to the number got from math.random, if there is not then the script will check if the statement “esiste” is false or true, based on if there is a value inside already and let the script add the number to the table, if instead there is a number in the table that is equal to the number extracted the script will just print a message.

Also the table needs at least one value inside to start, you can just do
table.remove to get rid of it later.

for i,v in pairs(NumberGot) do
	if v == 9999.5 then --Put here the number you used up all, make sure is a number that can't be chosen from the random generator, i used a double value so there is no problem
		table.remove(NumberGot,i)
	end
end

I also added a table.sort so is easier check if the script is giving correct outputs or not.
Thanks for the tips :smiley:

Also for paap15 i tried it but got only errors, i’m not much good on read and decrypt others’s code so also if mine is similiar to your i really don’t know but seems not, thank you too for helping :smiley:

1 Like

3 ways:

paap15’s method

(which is picking random numbers until you have one that isn’t one of the numbers.)
This works fantastic if you have up to 100 or so NPCs, or even just 3-4.
The worst case of that one is when math.random keeps picking used numbers 10000 or so times in a row, which is unlikely, but a looming threat.

Deck of cards method

Make a table/array. Fill it with all 9999 possible numbers. Shuffle the array (Fisher-Yates shuffle). Start picking numbers from the start.
This is like picking cards from a shuffled deck and setting used ones aside. Because the used cards are gone, they cannot be picked again. Blindly using math.random is like picking a random card from a deck and putting the card back in the deck, so it can be chosen again.

local numbers = {}
local which = 0
local max = 9999

-- fill it up!
for i = 1, max do table.insert(numbers, i) end
-- shuffle it!
-- https://gist.github.com/Uradamus/10323382#gistcomment-2754684
for i = #numbers, 2, -1 do
	local j = math.random(i)
	numbers[i], numbers[j] = numbers[j], numbers[i]
end

function getNPCNumber()
	assert(which < max, "NPC number limit reached")
	which = which + 1
	return numbers[which]
end

@Sleitnick thank you for being visible on google search!
This is guaranteed to give you a number and will not become slower and slower as it becomes more likely to pick a number that’s already in use. But it takes a lot of memory even if you only use 10 of the numbers.
Use this if you really do plan to have thousands of NPC names used, and never just 3 or 10.
(You need to modify this in a non-obvious way if you want 1, 6, 777, 3434 etc. to be the first ones to appear.)

I have no name for this. Skipping?

Make a table (list/array) for numbers that have been chosen. Pick a random number from 1 to (9999 - amount of items in table). Add 1 to the random number for each number in the array that is less than or equal to the random number. Insert the random number into the array such that the array stays sorted.
In essence, this picks one of the 9999 - n numbers that haven’t been picked, then makes it so the numbers that have been picked before, they don’t exist, just skip over them.
Example code (completely untested):

local used = {1, 6, 777, 3434} -- must be sorted ascending
local max = 9999

function getNPCNumber()
	assert(#used < max, "NPC number limit reached")
	
	local number = math.random(1, max - #used)
	for i,v in ipairs(used) do
		if v <= number then
			-- skip this number
			number = number + 1
		else
			-- pick this number, and add it to the list of used numbers
			-- before the number that was just checked, which is
			-- greater than the random number which is greater than
			-- the previously checked number
			table.insert(used, i, number)
			return number
		end
	end
	-- the random number is the highest one yet (or there are none in there at all)
	table.insert(used, number)
	return number
end

I won’t bother to check whether the statistics of this check out, but I think it’d work.
Similarly to 1, it only uses as much memory as you have names, but similarly to 2, it is guaranteed to return within 9999 iterations; this worst case only happens once or twice when all numbers 1-9950 have been picked and it has to go through the 9950 first numbers to check the latter ones.
You can use a binary search to make searching the table take no time as well, but it’s fine as-is.

4 Likes