web-dev-qa-db-ja.com

Rubyでn個の一意の乱数のリストを生成するにはどうすればよいですか?

これは私がこれまでに持っているものです:

myArray.map!{ Rand(max) }

ただし、リスト内の番号が一意でない場合もあります。リストに一意の番号のみが含まれていることを確認するにはどうすればよいですか。大きいリストを作成し、そこからn個の一意の番号を選択する必要はありません。

編集:
これがループなしで実行されることを本当に望んでいます-可能であれば。

34
Esteban Araya

これはセットを使用します:

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
23
Ryan Leavengood
(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)

したがって、現代のRuby=で乱数列を生成する最も簡単な方法は次のとおりです。

(0..50).to_a.shuffle.take(x)
64
Kent Fredric

速度についてのアイデアを与えるために、私はこれの4つのバージョンを実行しました。

  1. ライアンの提案のように、セットを使用します。
  2. 必要以上に少し大きい配列を使用してから、uniqを実行します!最後に。
  3. カイルが示唆したように、ハッシュを使用します。
  4. 必要なサイズの配列を作成し、ケントの提案のようにランダムに並べ替えます(ただし、無関係な "-0.5"を使用しないため、何も実行しません)。

それらはすべて小規模で高速であるため、1,000,000個の数値のリストを作成するように依頼しました。時間は秒単位です:

  1. セット:628
  2. 配列+ uniq:629
  3. ハッシュ:645
  4. 固定配列+並べ替え:8

そして、いいえ、最後の1つはタイプミスではありません。だからあなたがスピードを気にしていて、数値が0から何までの整数であっても問題ないなら、私の正確なコードは:

a = (0...1000000).sort_by{Rand}
22
glenn mcdonald

Ruby 1.9は、要素、または配列からランダムに選択された要素を返すArray#sampleメソッドを提供します。 #sampleの結果には、同じ配列要素が2回含まれません。

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

to_a.sort_byアプローチと比較すると、sampleメソッドは大幅に高速であるように見えます。簡単なシナリオでは、sort_bysampleと比較して、次の結果を得ました。

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
22
Duncan Beevers

はい、これはループなしで、どの番号が選択されているかを追跡することなく行うことができます。これは線形フィードバックシフトレジスタと呼ばれます。 繰り返しのない乱数列を作成

4
Bryan Larsen

これの遊びはいかがですか? SetまたはHashを使用する必要のない一意の乱数。

x = 0
(1..100).map{|iter| x += Rand(100)}.shuffle
2
Sam Saffron

ハッシュを使用して、これまでに使用した乱数を追跡できます。

seen = {}
max = 100
(1..10).map { |n|
  x = Rand(max)
  while (seen[x]) 
    x = Rand(max)
  end
  x
}
1
Kyle Burton

アイテムをリスト/配列に追加するのではなく、セットに追加します。

1
jon

可能な乱数の有限なリスト(つまり、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

必要に応じて、このコードをArray.mapのようなLOOKに記述できます。

1
Ryan Leavengood

この方法ではループはありません

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

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

=> 1.9114e-05 

上記のKent Fredricのソリューションに基づいて、これは私が最終的に使用したものです:

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

ケント、ありがとう。

1
Doug English

方法1

ケントのアプローチを使用すると、制限された範囲内のすべての値を維持する任意の長さの配列を生成することが可能です。

# 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

方法2

別の、おそらくより効率的な、同じケントの 別の答え から変更された方法:

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 :)
0
mmdemirbas

最大値を事前に知っておくと便利ですが、次の方法で行うことができます。

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は、すべての値がデッキから排出されるときに取得します。

0
TuxmAL

ここに1つの解決策があります:

これらの乱数をr_minr_maxの間にしたいとします。リストの各要素について、乱数rを生成し、list[i]=list[i-1]+rを作成します。これにより、単調増加する乱数が得られ、一意性が保証されます

  • r+list[i-1]はオーバーフローしない
  • r> 0

最初の要素には、r_minの代わりにlist[i-1]を使用します。完了したら、リストをシャッフルして、要素が明確に順序付けられないようにすることができます。

このメソッドの唯一の問題は、r_maxを超えても、生成する要素がまだある場合です。この場合、r_minr_maxを、すでに計算した2つの隣接する要素にリセットして、単にプロセスを繰り返すことができます。これは、すでに使用されている数値がない間隔で同じアルゴリズムを効果的に実行します。リストを作成するまで、これを続けることができます。

0
freespace