重複のない乱数の作成


88

この場合、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;
        }

}

3
多くの(疑似)乱数ジェネレーターは、完全な「サイクル」の間繰り返されません。もちろん、問題は、それらの完全な「サイクル」が数十億または数兆の値であり、それらが生成する値がそれらの数十億または数兆の値のいずれかである可能性があることです。理論的には、5または10などの「サイクル」を持つ乱数ジェネレーターを作成できますが、それはおそらく価値があるよりも厄介です。
ホットリック2011年

1
また、繰り返さない乱数ジェネレーターは「より少ない」ランダムです。MAX= 5で3つの数値を読み取った場合、50%の確率で次の数値を推測できます。4つの数値を読み取った場合、100%で次の数値がわかります。承知しました!
icza 2014



回答:


149

最も簡単な方法は、可能な番号(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には注意してください。ここで気にする挿入順序を維持するため、私は非常に慎重に使用しました。

さらに別のオプションは、毎回範囲を縮小し、既存の値を補正することによって、常に進歩を遂げることです。したがって、たとえば、0..9の範囲の3つの値が必要だとします。最初の反復で、0..9の範囲の任意の数を生成します-たとえば、4を生成するとします。

2回目の反復では、0..8の範囲の数値を生成します。生成された数が4未満の場合は、そのままにしておきます...それ以外の場合は、1を追加します。これにより、4なしで0..9の結果範囲が得られます。そのようにして7を取得するとします。

3回目の反復で、0..7の範囲の数値を生成します。生成された数が4未満の場合は、そのままにしておきます。4または5の場合は、1つ追加します。6または7の場合は、2つ追加します。そうすれば、結果の範囲は4または6なしで0..9になります。


可能な値の配列を生成し、ランダムに1つを選択し(乱数mod配列サイズ)、選択した数を削除(および保存)してから、繰り返します。
ホットリック2011年

または、完全なサイクルでランダムジェネレーターを使用し(素数に基づくものは、対応する小さなサイクルで小さな素数を使用できます)、値を範囲外にドロップします。
Paul de Vrieze 2011年

「さらに別のオプションは常に進歩することです」は、WAAAAYの方が解決策として優れています。反映するために編集してください。そして、この素晴らしい答えをありがとう。
user123321 2012年

1
@musselwhizzle:近いうちに時間を見つけようとします。「WAAAYbetter」についてはよくわかりませんが、効率は向上しますが、「明らかに正しい」とは言えません。読みやすさのためにパフォーマンスを犠牲にして喜んでいることがよくあります。
Jon Skeet 2012年

@Deepthi:OPが望む最大値は何でも-質問によると。
Jon Skeet 2012年

19

これが私がそれをする方法です

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は、選択可能な数字の合計サンプル空間であります:

  1. 場合のn << N、あなたはちょうどあなたが選んだという数値を格納し、選択した番号がそれであるかどうかを確認するために、リストを確認してください。
  2. 場合はN〜= N、おそらく全体のサンプル空間を含むリストを移入して、あなたがそれらを選択すると、それから番号を削除することによって、私の方法を使用する必要があります。

リストはLinkedListである必要があり、arraylistからランダムインデックスを削除することは非常に非効率的です
RiccardoCasatta18年

@RiccardoCasattaあなたはあなたの主張の源を持っていますか?リンクリストをトラバースすることも非常にパフォーマンスが高いとは想像できません。参照:stackoverflow.com/a/6103075/79450
Catchwa 2018年

私はそれをテストしました、そしてあなたは正しいです、私は私のコメントを削除するべきですか?
リカルドカサッタ2018年

@RiccardoCasatta他の人は私たちの行き来が役に立つと思うかもしれません
Catchwa 2018年

13
//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);
    }
}

これは、多数の場合、ひどいパフォーマンスになります。ArrayList.containsはリストを反復処理しています。代わりにセットを使用する方がはるかにクリーンです。セットが含まれているかどうかを確認する必要はありません。追加するだけでパフォーマンスが向上します。
kfox

