Skip to content

Instantly share code, notes, and snippets.

@fwilleke80
Last active February 23, 2025 12:54
Show Gist options
  • Select an option

  • Save fwilleke80/55b49cb0c30959aa1b760d5f1110b885 to your computer and use it in GitHub Desktop.

Select an option

Save fwilleke80/55b49cb0c30959aa1b760d5f1110b885 to your computer and use it in GitHub Desktop.
Non-repeating equally distributed random values
import random
### @brief Cache dictionary for `get_non_repeating_random_value()`
non_repeating_random_value_cache = {
"available_numbers_storage": {},
"previous_range_storage": {},
"previous_number_storage": {}
}
### @brief Returns a non-repeating random value in the range `min_val .. max_val`.
###
### @details
### The non-repetitiveness is garanted for any range of min/max values, as well as
### for each key. Use the key to manage multiple non-repeating random sequences in
### a row.
###
### @param str
### @param min_val
### @param max_val
###
### @return The generated random value
def get_non_repeating_random_value(key: str, min_val: int, max_val: int, cache: dict):
# Unpack caches
available_numbers_storage: dict = cache["available_numbers_storage"]
previous_range_storage: dict = cache["previous_range_storage"]
previous_number_storage: dict = cache["previous_number_storage"]
# Get array of available numbers for this value name
available_numbers: list[int] = available_numbers_storage.get(key, list())
# Compare current random range to previous random range.
# If they're not equal, re-init the array.
previous_range: tuple[int, int] = previous_range_storage.get(key, None)
if previous_range is not None and (min_val != previous_range[0] or max_val != previous_range[1]):
available_numbers_storage[key] = []
available_numbers: list[int] = available_numbers_storage[key]
# If the list of available random number is empty, populate it
if not available_numbers:
available_numbers: list[int] = [i for i in range(min_val, max_val + 1)]
available_numbers_storage[key] = available_numbers
# Get previously chosen random number
previous_number: int = previous_number_storage.get(key, None)
# Randomly choose an available number
# Additionally to chosing numbers only from the remaining available numbers,
# we also ensure that it's not the previously chosen number again. This could
# have happened if availableNumber had been re-populated during this call.
while True:
random_number: int = random.choice(available_numbers)
if random_number != previous_number:
break
# Remove the chosen number from array
available_numbers.remove(random_number)
# Memorize previously chosen random number
# This is done to prevent the same number to be chosen again, if the array
# is going to be re-populated in the next call.
previous_number_storage[key]: int = random_number
# Memorize random range
previous_range_storage[key]: list[int, int] = [min_val, max_val]
return random_number
def main():
# Test range A
for _ in range(20):
print("\n------")
random_number = get_non_repeating_random_value("a", 0, 5, non_repeating_random_value_cache)
print(f"get_non_repeating_random_value(\"a\", 0, 5) = {random_number}")
# Test range A
for _ in range(20):
print("\n------")
random_number = get_non_repeating_random_value("a", 7, 17, non_repeating_random_value_cache)
print(f"get_non_repeating_random_value(\"a\", 7, 17) = {random_number}")
# Test range B
for _ in range(20):
print("\n------")
random_number = get_non_repeating_random_value("b", 10, 15, non_repeating_random_value_cache)
print(f"get_non_repeating_random_value(\"b\", 10, 15) = {random_number}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment