随机范围但可以重复n次

时间:2016-02-18 22:55:20

标签: ruby-on-rails ruby loops random functional-programming

红宝石中的随机范围表示为

rand(2..11)

让我们说我喜欢数字2或该范围内的任何其他数字重复不超过 N 次数。

是这样做的吗?

2 个答案:

答案 0 :(得分:2)

我认为没有默认方法可以做到这一点。您需要编写一些代码,例如,请查看Marc Talbot的答案:https://stackoverflow.com/a/9458016/1306709

a = (100..999).to_a.shuffle 

然后每次你需要一个新的身份

new_id = a.pop

这可以保证数字永远不会被重用。当然,当你用完阵列上的元素时,你会遇到问题。

所以,如果你需要重复几次 - 将几个具有相似数字的集合和pop组合起来。

答案 1 :(得分:2)

您可以通过执行以下步骤来完成此操作:

  • 获取无约束的样本
  • 如果允许有限重复值的n个实例,且最多只允许m,请删除所有n并添加回m
  • 从数组中随机选择n-m值,该数组由初始范围的元素减去有限重复值
  • 组成
  • 随机播放生成的数组

<强>代码

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

puts声明仅供参考。

<强>实施例

我已经创建了一个帮助器,用于显示随机选择的范围中每个元素的数量。此方法还会在初始无限制样本中打印指定元素的实例数。

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)"] 

替代方法

  • 确定有限重复值的实例索引
  • 如果有n > m,请随意选择n-m
  • n-m个选定索引处的值替换为第一个解决方案中选择的随机值

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
  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)"]