この場合、MAXは5つだけなので、重複を1つずつ確認できますが、もっと簡単な方法でこれを行うにはどうすればよいですか?たとえば、MAXの値が20の場合はどうなりますか?ありがとう。
int MAX = 5;
for (i = 1 , i <= MAX; i++)
{
drawNum[1] = (int)(Math.random()*MAX)+1;
while (drawNum[2] == drawNum[1])
{
drawNum[2] = (int)(Math.random()*MAX)+1;
}
while ((drawNum[3] == drawNum[1]) || (drawNum[3] == drawNum[2]) )
{
drawNum[3] = (int)(Math.random()*MAX)+1;
}
while ((drawNum[4] == drawNum[1]) || (drawNum[4] == drawNum[2]) || (drawNum[4] == drawNum[3]) )
{
drawNum[4] = (int)(Math.random()*MAX)+1;
}
while ((drawNum[5] == drawNum[1]) ||
(drawNum[5] == drawNum[2]) ||
(drawNum[5] == drawNum[3]) ||
(drawNum[5] == drawNum[4]) )
{
drawNum[5] = (int)(Math.random()*MAX)+1;
}
}
最も簡単な方法は、可能な数字(1..20など)のリストを作成し、Collections.shuffle
でシャッフルすることです。次に、必要な要素をいくつでも使用します。これは、範囲が最後に必要な要素の数と等しい場合(カードのデッキをシャッフルする場合など)に便利です。
(たとえば)1..10,000の範囲内の10個のランダムな要素が必要な場合、これはあまりうまくいきません-あなたは不必要に多くの仕事をすることになります。その時点で、これまでに生成した一連の値を保持し、次の値がまだ存在しないまでループで数値を生成し続ける方がおそらく良いでしょう。
if (max < numbersNeeded)
{
throw new IllegalArgumentException("Can't ask for more numbers than are available");
}
Random rng = new Random(); // Ideally just create one instance globally
// Note: use LinkedHashSet to maintain insertion order
Set<Integer> generated = new LinkedHashSet<Integer>();
while (generated.size() < numbersNeeded)
{
Integer next = rng.nextInt(max) + 1;
// As we're adding to a set, this will automatically do a containment check
generated.add(next);
}
ただし、セットの選択には注意してください-ここでは、挿入順序を維持するため、LinkedHashSet
を非常に慎重に使用しています。
さらに別のオプションは、毎回範囲を縮小し、既存の値を補正することにより、always進歩させることです。たとえば、0..9の範囲の3つの値が必要だとします。最初の反復では、0..9の範囲の任意の数を生成します-4を生成するとしましょう。
2回目の反復では、0〜8の範囲の数値を生成します。生成された数が4未満の場合は、そのままにしておきます。それ以外の場合は、1を追加します。これにより、4なしの0..9の結果範囲が得られます。
3回目の反復で、0〜7の範囲の数値を生成します。生成された数が4未満の場合、そのまま保持します。 4または5の場合は、追加します。 6または7の場合、2つ追加します。そのようにして、結果の範囲は4または6なしの0..9です。
ここに私がそれをする方法があります
import Java.util.ArrayList;
import Java.util.Random;
public class Test {
public static void main(String[] args) {
int size = 20;
ArrayList<Integer> list = new ArrayList<Integer>(size);
for(int i = 1; i <= size; i++) {
list.add(i);
}
Random Rand = new Random();
while(list.size() > 0) {
int index = Rand.nextInt(list.size());
System.out.println("Selected: "+list.remove(index));
}
}
}
尊敬されるスキート氏が指摘したように:
nがランダムに選択したい数字の数であり、Nが選択可能な数字の合計サンプル空間の場合:
//random numbers are 0,1,2,3
ArrayList<Integer> numbers = new ArrayList<Integer>();
Random randomGenerator = new Random();
while (numbers.size() < 4) {
int random = randomGenerator .nextInt(4);
if (!numbers.contains(random)) {
numbers.add(random);
}
}
size
および返される数値のmin
およびmax
の値を使用して、必要な数を指定できる別のアプローチ
public static int getRandomInt(int min, int max) {
Random random = new Random();
return random.nextInt((max - min) + 1) + min;
}
public static ArrayList<Integer> getRandomNonRepeatingIntegers(int size, int min,
int max) {
ArrayList<Integer> numbers = new ArrayList<Integer>();
while (numbers.size() < size) {
int random = getRandomInt(min, max);
if (!numbers.contains(random)) {
numbers.add(random);
}
}
return numbers;
}
それを使用するには、0から25までの7つの数字を返します。
ArrayList<Integer> list = getRandomNonRepeatingIntegers(7, 0, 25);
for (int i = 0; i < list.size(); i++) {
System.out.println("" + list.get(i));
}
シーケンスのすべてのインデックスを生成することは、特にMAX
に対して選択する数値の比率が低い場合(複雑さはO(MAX)
に支配されるようになる場合)、多くの時間がかかるため、一般に悪い考えです。これは、MAX
に対して選択される数値の比率が1に近づくと悪化します。そのため、すべてのシーケンスから選択されたインデックスを削除するのも高価になります(O(MAX^2/2)
に近づく)。しかし、少数の場合、これは一般的にうまく機能し、特にエラーが発生しやすいわけではありません。
コレクションを使用して生成されたインデックスをフィルタリングすることも悪い考えです。インデックスをシーケンスに挿入するのに時間がかかるため、同じ乱数を数回描画できるため、進行は保証されません(ただし、MAX
が十分に大きい場合はありそうもない)。これは複雑さに近いかもしれませんO(k n log^2(n)/2)
、重複を無視し、コレクションが効率的なルックアップのためにツリーを使用すると仮定します(ただし、ツリーノードを割り当て、 リバランス を必要とするかなりの一定のコストk
を使用)。
別のオプションは、ランダム値を最初から一意に生成し、進行が保証されるようにすることです。つまり、最初のラウンドでは、[0, MAX]
のランダムインデックスが生成されます。
items i0 i1 i2 i3 i4 i5 i6 (total 7 items)
idx 0 ^^ (index 2)
2番目のラウンドでは、[0, MAX - 1]
のみが生成されます(1つのアイテムが既に選択されているため):
items i0 i1 i3 i4 i5 i6 (total 6 items)
idx 1 ^^ (index 2 out of these 6, but 3 out of the original 7)
次に、インデックスの値を調整する必要があります。2番目のインデックスがシーケンスの後半(最初のインデックスの後)にある場合、ギャップを考慮するためにインクリメントする必要があります。これをループとして実装して、任意の数の一意のアイテムを選択できます。
短いシーケンスの場合、これは非常に高速なO(n^2/2)
アルゴリズムです。
void RandomUniqueSequence(std::vector<int> &Rand_num,
const size_t n_select_num, const size_t n_item_num)
{
assert(n_select_num <= n_item_num);
Rand_num.clear(); // !!
// b1: 3187.000 msec (the fastest)
// b2: 3734.000 msec
for(size_t i = 0; i < n_select_num; ++ i) {
int n = n_Rand(n_item_num - i - 1);
// get a random number
size_t n_where = i;
for(size_t j = 0; j < i; ++ j) {
if(n + j < Rand_num[j]) {
n_where = j;
break;
}
}
// see where it should be inserted
Rand_num.insert(Rand_num.begin() + n_where, 1, n + n_where);
// insert it in the list, maintain a sorted sequence
}
// tier 1 - use comparison with offset instead of increment
}
n_select_num
は5で、n_number_num
はMAX
です。 n_Rand(x)
は、[0, x]
(両端を含む)でランダムな整数を返します。バイナリ検索を使用して挿入ポイントを見つけることにより、多くのアイテム(5ではなく500など)を選択する場合、これを少し速くすることができます。そのためには、要件を満たしていることを確認する必要があります。
比較n + j < Rand_num[j]
を使用してバイナリ検索を実行します。n < Rand_num[j] - j
。 Rand_num[j] - j
がソートシーケンスRand_num[j]
のソートシーケンスであることを示す必要があります。元のRand_num
の2つの要素間の最小距離は1であるため、これは幸いにも簡単に表示されます(生成された数値は一意であるため、常に少なくとも1の差があります)。同時に、すべての要素からインデックスj
を引くとRand_num[j]
、インデックスの違いは正確に1です。したがって、「最悪」の場合、一定のシーケンスが得られますが、減少はしません。したがって、バイナリ検索を使用して、O(n log(n))
アルゴリズムを生成できます。
struct TNeedle { // in the comparison operator we need to make clear which argument is the needle and which is already in the list; we do that using the type system.
int n;
TNeedle(int _n)
:n(_n)
{}
};
class CCompareWithOffset { // custom comparison "n < Rand_num[j] - j"
protected:
std::vector<int>::iterator m_p_begin_it;
public:
CCompareWithOffset(std::vector<int>::iterator p_begin_it)
:m_p_begin_it(p_begin_it)
{}
bool operator ()(const int &r_value, TNeedle n) const
{
size_t n_index = &r_value - &*m_p_begin_it;
// calculate index in the array
return r_value < n.n + n_index; // or r_value - n_index < n.n
}
bool operator ()(TNeedle n, const int &r_value) const
{
size_t n_index = &r_value - &*m_p_begin_it;
// calculate index in the array
return n.n + n_index < r_value; // or n.n < r_value - n_index
}
};
そして最後に:
void RandomUniqueSequence(std::vector<int> &Rand_num,
const size_t n_select_num, const size_t n_item_num)
{
assert(n_select_num <= n_item_num);
Rand_num.clear(); // !!
// b1: 3578.000 msec
// b2: 1703.000 msec (the fastest)
for(size_t i = 0; i < n_select_num; ++ i) {
int n = n_Rand(n_item_num - i - 1);
// get a random number
std::vector<int>::iterator p_where_it = std::upper_bound(Rand_num.begin(), Rand_num.end(),
TNeedle(n), CCompareWithOffset(Rand_num.begin()));
// see where it should be inserted
Rand_num.insert(p_where_it, 1, n + p_where_it - Rand_num.begin());
// insert it in the list, maintain a sorted sequence
}
// tier 4 - use binary search
}
これを3つのベンチマークでテストしました。最初に、7つの項目から3つの数値が選択され、選択された項目のヒストグラムが10,000回の実行で累積されました。
4265 4229 4351 4267 4267 4364 4257
これは、7つの項目のそれぞれがほぼ同じ回数選択されたことを示しており、アルゴリズムによる明らかなバイアスはありません。すべてのシーケンスの正確さ(内容の一意性)もチェックされました。
2番目のベンチマークでは、5000のアイテムから7つの数字を選択しました。アルゴリズムのいくつかのバージョンの時間は、10,000,000回以上実行されました。結果は、コード内のコメントにb1
として示されます。アルゴリズムの単純なバージョンはわずかに高速です。
3番目のベンチマークでは、5000個のアイテムから700個の数字を選択しました。アルゴリズムのいくつかのバージョンの時間が再び累積され、今回は10,000回以上実行されました。結果は、コード内のコメントにb2
として示されます。アルゴリズムのバイナリ検索バージョンは、単純なバージョンよりも2倍以上高速になりました。
2番目の方法は、マシン上でcca 75を超えるアイテムを選択する場合に高速になります(いずれかのアルゴリズムの複雑さは、アイテムの数MAX
に依存しないことに注意してください)。
上記のアルゴリズムは、昇順で乱数を生成することに注意してください。しかし、番号が生成された順序で保存される別の配列を追加し、代わりにそれを返すことは簡単です(ごくわずかな追加コストO(n)
で)。出力をシャッフルする必要はありません。それははるかに遅くなります。
ソースはC++であり、マシンにはJavaがありませんが、概念は明確でなければなりません。
編集:
娯楽のために、すべてのインデックスを含むリストを生成するアプローチも実装しました0 .. MAX
、それらをランダムに選択し、リストから削除して一意性を保証します。非常に高いMAX
(5000)を選択したため、パフォーマンスは壊滅的です。
// b1: 519515.000 msec
// b2: 20312.000 msec
std::vector<int> all_numbers(n_item_num);
std::iota(all_numbers.begin(), all_numbers.end(), 0);
// generate all the numbers
for(size_t i = 0; i < n_number_num; ++ i) {
assert(all_numbers.size() == n_item_num - i);
int n = n_Rand(n_item_num - i - 1);
// get a random number
Rand_num.Push_back(all_numbers[n]); // put it in the output list
all_numbers.erase(all_numbers.begin() + n); // erase it from the input
}
// generate random numbers
また、set
(C++コレクション)を使用したアプローチを実装しましたが、これは実際にベンチマークb2
で2番目にあり、バイナリ検索のアプローチよりも約50%遅いだけです。 set
はバイナリツリーを使用するため、挿入コストはバイナリ検索に似ているため、これは理解できます。唯一の違いは、アイテムが重複する可能性があることです。これにより、進行が遅くなります。
// b1: 20250.000 msec
// b2: 2296.000 msec
std::set<int> numbers;
while(numbers.size() < n_number_num)
numbers.insert(n_Rand(n_item_num - 1)); // might have duplicates here
// generate unique random numbers
Rand_num.resize(numbers.size());
std::copy(numbers.begin(), numbers.end(), Rand_num.begin());
// copy the numbers from a set to a vector
完全なソースコードは here です。
LFSRで「ランダムな」順序番号を実行する別の方法があります。
http://en.wikipedia.org/wiki/Linear_feedback_shift_register
この手法を使用すると、インデックスによって順序付けられた乱数を実現し、値が重複しないようにすることができます。
しかし、ランダムな生成は決定論的であるため、これらは真の乱数ではありません。
ただし、ケースに応じてシャッフルを使用する場合、この手法を使用して乱数生成の処理量を減らすことができます。
ここに、JavaのLFSRアルゴリズムがあります(覚えていないところに取りました)。
public final class LFSR {
private static final int M = 15;
// hard-coded for 15-bits
private static final int[] TAPS = {14, 15};
private final boolean[] bits = new boolean[M + 1];
public LFSR() {
this((int)System.currentTimeMillis());
}
public LFSR(int seed) {
for(int i = 0; i < M; i++) {
bits[i] = (((1 << i) & seed) >>> i) == 1;
}
}
/* generate a random int uniformly on the interval [-2^31 + 1, 2^31 - 1] */
public short nextShort() {
//printBits();
// calculate the integer value from the registers
short next = 0;
for(int i = 0; i < M; i++) {
next |= (bits[i] ? 1 : 0) << i;
}
// allow for zero without allowing for -2^31
if (next < 0) next++;
// calculate the last register from all the preceding
bits[M] = false;
for(int i = 0; i < TAPS.length; i++) {
bits[M] ^= bits[M - TAPS[i]];
}
// shift all the registers
for(int i = 0; i < M; i++) {
bits[i] = bits[i + 1];
}
return next;
}
/** returns random double uniformly over [0, 1) */
public double nextDouble() {
return ((nextShort() / (Integer.MAX_VALUE + 1.0)) + 1.0) / 2.0;
}
/** returns random boolean */
public boolean nextBoolean() {
return nextShort() >= 0;
}
public void printBits() {
System.out.print(bits[M] ? 1 : 0);
System.out.print(" -> ");
for(int i = M - 1; i >= 0; i--) {
System.out.print(bits[i] ? 1 : 0);
}
System.out.println();
}
public static void main(String[] args) {
LFSR rng = new LFSR();
Vector<Short> vec = new Vector<Short>();
for(int i = 0; i <= 32766; i++) {
short next = rng.nextShort();
// just testing/asserting to make
// sure the number doesn't repeat on a given list
if (vec.contains(next))
throw new RuntimeException("Index repeat: " + i);
vec.add(next);
System.out.println(next);
}
}
}
繰り返しのない乱数を生成する最も効率的で基本的な方法は、この擬似コードで説明されています。ネストされたループやハッシュ化されたルックアップは必要ありません。
// get 5 unique random numbers, possible values 0 - 19
// (assume desired number of selections < number of choices)
const int POOL_SIZE = 20;
const int VAL_COUNT = 5;
declare Array mapping[POOL_SIZE];
declare Array results[VAL_COUNT];
declare i int;
declare r int;
declare max_Rand int;
// create mapping array
for (i=0; i<POOL_SIZE; i++) {
mapping[i] = i;
}
max_Rand = POOL_SIZE-1; // start loop searching for maximum value (19)
for (i=0; i<VAL_COUNT; i++) {
r = Random(0, max_Rand); // get random number
results[i] = mapping[r]; // grab number from map array
mapping[r] = max_Rand; // place item past range at selected location
max_Rand = max_Rand - 1; // reduce random scope by 1
}
最初の反復で乱数3を生成して開始するとします(0〜19)。これにより、results [0] = mapping [3]、つまり値3になります。その後、mapping [3]を19に割り当てます。
次の反復では、乱数は5(0〜18)でした。これにより、results [1] = mapping [5]、つまり値5になります。その後、mapping [5]を18に割り当てます。
ここで、次の反復で再び3(0〜17)を選択したとします。 results [2]にはmapping [3]の値が割り当てられますが、現在、この値は3ではなく19です。
この同じ保護は、同じ番号を5回続けて取得した場合でも、すべての番号に対して持続します。たとえば、乱数ジェネレーターが0を5回連続で与えた場合、結果は[0、19、18、17、16]になります。
同じ番号を2回取得することはありません。
あなたの問題は、n個の要素のコレクションからk個の要素をランダムに選択することで減少するようです。したがって、Collections.shuffleの答えは正しいですが、非効率的であると指摘されているように、そのO(n)です。
Wikipedia:Fisher–Yates shuffle は、配列が既に存在する場合、O(k)バージョンを持ちます。あなたの場合、要素の配列はなく、要素の配列の作成は非常に高価になる可能性があります。たとえば、maxが20ではなく10000000だった場合などです。
シャッフルアルゴリズムでは、すべての要素がそのインデックスに等しいサイズnの配列を初期化し、最大値が前の範囲よりも小さい範囲の各数値をk個の乱数を選択し、配列の終わりに向かって要素を交換します。
ハッシュマップを使ってO(k)の時間に同じ操作を行うことができますが、その種の苦痛は認めます。これは、kがnよりはるかに小さい場合にのみ価値があることに注意してください。 (つまり、k〜lg(n)など)、そうでなければシャッフルを直接使用する必要があります。
シャッフルアルゴリズムのバッキング配列の効率的な表現としてハッシュマップを使用します。インデックスと等しい配列の要素は、マップに表示する必要はありません。これにより、サイズnの配列を一定の時間で表現できます。初期化に時間はかかりません。
K個の乱数を選択します。1番目は0〜n-1、2番目は0〜n-2、3番目は0〜n-3など、n〜kの範囲です。
乱数をスワップのセットとして扱います。最初のランダムインデックスは最終位置にスワップします。 2番目のランダムインデックスは、2番目から最後の位置にスワップします。ただし、バッキング配列に対して作業する代わりに、ハッシュマップに対して作業します。ハッシュマップには、位置がずれているすべてのアイテムが保存されます。
int getValue(i)
{
if (map.contains(i))
return map[i];
return i;
}
void setValue(i, val)
{
if (i == val)
map.remove(i);
else
map[i] = val;
}
int[] chooseK(int n, int k)
{
for (int i = 0; i < k; i++)
{
int randomIndex = nextRandom(0, n - i); //(n - i is exclusive)
int desiredIndex = n-i-1;
int valAtRandom = getValue(randomIndex);
int valAtDesired = getValue(desiredIndex);
setValue(desiredIndex, valAtRandom);
setValue(randomIndex, valAtDesired);
}
int[] output = new int[k];
for (int i = 0; i < k; i++)
{
output[i] = (getValue(n-i-1));
}
return output;
}
Setインターフェースを実装するクラスの1つ( API )を使用し、生成する各番号をSet.add()を使用して挿入できます。
戻り値がfalseの場合、以前に番号がすでに生成されていることがわかります。
Java-8
の方がはるかに簡単です:
Stream.generate(new Random()::ints)
.distinct()
.limit(16) // whatever limit you might need
.toArray(Integer[]::new);
これをすべて行う代わりに、LinkedHashSet
オブジェクトを作成し、Math.random()
関数によってそれに乱数を作成します。重複したエントリが発生した場合、LinkedHashSet
オブジェクトはその番号をリストに追加しません。 ...このコレクションクラスでは重複値は許可されないため..最終的に重複値を持たない乱数のリストを取得します....:D
次のコードは、以前に生成されなかった[1、m]の間のシーケンス乱数を作成します。
public class NewClass {
public List<Integer> keys = new ArrayList<Integer>();
public int Rand(int m) {
int n = (int) (Math.random() * m + 1);
if (!keys.contains(n)) {
keys.add(n);
return n;
} else {
return Rand(m);
}
}
public static void main(String[] args) {
int m = 4;
NewClass ne = new NewClass();
for (int i = 0; i < 4; i++) {
System.out.println(ne.Rand(m));
}
System.out.println("list: " + ne.keys);
}
}
カードバッチのアルゴリズムがあります:番号の順序付き配列(「カードバッチ」)を作成し、反復ごとにランダムな位置にある番号を選択します(もちろん「カードバッチ」から選択した番号を削除します)。
それは本当にあなたがランダム生成を必要とするものに正確に依存していますが、ここに私の見解があります。
最初に、乱数を生成するためのスタンドアロンメソッドを作成します。制限を考慮してください。
public static int newRandom(int limit){
return generatedRandom.nextInt(limit); }
次に、値を比較する非常に単純な決定構造を作成します。これは、2つの方法のいずれかで実行できます。検証する数値が非常に限られている場合、単純なIFステートメントで十分です。
public static int testDuplicates(int int1, int int2, int int3, int int4, int int5){
boolean loopFlag = true;
while(loopFlag == true){
if(int1 == int2 || int1 == int3 || int1 == int4 || int1 == int5 || int1 == 0){
int1 = newRandom(75);
loopFlag = true; }
else{
loopFlag = false; }}
return int1; }
上記では、int5を介してint1とint2を比較し、ランダムにゼロがないことを確認します。
これらの2つの方法を導入すると、次のことが可能になります。
num1 = newRandom(limit1);
num2 = newRandom(limit1);
num3 = newRandom(limit1);
num4 = newRandom(limit1);
num5 = newRandom(limit1);
に続く:
num1 = testDuplicates(num1, num2, num3, num4, num5);
num2 = testDuplicates(num2, num1, num3, num4, num5);
num3 = testDuplicates(num3, num1, num2, num4, num5);
num4 = testDuplicates(num4, num1, num2, num3, num5);
num5 = testDuplicates(num5, num1, num2, num3, num5);
検証するリストが長い場合は、より複雑な方法を使用すると、コードの明瞭さとリソースの処理の両方でより良い結果が得られます。
お役に立てれば。このサイトは私を大いに助けてくれました。少なくともTRYを助けなければならないと感じました。
Collections.shuffleよりも効率的で扱いやすい整数のソリューションがあります。
問題は、セット内の選択されていないアイテムのみからアイテムを連続して選択し、他の場所で順番に設定することと同じです。これは、カードをランダムに配ったり、帽子やビンから抽選券を引いたりするのとまったく同じです。
このアルゴリズムは、任意の配列をロードし、ロードの最後にランダムな順序を実現するために機能します。また、Listコレクション(またはその他のインデックス付きコレクション)に追加し、追加の最後にコレクション内のランダムシーケンスを実現するためにも機能します。
一度作成した単一の配列、またはリストなどの数値的に順序付けられたコレクションを使用して実行できます。配列の場合、初期配列サイズは、目的の値をすべて含む正確なサイズである必要があります。事前に発生する値の数がわからない場合は、サイズが不変ではないArrayListやListなどの数値的に順序付けられたコレクションを使用しても機能します。 Integer.MAX_VALUE(2,000,000,000をわずかに超える)までの任意のサイズの配列に対して普遍的に機能します。リストオブジェクトには同じインデックス制限があります。そのサイズの配列に到達する前に、マシンのメモリが不足する場合があります。オブジェクト型に型付けされた配列をロードし、配列をロードした後にコレクションに変換する方が効率的です。これは、ターゲットコレクションに数値インデックスが付けられていない場合に特に当てはまります。
このアルゴリズムは、書かれているとおり、重複のない非常に均一な分布を作成します。非常に重要な側面の1つは、次のアイテムを現在のサイズ+ 1まで挿入できるようにする必要があることです。したがって、2番目のアイテムについては、ロケーション0またはロケーション1に格納できる可能性があります。 20番目のアイテムについては、0〜19の任意の場所に保存できます。最初のアイテムが他の場所に配置されるのと同様に、最初のアイテムは0に配置されます。次の新しいアイテムは、次の新しい場所を含め、どこにでも行くことができます。
シーケンスのランダム性は、乱数ジェネレーターのランダム性と同じくらいランダムになります。
このアルゴリズムは、参照タイプを配列内のランダムな場所にロードするためにも使用できます。これは配列で機能するため、コレクションでも機能します。つまり、コレクションを作成してからシャッフルしたり、オブジェクトを挿入する順序に関係なく並べたりする必要はありません。コレクションに必要なのは、コレクション内の任意の場所にアイテムを挿入または追加する機能のみです。
// RandomSequence.Java
import Java.util.Random;
public class RandomSequence {
public static void main(String[] args) {
// create an array of the size and type for which
// you want a random sequence
int[] randomSequence = new int[20];
Random randomNumbers = new Random();
for (int i = 0; i < randomSequence.length; i++ ) {
if (i == 0) { // seed first entry in array with item 0
randomSequence[i] = 0;
} else { // for all other items...
// choose a random pointer to the segment of the
// array already containing items
int pointer = randomNumbers.nextInt(i + 1);
randomSequence[i] = randomSequence[pointer];
randomSequence[pointer] = i;
// note that if pointer & i are equal
// the new value will just go into location i and possibly stay there
// this is VERY IMPORTANT to ensure the sequence is really random
// and not biased
} // end if...else
} // end for
for (int number: randomSequence) {
System.out.printf("%2d ", number);
} // end for
} // end main
} // end class RandomSequence
ここ は、ランダム化された配列を迅速に作成するための効率的なソリューションです。ランダム化後、配列のn
番目の要素e
を選択し、n
をインクリメントしてe
を返すことができます。このソリューションには、乱数を取得するためのO(1)と初期化のためのO(n)がありますが、nが十分に大きくなるとトレードオフとして十分な量のメモリが必要になります。