36

这是我到目前为止所拥有的:

myArray.map!{ rand(max) }

然而,显然,有时列表中的数字并不是唯一的。如何确保我的列表只包含唯一编号,而不必创建一个更大的列表,然后从中选择 n 个唯一编号?

编辑:
我真的很想看到这个没有循环完成 - 如果可能的话。

4

15 回答 15

65
(0..50).to_a.sort{ rand() - 0.5 }[0..x] 

(0..50).to_a可以替换为任何数组。0 是“最小值”,50 是“最大值” x 是“我想要多少个值”

当然,不可能允许 x 大于 max-min :)

扩展它的工作原理

(0..5).to_a  ==> [0,1,2,3,4,5]
[0,1,2,3,4,5].sort{ -1 }  ==>  [0, 1, 2, 4, 3, 5]  # constant
[0,1,2,3,4,5].sort{  1 }  ==>  [5, 3, 0, 4, 2, 1]  # constant
[0,1,2,3,4,5].sort{ rand() - 0.5 }   ==>  [1, 5, 0, 3, 4, 2 ]  # random
[1, 5, 0, 3, 4, 2 ][ 0..2 ]   ==>  [1, 5, 0 ]

脚注:

值得一提的是,在 2008 年 9 月最初回答这个问题时,Array#shuffle我要么不知道,要么不知道,因此近似于Array#sort

因此,对此有大量建议的编辑。

所以:

.sort{ rand() - 0.5 }

可以更好,更短地表达现代 ruby​​ 实现使用

.shuffle

此外,

[0..x]

可以更明显地写成Array#take

.take(x)

因此,在现代红宝石上生成随机数序列的最简单方法是:

(0..50).to_a.shuffle.take(x)
于 2008-09-23T05:10:08.303 回答
25

这使用集合:

require 'set'

def rand_n(n, max)
    randoms = Set.new
    loop do
        randoms << rand(max)
        return randoms.to_a if randoms.size >= n
    end
end
于 2008-09-23T04:36:53.173 回答
23

Ruby 1.9 提供了 Array#sample 方法,它返回一个元素,或者从一个数组中随机选择的元素。#sample 的结果不会两次包含相同的 Array 元素。

(1..999).to_a.sample 5 # => [389, 30, 326, 946, 746]

与该to_a.sort_by方法相比,该sample方法似乎要快得多。在一个简单的场景中,我与sort_by进行了比较sample,得到了以下结果。

require 'benchmark'
range = 0...1000000
how_many = 5

Benchmark.realtime do
  range.to_a.sample(how_many)
end
=> 0.081083

Benchmark.realtime do
  (range).sort_by{rand}[0...how_many]
end
=> 2.907445
于 2012-03-27T03:03:25.643 回答
22

只是为了让您了解速度,我运行了四个版本:

  1. 使用 Sets,就像 Ryan 的建议一样。
  2. 使用比需要稍大的 Array,然后执行 uniq! 在末尾。
  3. 像凯尔建议的那样使用哈希。
  4. 创建一个所需大小的数组,然后随机排序,就像 Kent 的建议一样(但没有多余的“- 0.5”,它什么都不做)。

它们在小范围内都很快,所以我让它们各自创建一个包含 1,000,000 个数字的列表。以下是时间,以秒为单位:

  1. 套数:628
  2. 数组 + uniq:629
  3. 哈希:645
  4. 固定数组+排序:8

不,最后一个不是错字。因此,如果您关心速度,并且数字可以是从 0 到任何值的整数,那么我的确切代码是:

a = (0...1000000).sort_by{rand}
于 2008-09-23T17:25:58.113 回答
4

是的,可以在没有循环且不跟踪选择了哪些数字的情况下执行此操作。它被称为线性反馈移位寄存器: 创建无重复的随机数序列

于 2010-11-02T15:18:49.670 回答
4
[*1..99].sample(4) #=> [64, 99, 29, 49]

根据Array#sample文档,

通过使用随机和唯一索引选择元素

如果您需要SecureRandom(使用计算机噪声而不是伪随机数):

require 'securerandom'

[*1..99].sample(4, random: SecureRandom) #=> [2, 75, 95, 37]
于 2020-05-18T04:38:51.267 回答
2

玩这个怎么样?唯一的随机数,无需使用 Set 或 Hash。

