这是我到目前为止所做的:
myArray.map!{ rand(max) }
然而,显然,有时候列表中的数字并不是唯一的。如何确保我的列表只包含唯一的数字,而不必创建一个更大的列表,然后我从中选择n个唯一的数字?
修改
我真的很想看到这个没有循环 - 如果可能的话。
答案 0 :(得分:64)
(0..50).to_a.sort{ rand() - 0.5 }[0..x]
(0..50).to_a
可以替换为任何数组。
0是“minvalue”,50是“max value”
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)
答案 1 :(得分:23)
这使用Set:
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
答案 2 :(得分:22)
为了让您了解速度,我运行了四个版本:
他们都是小规模的快速,所以我让它们各自创建一个1,000,000个数字的列表。以下是时间,以秒为单位:
不,最后一个不是拼写错误。所以,如果你关心速度,并且数字从0到任何整数都可以,那么我的确切代码是:
a = (0...1000000).sort_by{rand}
答案 3 :(得分:22)
Ruby 1.9提供了Array#sample方法,该方法返回一个元素或从Array中随机选择的元素。 #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
答案 4 :(得分:4)
是的,可以在没有循环的情况下执行此操作,并且无需跟踪已选择的数字。它被称为线性反馈移位寄存器:Create Random Number Sequence with No Repeats
答案 5 :(得分:2)
这个游戏怎么样?独特的随机数,无需使用Set或Hash。
x = 0
(1..100).map{|iter| x += rand(100)}.shuffle
答案 6 :(得分:1)
您可以使用哈希来跟踪到目前为止使用的随机数:
seen = {}
max = 100
(1..10).map { |n|
x = rand(max)
while (seen[x])
x = rand(max)
end
x
}
答案 7 :(得分:1)
不是将项目添加到列表/数组,而是将它们添加到Set中。
答案 8 :(得分:1)
如果你有一个有限的可能随机数列表(即1到100),肯特的解决方案是好的。
否则没有循环就没有其他好方法可以做到。问题是如果你得到重复,你必须做一个循环。我的解决方案应该是高效的,并且循环不应该比数组的大小多得多(即如果你想要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
如果您愿意,我可以将此代码写入LOOK,更像是Array.map:)
答案 9 :(得分: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
谢谢肯特。
答案 10 :(得分:1)
使用此方法无循环
Array.new(size) { rand(max) }
require 'benchmark'
max = 1000000
size = 5
Benchmark.realtime do
Array.new(size) { rand(max) }
end
=> 1.9114e-05
答案 11 :(得分:1)
[*1..99].sample(4) #=> [64, 99, 29, 49]
根据Array#sample
个文档
使用随机和唯一索引选择元素
如果您需要SecureRandom
(使用计算机噪声而不是伪随机数):
require 'securerandom'
[*1..99].sample(4, random: SecureRandom) #=> [2, 75, 95, 37]
答案 12 :(得分:0)
这是一个解决方案:
假设您希望这些随机数介于r_min
和r_max
之间。对于列表中的每个元素,生成一个随机数r
,并生成list[i]=list[i-1]+r
。这将为您提供单调递增的随机数,保证唯一性,只要
r+list[i-1]
不会过度流动r
&gt; 0 对于第一个元素,您将使用r_min
而不是list[i-1]
。完成后,您可以对列表进行洗牌,因此元素的顺序不是很明显。
此方法的唯一问题是当您查看r_max
并且仍有更多元素要生成时。在这种情况下,您可以将r_min
和r_max
重置为已计算的2个相邻元素,然后重复此过程。这有效地在没有已使用数字的区间内运行相同的算法。您可以继续这样做,直到填写完列表。
答案 13 :(得分: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
。
答案 14 :(得分: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
从同一个肯特another answer修改的另一个可能效率更高的方法:
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 :)