我需要随机选择一个哈希条目,所以我做
h = {1 => 'one', 2 => 'two', 3 => 'three'}
k = h.keys.sample
result = h[k]
由于h.keys
创建了一个新数组,我不喜欢它。有没有办法避免每次都创建一个新数组?
答案 0 :(得分:2)
这不会生成另一个数组。平均 hash_random_value 将在给定哈希中间迭代以生成随机值。
def hash_random_value(h)
i = rand(h.length)
h.each_with_index do |(_, v), i2|
return v if i == i2
end
end
h = {1 => 'one', 2 => 'two', 3 => 'three'}
hash_random_value(h)
这就是说,只有在确定需要这样做时才应优化。您可以知道的唯一方法是分析您的代码,否则您很可能会进行过早优化。即使代码复杂化并增加引入错误的可能性 - 有时甚至会降低程序的性能。您的原始解决方案比我的解决方案更容易理解,并且很明显它是正确的。
答案 1 :(得分:2)
我想首先重申大多数人所说的话:这可能无关紧要。
其次,我会指出,您确实需要一个随机的值,而不是一个随机的键。也许这只是因为你的示例代码片段没有显示你真正在做什么。
如果您经常需要随机值,并且很少更新Hash,我建议在修改Hash时随时缓存值,然后从缓存中获取随机值。一种方法可能是这样的:
class RandomValueHash < Hash
def []=(k, v)
super(k, v)
@values = self.values
end
def sample_value
@values ||= self.values
@values.sample
end
end
rvh = RandomValueHash[{1 => 'one', 2 => 'two', 3 => 'three'}]
rvh.sample_value
# => "one"
rvh[4] = 'four'
rvh[5] = 'five'
rvh.sample_value
# => "four"
当然,如果您确实需要随机密钥而不是值,则应用完全相同的概念。无论哪种方式,这都可以避免每次获得值时重新创建数组;它只在必要时创建它。
答案 2 :(得分:1)
怎么样......
h = {1 => 'one', 2 => 'two', 3 => 'three'}
k = h.keys
...
result = h[k.sample]
您可以根据需要随时执行result = h[k.sample]
次,并且不会重新生成k
数组。但是,您应该在k
更改时随时重新生成h
。
ADDENDUM :我正在为几个建议的解决方案投入基准代码。享受。
#!/usr/bin/env ruby
require 'benchmark'
NUM_ITERATIONS = 1_000_000
def hash_random_value(h)
i = rand(h.length)
h.each_with_index do |(_, v), i2|
return v if i == i2
end
end
class RandomValueHash < Hash
def []=(k, v)
super(k, v)
@values = self.values
end
def sample_value
@values ||= self.values
@values.sample
end
end
Benchmark.bmbm do |b|
h = {1 => 'one', 2 => 'two', 3 => 'three'}
b.report("original proposal") do
NUM_ITERATIONS.times {k = h.keys.sample; result = h[k]}
end
b.report("hash_random_value") do
NUM_ITERATIONS.times {result = hash_random_value(h)}
end
b.report("manual keyset") do
k = h.keys
NUM_ITERATIONS.times {result = h[k.sample]}
end
rvh = RandomValueHash[{1 => 'one', 2 => 'two', 3 => 'three'}]
b.report("RandomValueHash") do
NUM_ITERATIONS.times {result = rvh.sample_value}
end
end
答案 3 :(得分:1)
如果你需要经常制作随机样本,并且需要高效,那么Ruby Hash
可能不是你问题的正确数据结构或存储。即使是将Hash
和Array
属性保持在一起的包装类也可以正常工作 - 例如,如果每次写入哈希都需要读取20个随机样本。
这是否适合您不仅取决于阅读和写作的比例,还与您的问题数据的逻辑结构有关(而不是您在解决方案中选择如何表示它)。
但在您开始重新思考问题之前,您需要在受影响的代码中实现更高性能的实际需求。散列需要非常大,以便获取其密钥具有明显的成本。当哈希在我的笔记本电脑上有100万个条目时,h.keys
大约需要250毫秒。
答案 4 :(得分:0)
不是真的。哈希没有索引,因此您可以将它们转换为数组并选择随机索引,也可以随机枚举哈希值。您应该对哪种方法最快进行基准测试,但我怀疑您是否可以避免创建新对象。
如果您不关心您的对象,您可以随机移动它的键,但是您可以使用Arrays来获取返回值。
答案 5 :(得分:0)
除非你有一个巨大的哈希,否则这是一个毫无意义的问题。 Ruby不是效率强者,如果你对此感到担心,你应该使用C(++)。
答案 6 :(得分:0)
类似的东西:
h.each_with_index.reduce(nil) {|m, ((_, v), i)|
rand(i + 1) == 0 ? v : m
}