避免密钥重复以获得随机哈希密钥

时间:2013-05-31 20:43:14

标签: ruby

我需要随机选择一个哈希条目,所以我做

h = {1 => 'one', 2 => 'two', 3 => 'three'}
k = h.keys.sample
result = h[k]

由于h.keys创建了一个新数组,我不喜欢它。有没有办法避免每次都创建一个新数组?

7 个答案:

答案 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可能不是你问题的正确数据结构或存储。即使是将HashArray属性保持在一起的包装类也可以正常工作 - 例如,如果每次写入哈希都需要读取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
}