これは私がこれまでに持っているものです:
myArray.map!{ rand(max) }
ただし、明らかに、リスト内の番号が一意でない場合があります。より大きなリストを作成せずに、リストに一意の番号のみが含まれていることを確認するにはどうすればよいですか。リストからn個の一意の番号を選択するだけです。
編集:
可能であれば、ループなしでこれが行われることを本当に望んでいます。
(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 }
を使用して現代のルビーの実装でより良く、より短く表現することができます
.shuffle
さらに、
[0..x]
より明確に次のように書くことができますArray#take
:
.take(x)
したがって、現代のルビーで一連の乱数を生成する最も簡単な方法は次のとおりです。
(0..50).to_a.shuffle.take(x)
これはセットを使用します:
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
Ruby 1.9 では、配列からランダムに選択された要素、または要素を返す Array#sample メソッドが提供されています。#sample の結果には、同じ Array 要素が 2 回含まれることはありません。
(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 つのバージョンを実行しました。
小さいスケールではどれも高速なので、それぞれに 1,000,000 個の数字のリストを作成してもらいました。秒単位の時間は次のとおりです。
いいえ、最後のものはタイプミスではありません。したがって、速度を気にし、数値が 0 から任意の整数であっても問題ない場合、私の正確なコードは次のとおりです。
a = (0...1000000).sort_by{rand}
[*1..99].sample(4) #=> [64, 99, 29, 49]
Array#sample
ドキュメントによると、
要素は、ランダムで一意のインデックスを使用して選択されます
必要な場合SecureRandom
(疑似乱数の代わりにコンピューター ノイズを使用します):
require 'securerandom'
[*1..99].sample(4, random: SecureRandom) #=> [2, 75, 95, 37]
はい、ループなしで、どの番号が選択されたかを追跡せずにこれを行うことができます。これは線形フィードバック シフト レジスタと呼ばれます: 繰り返しのない乱数シーケンスの作成
これで遊んでみてはどうですか?Set や Hash を使用する必要のない一意の乱数。
x = 0
(1..100).map{|iter| x += rand(100)}.shuffle
ハッシュを使用して、これまでに使用した乱数を追跡できます。
seen = {}
max = 100
(1..10).map { |n|
x = rand(max)
while (seen[x])
x = rand(max)
end
x
}
アイテムをリスト/配列に追加するのではなく、セットに追加します。
上記の 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
ありがとうケント。
この方法ではループはありません
Array.new(size) { rand(max) }
require 'benchmark'
max = 1000000
size = 5
Benchmark.realtime do
Array.new(size) { rand(max) }
end
=> 1.9114e-05
可能性のある乱数 (つまり 1 から 100) のリストが有限である場合、Kent の解は適切です。
そうでなければ、ループせずにそれを行う良い方法は他にありません。問題は、重複を取得した場合にループを実行する必要があることです。私のソリューションは効率的であり、ループは配列のサイズを超えてはなりません (つまり、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 のように見えるように書くこともできます:)
ここに1つの解決策があります:
これらの乱数を ~ の間にするr_min
としr_max
ます。リスト内の各要素に対して、乱数を生成し、r
make しlist[i]=list[i-1]+r
ます。これにより、単調に増加する乱数が得られ、一意性が保証されます。
r+list[i-1]
オーバーフローしないr
> 0最初の要素には、r_min
代わりにlist[i-1]
. 完了したら、リストをシャッフルして、要素がそれほど明確に順番に並んでいないようにすることができます。
この方法の唯一の問題は、r_max
生成する要素がまだ残っている場合です。この場合、計算済みの隣接する 2 つの要素にリセットr_min
して、プロセスを繰り返すだけです。r_max
これにより、すでに使用されている数値がない間隔で同じアルゴリズムが効果的に実行されます。リストにデータが入力されるまで、これを続けることができます。
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
同じケントの別の回答から変更された、おそらくより効率的な別の方法:
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 :)
最大値を事前に知っておくと便利な場合は、次の方法で実行できます。
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
すべての値がデッキから排出されるときに取得します。