x = 0
(1..100).map{|iter| x += rand(100)}.shuffle
于 2008-09-24T07:03:49.800 回答
1

您可以使用哈希来跟踪您迄今为止使用的随机数:

seen = {}
max = 100
(1..10).map { |n|
  x = rand(max)
  while (seen[x]) 
    x = rand(max)
  end
  x
}
于 2008-09-23T04:24:26.527 回答
1

与其将项目添加到列表/数组中,不如将它们添加到集合中。

于 2008-09-23T04:24:49.037 回答
1

如果您有一个可能的随机数的有限列表(即 1 到 100),那么 Kent 的解决方案很好。

否则,没有循环就没有其他好方法可以做到这一点。问题是如果你得到一个重复,你必须做一个循环。我的解决方案应该是有效的,并且循环不应超过数组的大小(即,如果您想要 20 个唯一的随机数,则平均可能需要 25 次迭代。)尽管迭代次数越多,您的数字越多需要和较小的最大值。这是我上面的代码修改以显示给定输入需要多少次迭代:

require 'set'

def rand_n(n, max)
    randoms = Set.new
    i = 0
    loop do
        randoms << rand(max)
        break if randoms.size > n
        i += 1
    end
    puts "Took #{i} iterations for #{n} random numbers to a max of #{max}"
    return randoms.to_a
end

如果你愿意,我可以写这个代码看起来更像 Array.map :)

于 2008-09-23T06:41:42.857 回答
1

基于上面肯特弗雷德里克的解决方案,这就是我最终使用的:

def n_unique_rand(number_to_generate, rand_upper_limit)
  return (0..rand_upper_limit - 1).sort_by{rand}[0..number_to_generate - 1]
end

谢谢肯特。

于 2012-01-16T05:00:38.093 回答
1

这种方法没有循环

Array.new(size) { rand(max) }

require 'benchmark'
max = 1000000
size = 5
Benchmark.realtime do
  Array.new(size) { rand(max) }
end

=> 1.9114e-05 
于 2015-09-03T08:38:12.157 回答
0

这是一种解决方案:

假设您希望这些随机数介于r_min和之间r_max。对于列表中的每个元素,生成一个随机数r,然后生成list[i]=list[i-1]+r。这将为您提供单调递增的随机数,保证唯一性,前提是

  • r+list[i-1]不会溢出
  • r> 0

对于第一个元素,您将使用r_min而不是list[i-1]. 完成后,您可以对列表进行洗牌,这样元素的顺序就不会那么明显了。

这种方法的唯一问题是当你过去r_max并且仍然有更多的元素要生成时。在这种情况下,您可以将r_min和重置r_max为您已经计算的 2 个相邻元素,然后简单地重复该过程。这有效地在没有使用数字的区间内运行相同的算法。您可以继续这样做,直到您填充了列表。

于 2008-09-23T04:44:39.753 回答
0

最好提前知道最大值,您可以这样做:

class NoLoopRand
  def initialize(max)
    @deck = (0..max).to_a
  end

  def getrnd
    return @deck.delete_at(rand(@deck.length - 1))
  end
end

您可以通过这种方式获取随机数据:

aRndNum = NoLoopRand.new(10)
puts aRndNum.getrnd

nil当所有值都从甲板上耗尽时,您将获得。

于 2008-09-23T12:25:28.180 回答
0

方法一

使用 Kent 的方法,可以生成一个任意长度的数组,将所有值保持在有限范围内:

# Generates a random array of length n.
#
# @param n     length of the desired array
# @param lower minimum number in the array
# @param upper maximum number in the array
def ary_rand(n, lower, upper)
    values_set = (lower..upper).to_a
    repetition = n/(upper-lower+1) + 1
    (values_set*repetition).sample n
end

方法二

另一种可能更有效的方法是从同一个肯特的另一个答案修改而来的:

def ary_rand2(n, lower, upper)
    v = (lower..upper).to_a
    (0...n).map{ v[rand(v.length)] }
end

输出

puts (ary_rand 5, 0, 9).to_s # [0, 8, 2, 5, 6] expected
puts (ary_rand 5, 0, 9).to_s # [7, 8, 2, 4, 3] different result for same params
puts (ary_rand 5, 0, 1).to_s # [0, 0, 1, 0, 1] repeated values from limited range
puts (ary_rand 5, 9, 0).to_s # []              no such range :)
于 2012-10-19T13:46:43.357 回答