3

ハッシュエントリをランダムに選択する必要があるため、そうします

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

新しい配列を作成するのでh.keys、私はそれが好きではありません。毎回新しい配列を作成しないようにする方法はありますか?

4

7 に答える 7

2

最初に、ほとんどの人が言っていることを繰り返したいと思います。これはおそらく問題ではありません。

次に、 random keyではなくrandom valueが必要であるように思われることを指摘します。おそらく、コードのスニペットの例が実際に何をしているのかを示していないからです。

ランダム値が頻繁に必要で、ハッシュの更新頻度が非常に低い場合は、ハッシュが変更されるたびに値をキャッシュし、キャッシュからランダム値を取得することをお勧めします。これを行う 1 つの方法は、次のようになります。

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"

もちろん、値ではなくランダムなキーが本当に必要な場合は、まったく同じ概念が適用されます。いずれにせよ、これにより、値を取得するたびに配列を再作成する必要がなくなります。必要な場合にのみ作成します。

于 2013-06-01T00:08:08.347 に答える
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)

そうは言っても、それを行う必要があることが確実な場合にのみ最適化する必要があります。知ることができる唯一の方法は、コードをプロファイリングすることです。そうしないと、時期尚早の最適化を行っている可能性が高くなります。つまり、コードが複雑になり、バグが発生する可能性が高くなり、プログラムのパフォーマンスが低下することさえあります。あなたの元の解決策は私のものよりもはるかに理解しやすく、それが正しいことはすぐにわかります。

于 2013-05-31T23:20:55.730 に答える
1

どうですか...

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

result = h[k.sample]好きなだけ何度でも実行できますが、k配列が再生成されることはありません。ただし、k時間のh変更は再生成する必要があります。

補遺: 提案されたソリューションのいくつかのベンチマーク コードを投入しています。楽しみ。

#!/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
于 2013-05-31T20:53:31.733 に答える
1

ランダム サンプルを大量に作成する必要があり、それを効率的にする必要がある場合、おそらく RubyHashは問題のデータ構造またはストレージとして適切ではありません。Hashたとえば、ハッシュへの書き込みごとに 20 個のランダムなサンプルを読み取る必要がある場合は、一緒に属性を保持するラッパー クラスでもArrayうまく機能する可能性があります。

それが機能するかどうかは、読み取りと書き込みの比率に依存するだけでなく、問題データの論理構造にも関係します (ソリューションでの表現方法とは対照的に)。

しかし、問題の再考に着手する前に、影響を受けるコードでより高いパフォーマンスが実際に必要とされている必要があります。キーをフェッチするための顕著なコストがかかるようにするには、ハッシュをかなり大きくする必要があります。h.keysラップトップでハッシュに 100 万のエントリがある場合、約 250 ミリ秒かかります。

于 2013-05-31T21:39:10.417 に答える
0

あまり。ハッシュにはインデックスがないため、それらを配列に変換してランダムなインデックスを選択するか、ハッシュをランダムな回数列挙します。どのメソッドが最速かをベンチマークする必要がありますが、新しいオブジェクトの作成を回避できるとは思えません。

オブジェクトを気にしない場合は、そのキーをランダムな回数シフトできますが、戻り値の配列を作成します。

于 2013-05-31T20:52:34.630 に答える
0

巨大なハッシュがない限り、これは無意味な懸念です。Ruby は効率化の原動力ではありません。これが心配な場合は、C(++) を使用する必要があります。

于 2013-05-31T20:57:32.533 に答える
0

このようなもの:

h.each_with_index.reduce(nil) {|m, ((_, v), i)|
  rand(i + 1) == 0 ? v : m
}
于 2013-05-31T23:05:25.930 に答える