5

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);
        }
    }
}

4

あなたがしたいどのように多くの番号を指定することができます別のアプローチsizeminして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));
    }

4

これは、次の場合にはるかに簡単になりjava-8ます。

Stream.generate(new Random()::ints)
            .distinct()
            .limit(16) // whatever limit you might need
            .toArray(Integer[]::new);

3

繰り返しのない乱数を使用するための最も効率的で基本的な方法は、この擬似コードによって説明されます。ネストされたループやハッシュルックアップは必要ありません。

// 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回取得することはありません。


私はこれがあなたがそれを鳴らすのと同じくらいランダムであるとは思わない。標準のランダム性検定に合格していますか?; スペクトルの終わり近くに数字が集中しているように見えます。
tucuxi 2013年

これが基本ケースです。プールは{a、b、c}です。2つの非反復要素が必要です。アルゴリズムに従って、描画できる組み合わせとその結果を次に示します。0,0:a、c 0,1:a、b 1,0:b、a 1,1:b、c 2,0:c、a 2、 1:c、bスコア:a-4、b-4、c-4
blackcatweb 2013年

3

シーケンスのすべてのインデックスを生成することは、特に選択する数値の比率MAXが低い場合(複雑さがによって支配される場合)、時間がかかる可能性があるため、一般的には悪い考えO(MAX)です。選択する数の比率がMAX1に近づくと、これはさらに悪化します。選択したインデックスをすべてのシーケンスから削除することもコストがかかるためです(アプローチしますO(MAX^2/2))。ただし、少数の場合、これは一般にうまく機能し、特にエラーが発生しやすいわけではありません。

コレクションを使用して生成されたインデックスをフィルタリングすることも悪い考えです。シーケンスにインデックスを挿入するのに時間がかかり、同じ乱数を数回描画できるため、進行が保証されません(ただし、十分MAXに大きい場合はほとんどありません)。 )。これは複雑さ
O(k n log^2(n)/2)に近い可能性があり、重複を無視し、コレクションが効率的なルックアップのためにツリーを使用すると仮定します(ただしk、ツリーノードの割り当てにかなりの一定のコストがかかり、場合によってはリバランスが必要になります))。

もう1つのオプションは、最初からランダムな値を一意に生成して、進行が確実に行われるようにすることです。つまり、最初のラウンドで、ランダムなインデックス[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_numMAXです。でn_Rand(x)ランダムな整数を返します[0, x](包括的)。バイナリ検索を使用して挿入ポイントを見つけることにより、多くのアイテム(たとえば、5ではなく500)を選択する場合、これを少し速くすることができます。そのためには、要件を満たしていることを確認する必要があります。

n + j < rand_num[j]同じ比較で二分探索を行い
n < rand_num[j] - jます。それrand_num[j] - jがまだソートされたシーケンスのソートされたシーケンスであることを示す必要がありますrand_num[j]。オリジナルの2つの要素間の最小距離rand_numは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

完全なソースコードはこちらです。


2

Setインターフェイス(API)を実装するクラスの1つを使用し、生成する各数値をSet.add()を使用して挿入することができます。

戻り値がfalseの場合、その番号は以前に生成されていることがわかります。


2

これらすべてを行う代わりにLinkedHashSetMath.random()関数LinkedHashSetごとにオブジェクトと乱数を作成します....重複するエントリが発生した場合、オブジェクトはその番号をリストに追加しません...このコレクションクラスでは重複する値は許可されないため..最後に、重複する値を持たない乱数のリストを取得します....:D


2

あなたの問題は、n個の要素のコレクションからランダムにk個の要素を選択することで減少するようです。したがって、Collections.shuffleの答えは正しいですが、非効率的であると指摘されているように、そのO(n)です。

ウィキペディア:フィッシャー-イェーツシャッフルは、配列がすでに存在する場合のO(k)バージョンがあります。あなたの場合、要素の配列はなく、要素の配列の作成は非常にコストがかかる可能性があります。たとえば、maxが20ではなく10000000の場合です。

シャッフルアルゴリズムでは、すべての要素がインデックスに等しいサイズnの配列を初期化し、前の範囲よりも最大1小さい範囲内の各数値にk個の乱数を選択し、配列の最後に向かって要素を交換します。

私はその種の苦痛を認めますが、ハッシュマップを使用してO(k)時間で同じ操作を行うことができます。これは、kがnよりはるかに小さい場合にのみ価値があることに注意してください。(つまり、k〜lg(n)など)。それ以外の場合は、シャッフルを直接使用する必要があります。

ハッシュマップは、シャッフルアルゴリズムのバッキング配列の効率的な表現として使用します。インデックスと等しい配列の要素は、マップに表示される必要はありません。これにより、サイズnの配列を一定時間で表すことができ、初期化に時間を費やすことはありません。

  1. k個の乱数を選択します。最初の乱数は0からn-1の範囲で、2番目の乱数は0からn-2、3番目の乱数は0からn-3というように続きます。

  2. 乱数をスワップのセットとして扱います。最初のランダムインデックスは最終位置にスワップします。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; }


