Random range in ruby presented as
rand(2..11)
Lets say I would love number 2 or any other number in the range to be repeated not more than N amount of times.
Is the way to do it?
Random range in ruby presented as
rand(2..11)
Lets say I would love number 2 or any other number in the range to be repeated not more than N amount of times.
Is the way to do it?
I think there is no default way to do that. You need to write some code, for example check this answer from Marc Talbot: https://stackoverflow.com/a/9458016/1306709
a = (100..999).to_a.shuffle
then every time you need a new id
new_id = a.pop
This guarantees that numbers are never reused. Of course, you'll have problems when you run out of elements on the array.
so if you need a few times of repeating - combine a few sets with similar numbers and pop
from it.
You can do that by performing the following steps:
n
instances of the limited repeat value, and at most m
are permitted, delete all n
and add back m
n-m
values from an array comprised of elements of the initial range less the limited repeat valueCode
def random_with_limit(range, sample_size, limited_repeat_value, max_instances)
a = Array.new(sample_size) { rand range }
extra = a.count(limited_repeat_value) - max_instances
puts "inital number of #{limited_repeat_value} = #{max_instances+extra}"
return a if extra <= 0
b = [*range]-[limited_repeat_value]
(a-[limited_repeat_value]).
concat([limited_repeat_value]*max_instances).
concat(Array.new(extra) { b.sample }).
shuffle
end
The puts
statement is included merely for illustration.
Examples
I've created a helper for displaying numbers of each element of the range that are randomly selected. This method also prints the number of instances of the specified element in the initial unrestricted sample.
def show_result(range, sample_size, limited_repeat_value, max_instances)
random_with_limit(2..11, 50, 4, 3).sort.chunk(&:itself).map { |n,dups|
"#{n} (#{dups.count})" }
end
show_result(2..11, 50, 4, 3)
# inital number of 4 = 4
#=> ["2 (6)", "3 (10)", "4 (3)", "5 (5)", "6 (5)",
# "7 (6)", "8 (5)", "9 (2)", "10 (6)", "11 (2)"]
show_result(2..11, 50, 4, 3)
# inital number of 4 = 3
#=> ["2 (7)", "3 (5)", "4 (3)", "5 (3)", "6 (4)",
# "7 (6)", "8 (5)", "9 (6)", "10 (7)", "11 (4)"]
show_result(2..11, 50, 4, 3)
# inital number of 4 = 2
#=> ["2 (5)", "3 (5)", "4 (2)", "5 (4)", "6 (8)",
# "7 (8)", "8 (1)", "9 (7)", "10 (4)", "11 (6)"]
show_result(2..11, 50, 4, 3)
# inital number of 4 = 7
#=> ["2 (2)", "3 (8)", "4 (3)", "5 (8)", "6 (5)",
# "7 (3)", "8 (3)", "9 (6)", "10 (5)", "11 (7)"]
Alternative approach
n > m
, select n-m
at randomn-m
selected indices with random values selected as in the first solutiondef random_with_limit(range, sample_size, limited_repeat_value, max_instances)
a = Array.new(sample_size) { rand range }
extra = a.count(limited_repeat_value) - max_instances
puts "inital number of #{limited_repeat_value} = #{max_instances+extra}"
return a if extra <= 0
idx = a.each_with_index.select { |x,_| x == limited_repeat_value }.map(&:last)
b = [*range]-[limited_repeat_value]
idx.shuffle.first(extra).each { |i| a[i] = b.sample }
a
end
show_result(2..11, 50, 4, 3)
# inital number of 4 = 6
# => ["2 (2)", "3 (3)", "4 (3)", "5 (4)", "6 (7)",
# "7 (12)", "8 (4)", "9 (7)", "10 (5)", "11 (3)"]
show_result(2..11, 50, 4, 3)
# inital number of 4 = 2
#=> ["2 (9)", "3 (3)", "4 (2)", "5 (6)", "6 (6)",
# "7 (3)", "8 (2)", "9 (5)", "10 (9)", "11 (5)"]
show_result(2..11, 50, 4, 3)
# inital number of 4 = 4
#=> ["2 (3)", "3 (3)", "4 (3)", "5 (5)", "6 (4)",
# "7 (7)", "8 (6)", "9 (4)", "10 (7)", "11 (8)"]