creating the array of elements could be very expensive-配列の作成はシャッフルよりもコストがかかるのはなぜですか?この点で悲観的な理由はまったくないと思います:
Wolf

1

次のコードは、以前に生成されなかった[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);
    }
}

0

カードバッチのアルゴリズムがあります。順序付けられた番号の配列(「カードバッチ」)を作成し、反復ごとにランダムな位置で番号を選択します(もちろん「カードバッチ」から選択した番号を削除します)。


0

これは、ランダム化された配列を高速に作成するための効率的なソリューションです。ランダム化後、配列のn-番目の要素eを選択し、インクリメントnして返すことができますe。このソリューションには、乱数を取得するためのO(1)と初期化のためのO(n)がありますが、トレードオフとして、nが十分に大きくなると、かなりの量のメモリが必要になります。


0

Collections.shuffleよりも、整数の方が効率的で面倒な解決策はありません。

問題は、セット内の選択されていないアイテムのみからアイテムを連続して選択し、別の場所に順番に設定することと同じです。これは、ランダムにカードを配ったり、帽子やゴミ箱から当選したラッフルのチケットを引いたりするのとまったく同じです。

このアルゴリズムは、任意の配列をロードし、ロードの最後にランダムな順序を実現するために機能します。また、リストコレクション(またはその他のインデックス付きコレクション)に追加し、追加の最後にコレクション内でランダムなシーケンスを実現するためにも機能します。

これは、1回作成された単一の配列、またはリストなどの数値順に並べられた集合を使用して実行できます。配列の場合、初期配列サイズは、意図したすべての値を含む正確なサイズである必要があります。事前にいくつの値が発生する可能性があるかわからない場合は、サイズが不変ではないArrayListやListなどの数値順に並べられたコレクションを使用することもできます。これは、2,000,000,000をわずかに超えるInteger.MAX_VALUEまでの任意のサイズの配列に対して普遍的に機能します。リストオブジェクトには同じインデックス制限があります。そのサイズの配列に到達する前に、マシンのメモリが不足する可能性があります。配列をロードした後、オブジェクトタイプに型指定された配列をロードし、それを何らかのコレクションに変換する方が効率的な場合があります。これは、ターゲットコレクションに数値インデックスが付けられていない場合に特に当てはまります。

このアルゴリズムは、記述されているとおり、重複がない非常に均一な分布を作成します。非常に重要な側面の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

0

それは本当にすべて、ランダム生成が何のために必要かによって異なりますが、これが私の見解です。

まず、乱数を生成するためのスタンドアロンメソッドを作成します。必ず制限を考慮してください。

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;    }

上記では、int1とint2からint5を比較し、ランダムにゼロがないことを確認しています。

これらの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も助けなければならないと感じました。



0

最も簡単な方法は、nanoDateTimeを長い形式として使用することです。System.nanoTime();

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.