Javaで一意の乱数を生成する


87

0〜100の乱数を取得しようとしています。ただし、シーケンスで繰り返されるのではなく、一意である必要があります。たとえば、5つの番号を取得した場合、82,12,53,12,32ではなく82,12,53,64,32になるはずですが、これを使用しましたが、同じ番号が順番に生成されます。

Random rand = new Random();
selected = rand.nextInt(100);

5
範囲のランダム順列を作成することもできますが1..100(そのための有名なアルゴリズムがあります)、最初のn要素を決定した後で停止します。
Kerrek SB 2011



回答:


143
  • リスト構造で、範囲内の各番号を順番に追加します。
  • シャッフルします。
  • 最初の「n」を取ります。

これが簡単な実装です。これにより、1〜10の範囲の3つの一意の乱数が出力されます。

import java.util.ArrayList;
import java.util.Collections;

public class UniqueRandomNumbers {

    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        for (int i=1; i<11; i++) {
            list.add(new Integer(i));
        }
        Collections.shuffle(list);
        for (int i=0; i<3; i++) {
            System.out.println(list.get(i));
        }
    }
}

Mark Byersが現在削除されている回答で指摘しているように、元のアプローチによる修正の最初の部分は、単一のRandomインスタンスのみを使用することです。

それが数字が同じになる原因です。Randomインスタンスは、ミリ秒単位で現在の時刻で播種されます。特定のシード値の場合、「random」インスタンスはまったく同じ一連の疑似乱数を返します。

public Integer​(int value)コンストラクタはdeprecatedJava9以降であることに注意してください。

最初のforループは、次のように簡単に変更できます。

for (int i = 1; i < 11; i++) {
  list.add(i);
}

3
単一のランダムなインスタンスを指摘して+1の質問に答えます。:)
マークバイアーズ2011年

範囲全体をシャッフルする必要はありません。n個の一意の番号が必要な場合は、Fisher-Yatesシャッフルを使用して最初のn個の位置をシャッフルするだけで済みます。これは、大きなリストと小さなnに役立つ場合があります。
ロッサム

59

Javaの8+を使用すると、使用できるints方法をRandom取得するにはIntStream、ランダムな値のをdistinctし、limit独特のランダムな値の数にストリームを減らすために。

ThreadLocalRandom.current().ints(0, 100).distinct().limit(5).forEach(System.out::println);

Random代わりに必要に応じてLongStreamsとDoubleStreamsを作成するメソッドもあります。

上記の例が現在実装されているため、範囲内のすべての(または大量の)数値がランダムな順序で必要な場合は、すべての数値をリストに追加してシャッフルし、最初のnを取得する方が効率的です。要求された範囲で乱数を生成し、それらをセットに渡すことによって(Rob Kieltyの回答と同様)、新しい一意の番号を生成する確率が見つかるたびに減少するため、制限に渡された量よりも多くを生成する必要があります。他の方法の例を次に示します。

List<Integer> range = IntStream.range(0, 100).boxed()
        .collect(Collectors.toCollection(ArrayList::new));
Collections.shuffle(range);
range.subList(0, 99).forEach(System.out::println);

ベンチマークしているコードにこれが必要Arrays#setAll()で、ストリームよりも少し高速です。したがって、 `Integer []インデックス= new Integer [n]; Arrays.setAll(indices、i-> i); Collections.shuffle(Arrays.asList(indices)); Arrays.stream(indices).mapToInt(Integer :: intValue).toArray();を返します。`
AbuNassar 2018

18
  1. 100個の数字の配列を作成し、それらの順序をランダム化します。
  2. 範囲が100の疑似乱数ジェネレーターを考案します。
  3. 100個の要素のブール配列を作成し、その数を選択するときに要素をtrueに設定します。次の番号を選択するときは、配列を確認し、配列要素が設定されているかどうかを再試行してください。(long個々のビットにアクセスするためにシフトおよびマスクする場所の配列を使用して、クリアしやすいブール配列を作成できます。)

2
代替アプローチの場合は+1。pick()例です。
trashgod 2011年

1
ブール配列を使用する代わりに、を使用してHashSet、すでに生成した数値を格納し、その数値をすでに生成したcontainsかどうかをテストするために使用できます。HashSetおそらくブール配列よりも少し遅くなりますが、少ないメモリを取るだろう。
Rory O'Kane 2013年

1
@ RoryO'Kane-long [2]の配列として実装された場合、ブール配列はより少ないスペースで済むと確信しています。HashSetをこれほど小さくすることはできません。
Hot Licks 2013年

最後のアプローチは、シーケンス全体を生成するための明確に定義されたステップ数がないため、少し醜いです。また、車輪の再発明をする必要はありません-BitSet
Pavel Horal 2014


13

この方法は言及する価値があると思います。

   private static final Random RANDOM = new Random();    
   /**
     * Pick n numbers between 0 (inclusive) and k (inclusive)
     * While there are very deterministic ways to do this,
     * for large k and small n, this could be easier than creating
     * an large array and sorting, i.e. k = 10,000
     */
    public Set<Integer> pickRandom(int n, int k) {
        final Set<Integer> picked = new HashSet<>();
        while (picked.size() < n) {
            picked.add(RANDOM.nextInt(k + 1));
        }
        return picked;
    }

9

セットの一意のプロパティだけでなく、セットset.add()への追加が失敗したときに返されるブール値のfalseも使用するように、Anandの回答をリファクタリングしました。

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class randomUniqueNumberGenerator {

    public static final int SET_SIZE_REQUIRED = 10;
    public static final int NUMBER_RANGE = 100;

    public static void main(String[] args) {
        Random random = new Random();

        Set set = new HashSet<Integer>(SET_SIZE_REQUIRED);

        while(set.size()< SET_SIZE_REQUIRED) {
            while (set.add(random.nextInt(NUMBER_RANGE)) != true)
                ;
        }
        assert set.size() == SET_SIZE_REQUIRED;
        System.out.println(set);
    }
}

1
良いアイデア。ただし、重要なマーク-SET_SIZE_REQUIRED十分に大きい場合(たとえば、それを超えると、NUMBER_RANGE / 2予想される実行時間がはるかに長くなります。
noamgot18年

5

私はこれをそのように作りました。

    Random random = new Random();
    ArrayList<Integer> arrayList = new ArrayList<Integer>();

    while (arrayList.size() < 6) { // how many numbers u need - it will 6
        int a = random.nextInt(49)+1; // this will give numbers between 1 and 50.

        if (!arrayList.contains(a)) {
            arrayList.add(a);
        }
    }

4

これは、一意の乱数を生成するために機能します................

import java.util.HashSet;
import java.util.Random;

public class RandomExample {

    public static void main(String[] args) {
        Random rand = new Random();
        int e;
        int i;
        int g = 10;
        HashSet<Integer> randomNumbers = new HashSet<Integer>();

        for (i = 0; i < g; i++) {
            e = rand.nextInt(20);
            randomNumbers.add(e);
            if (randomNumbers.size() <= 10) {
                if (randomNumbers.size() == 10) {
                    g = 10;
                }
                g++;
                randomNumbers.add(e);
            }
        }
        System.out.println("Ten Unique random numbers from 1 to 20 are  : " + randomNumbers);
    }
}

3

これを行うための賢い方法の1つは、モジュラスでプリミティブ要素の指数を使用することです。

たとえば、2は原始根mod 101です。つまり、2 mod 101の累乗は、1から100までのすべての数値を見る非反復シーケンスを提供します。

2^0 mod 101 = 1
2^1 mod 101 = 2
2^2 mod 101 = 4
...
2^50 mod 101 = 100
2^51 mod 101 = 99
2^52 mod 101 = 97
...
2^100 mod 101 = 1

Javaコードでは、次のように記述します。

void randInts() {
int num=1;
for (int ii=0; ii<101; ii++) {
    System.out.println(num);
    num= (num*2) % 101;
    }
}

特定の係数の原始根を見つけるのは難しい場合がありますが、Mapleの「primroot」関数がこれを行います。


これは興味深いことですが、生成されたシーケンスがランダムであることをどのように確認しますか?そうではないようです。シーケンスの先頭に1,2,4,8,16、...があることは非常に決定論的であるように思われます。
h4nek

それはランダムではありません...それは疑似ランダムです。真に乱数を生成する方法を誰も知りません。初期パターンが気に入らない場合は、より大きなベースを原始根として使用できます。
AT -学生

疑似ランダムで十分です。しかし、ここでは、与えられた「範囲」に対して、原始根の量、したがって一意のシーケンスが制限されます。特に、より小さな範囲の場合はそうです。したがって、パターンには問題があるようです。たとえば、常に根の累乗のサブシーケンスがあります。そして、さらにいくつかのシェナニガンを適用しない限り、複数の実行で(おそらく)非常に異なるシーケンスを取得しません。ユースケースにもよると思います。ベースを変更することは、パターンを「シフト」するだけですが、とにかく素晴らしいアップグレードです。
h4nek

2

私はこの質問と重複している別の質問からここに来ました(Javaで一意の乱数を生成する

  1. 1から100の数値を配列に格納します。

  2. 位置として1から100までの乱数を生成し、array [position-1]を返して値を取得します

  3. 配列で数値を使用したら、値を-1としてマークします(この数値がすでに使用されているかどうかを確認するために別の配列を維持する必要はありません)

  4. 配列の値が-1の場合、乱数を再度取得して、配列の新しい場所をフェッチします。


1

この問題の簡単な解決策があります。これにより、n個の一意の乱数を簡単に生成できます。その論理は、誰でも任意の言語で使用できます。

for(int i=0;i<4;i++)
        {
            rn[i]= GenerateRandomNumber();
            for (int j=0;j<i;j++)
            {
                if (rn[i] == rn[j])
                {
                    i--;
                }
            }
        }

0

これを試してみてください

public class RandomValueGenerator {
    /**
     * 
     */
    private volatile List<Double> previousGenValues = new ArrayList<Double>();

    public void init() {
        previousGenValues.add(Double.valueOf(0));
    }

    public String getNextValue() {
        Random random = new Random();
        double nextValue=0;
        while(previousGenValues.contains(Double.valueOf(nextValue))) {
            nextValue = random.nextDouble();
        }
        previousGenValues.add(Double.valueOf(nextValue));
        return String.valueOf(nextValue);
    }
}

0

これは他の回答と大きな違いはありませんが、最後に整数の配列が必要でした。

    Integer[] indices = new Integer[n];
    Arrays.setAll(indices, i -> i);
    Collections.shuffle(Arrays.asList(indices));
    return Arrays.stream(indices).mapToInt(Integer::intValue).toArray();

0

ブール配列を使用してtrueを埋めることができます。そうでない場合は、ブール配列をナビゲートして、以下に示す値を取得します。

package study;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/*
Created By Sachin  Rane on Jul 18, 2018
*/
public class UniqueRandomNumber {
    static Boolean[] boolArray;
    public static void main(String s[]){
        List<Integer> integers = new ArrayList<>();


        for (int i = 0; i < 10; i++) {
            integers.add(i);
        }


        //get unique random numbers
        boolArray = new Boolean[integers.size()+1];
        Arrays.fill(boolArray, false);
        for (int i = 0; i < 10; i++) {
            System.out.print(getUniqueRandomNumber(integers) + " ");

        }

    }

    private static int  getUniqueRandomNumber(List<Integer> integers) {
        int randNum =(int) (Math.random()*integers.size());
        if(boolArray[randNum]){
            while(boolArray[randNum]){
                randNum++;
                if(randNum>boolArray.length){
                    randNum=0;
                }
            }
            boolArray[randNum]=true;
            return randNum;
        }else {
            boolArray[randNum]=true;
            return randNum;
        }

    }

}

0

0からm-1までのn個の一意の乱数を選択します。

int[] uniqueRand(int n, int m){
    Random rand = new Random();
    int[] r = new int[n];
    int[] result = new int[n];
    for(int i = 0; i < n; i++){
        r[i] = rand.nextInt(m-i);
        result[i] = r[i];
        for(int j = i-1; j >= 0; j--){
            if(result[i] >= r[j])
                result[i]++;
        }
    }
    return result;
}

0からm-1までの数字を含むリストを想像してみてください。最初の番号を選択するには、単にを使用しますrand.nextInt(m)。次に、リストから番号を削除します。今はm-1の番号が残っているので、rand.nextInt(m-1)。取得した数値は、リスト内の位置を表しています。最初の番号より前のリストの部分は最初の番号の削除によって変更されていないため、最初の番号よりも小さい場合は2番目の番号になります。位置が最初の数値以上の場合、2番目の数値はposition +1です。さらに派生を行うと、このアルゴリズムを取得できます。

説明

このアルゴリズムの複雑さはO(n ^ 2)です。したがって、大きなセットから少量の一意の番号を生成するのに適しています。シャッフルベースのアルゴリズムでは、シャッフルを実行するために少なくともO(m)が必要です。

また、シャッフルベースのアルゴリズムには、シャッフルを実行するために考えられるすべての結果を格納するためのメモリが必要です。このアルゴリズムは必要ありません。


0

Collectionsクラスを使用できます。

Collectionsと呼ばれるユーティリティクラスは、ArrayListなどのコレクションに対して実行できるさまざまなアクションを提供します(たとえば、要素の検索、最大要素または最小要素の検索、要素の順序の逆順など)。実行できるアクションの1つは、要素をシャッフルすることです。シャッフルは、各要素をリスト内の異なる位置にランダムに移動します。これは、ランダムオブジェクトを使用して行います。これは、決定論的なランダム性であることを意味しますが、ほとんどの状況で機能します。

ArrayListをシャッフルするには、Collectionsインポートをプログラムの先頭に追加してから、Shuffle静的メソッドを使用します。ArrayListをパラメーターとしてシャッフルする必要があります。

import java.util.Collections;
import java.util.ArrayList;
public class Lottery {
public static void main(String[] args) {
//define ArrayList to hold Integer objects
ArrayList numbers = new ArrayList();
for(int i = 0; i < 100; i++)
{
numbers.add(i+1);
}
Collections.shuffle(numbers);
System.out.println(numbers);
}
}

0

古いスレッドですが、別のオプションを追加しても害はないかもしれません。(JDK 1.8ラムダ関数はそれを簡単にするようです);

この問題は、次の手順に分類できます。

  • 提供された整数のリストの最小値を取得します(一意の乱数を生成するため)
  • 提供された整数のリストの最大値を取得します
  • ThreadLocalRandomクラス(JDK 1.8から)を使用して、以前に見つかった最小および最大整数値に対してランダムな整数値を生成し、フィルターをかけて、値が最初に提供されたリストに実際に含まれていることを確認します。最後に、intstreamにdistinctを適用して、生成された番号が一意であることを確認します。

説明付きの関数は次のとおりです。

/**
 * Provided an unsequenced / sequenced list of integers, the function returns unique random IDs as defined by the parameter
 * @param numberToGenerate
 * @param idList
 * @return List of unique random integer values from the provided list
 */
private List<Integer> getUniqueRandomInts(List<Integer> idList, Integer numberToGenerate) {

    List<Integer> generatedUniqueIds = new ArrayList<>();

    Integer minId = idList.stream().mapToInt (v->v).min().orElseThrow(NoSuchElementException::new);
    Integer maxId = idList.stream().mapToInt (v->v).max().orElseThrow(NoSuchElementException::new);

            ThreadLocalRandom.current().ints(minId,maxId)
            .filter(e->idList.contains(e))
            .distinct()
            .limit(numberToGenerate)
            .forEach(generatedUniqueIds:: add);

    return generatedUniqueIds;

}

そのため、「allIntegers」リストオブジェクトの11個の一意の乱数を取得するには、次のような関数を呼び出します。

    List<Integer> ids = getUniqueRandomInts(allIntegers,11);

この関数は、新しいarrayList'generatedUniqueIds 'を宣言し、返される前に、必要な数までの一意のランダムな整数をそれぞれ入力します。

PS ThreadLocalRandomクラスは、並行スレッドの場合に共通のシード値を回避します。


0

これは、範囲内または配列から一意のランダム値を生成するための最も簡単な方法です。です。

この例では、事前定義された配列を使用しますが、この方法を適用して乱数を生成することもできます。まず、データを取得するためのサンプル配列を作成します。

  1. 乱数を生成し、それを新しい配列に追加します。
  2. 別の乱数を生成し、それが新しい配列にすでに格納されているかどうかを確認します。
  3. そうでない場合は、それを追加して続行します
  4. それ以外の場合は、手順を繰り返します。
ArrayList<Integer> sampleList = new ArrayList<>();
sampleList.add(1);
sampleList.add(2);
sampleList.add(3);
sampleList.add(4);
sampleList.add(5);
sampleList.add(6);
sampleList.add(7);
sampleList.add(8);

ここから、sampleList一意の5つの乱数を生成します。

int n;
randomList = new ArrayList<>();
for(int  i=0;i<5;i++){
    Random random = new Random();
    n=random.nextInt(8);     //Generate a random index between 0-7

    if(!randomList.contains(sampleList.get(n)))
    randomList.add(sampleList.get(n));
    else
        i--;    //reiterating the step
}
        

これは概念的に非常に単純です。生成されたランダム値がすでに存在する場合は、手順を繰り返します。これは、生成されたすべての値が一意になるまで続きます。

この回答が役に立った場合は、他の回答と比較して概念が非常に単純であるため、投票することができます


0

ViperCreationによるVeryRandomクラス

ウェブサイト:vipercreation.blogspot.com

使い方 :

レジスターなし(VeryRandomには内部レジスターがあり、クラスがすでに指定された値をスキップするのに役立ちます):

注:最小<= rnd <最大

int rnd = VeryRandom.randomInt(min、max);

double rnd = VeryRandom.randomDouble(min、max);

float rnd = VeryRandom.randomFloat(min、max);

注:randomString / randomChar / ofString()/ ofChar()は指定されたすべてのデータを取得します。つまり、上記のメソッドやofInt / Double / Float()とは異なり、データの最後の値もカウントされます。

String rnd = VeryRandom.randomString(String [] / List);

char rnd = VeryRandom.randomChar(String);

boolean rnd = VeryRandom.randomBoolean();

文字列generateString = VeryRandom.generateString(charsasString、length);

文字列generateString = VeryRandom.generateString(lengthonly);

レジスター付き:

/ *クラスでVeryRandomフィールドを定義し、毎回定義者を呼び出さないことをお勧めします。これにより、レジスターが破損します* /

/ *クラスで* /

VeryRandom rnd;

/ * Androidの「メイン」サイクルまたは「onCreate」サイクル* /

rnd = VeryRandom.ofInt / Float / Double / String / Char()。setRange / Data(int / double / float、int / double / float OR String [] / String);

/ *ランダムな値を取得するたびにこれを呼び出します* /

int / double / float / String / char rndvalue =(int / double / float / String / char)rnd.get();

定義:

VeryRandomは、Java SDKのRandomクラスと同じように機能しますが、繰り返しを減らすために、指定された値を登録します。

Q&A:

  • Q:新しいVeryRandomを生成することと、ランダム値を生成するために静的メソッドを使用することの違いは何ですか?

  • A:静的メソッドは内部レジスターを使用せず、新しいVeryRandomフィールドを定義すると、すべてのVeryRandomフィールドに独自のレジスターがあるため、異なるランダムオーダーに対して新しいレジスターを定義することをお勧めします。

ドキュメンテーション :

静的メソッド:

int:VeryRandom.randomInt(int、int)[1番目のintはランダム範囲の最小値であり、2番目のintはランダム範囲の最大値です]

double:VeryRandom.randomDouble(double、double)[最初のdoubleはランダム範囲の最小値であり、2番目のdoubleはランダム範囲の最大値です]

float:VeryRandom.randomFloat(float、float)[1番目のfloatはランダム範囲の最小値であり、2番目のfloatはランダム範囲の最大値です]

文字列:VeryRandom.randomString(String [])[文字列配列は、ランダムな文字列を選択するために指定された文字列です]

文字列:VeryRandom.randomString(List)[リスト文字列はランダムな文字列を選択するために指定された文字列です]

char:VeryRandom.randomChar(String)[文字列はランダムな文字を選択するために指定された文字です]

ブール値:VeryRandom.randomBoolean()

文字列:VeryRandom.generateString(String、int)[文字列はランダムな文字列を生成するために指定された文字であり、intは生成された文字列の長さです]

文字列:VeryRandom.generateString(int)[Intは生成された文字列の長さです]

VeryRandom:VeryRandom.ofInt()[定義メソッド]

VeryRandom:VeryRandom.ofDouble()[定義メソッド]

VeryRandom:VeryRandom.ofFloat()[定義メソッド]

VeryRandom:VeryRandom.ofString()[定義メソッド]

VeryRandom:VeryRandom.ofChar()[定義メソッド]

クラス:タイプ{VeryRandom.Type.INT、VeryRandom.Type.INTEGER、VeryRandom.Type.DOUBLE、VeryRandom.Type.FLOAT、VeryRandom.Type.STRING、VeryRandom.Type.CHAR、VeryRandom.Type.CHARACTER}

非静的メソッド:

VeryRandom:setRange(int、int)[1番目のintはランダム範囲の最小値であり、2番目のintはランダム範囲の最大値です]

VeryRandom:setRange(double、double)[最初のdoubleはランダム範囲の最小値であり、2番目のdoubleはランダム範囲の最大値です]

VeryRandom:setRange(float、float)[1番目のfloatはランダム範囲の最小値であり、2番目のfloatはランダム範囲の最大値です]

VeryRandom:setData(String [])[文字列配列は、ランダムな文字列を選択するために指定された文字列です]

VeryRandom:setData(List)[リスト文字列はランダムな文字列を選択するために指定された文字列です]

VeryRandom:setData(String)[文字列はランダムな文字を選択するために指定された文字です]

オブジェクト:get()[ランダムな値を取得]

文字列:getType()[VeryRandomタイプを取得]

それを得るために:

https://drive.google.com/folderview?id=18eZwJXrbtSHmXlXX8Qq2tJGFbzPZiZe5


これはstackoverflow.com/a/64733622/4657412と同じです。これは通常、一方の質問にもう一方の質問の重複としてフラグを立てるか、質問された質問に実際に回答していないことを示しています
DavidW6

-1

Javaで0からn-1までのn個の一意の乱数を生成できます

public static void RandomGenerate(int n)
{
     Set<Integer> st=new HashSet<Integer>();
     Random r=new Random();
     while(st.size()<n)
     {
        st.add(r.nextInt(n));
     }

}


-2

これをチェックして

public class RandomNumbers {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int n = 5;
        int A[] = uniqueRandomArray(n);
        for(int i = 0; i<n; i++){
            System.out.println(A[i]);
        }
    }
    public static int[] uniqueRandomArray(int n){
        int [] A = new int[n];
        for(int i = 0; i< A.length; ){
            if(i == A.length){
                break;
            }
            int b = (int)(Math.random() *n) + 1;
            if(f(A,b) == false){
                A[i++] = b;
            } 
        }
        return A;
    }
    public static boolean f(int[] A, int n){
        for(int i=0; i<A.length; i++){
            if(A[i] == n){
                return true;
            }
        }
        return false;
    }
}

2
Java標準、読みやすさ、使いやすさを窓の外に投げ出しますか?
austin wernli 2015年

コードは答えではありません。答えを書いてから、コードを追加して、必要なものを説明します。
aditya 2016年

-2

以下は、私が常に一意の番号を生成するために使用した方法です。ランダム関数は番号を生成してテキストファイルに保存し、次にファイルでチェックするときにそれを比較して新しい一意の番号を生成します。したがって、このようにして常に新しい一意の番号があります。

public int GenerateRandomNo()
{
    int _min = 0000;
    int _max = 9999;
    Random _rdm = new Random();
    return _rdm.Next(_min, _max);
}
public int rand_num()
{
    randnum = GenerateRandomNo();
    string createText = randnum.ToString() + Environment.NewLine;
    string file_path = System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath) + @"\Invoices\numbers.txt";
    File.AppendAllText(file_path, createText);
    int number = File.ReadLines(file_path).Count(); //count number of lines in file
    System.IO.StreamReader file = new System.IO.StreamReader(file_path);
    do
    {
        randnum = GenerateRandomNo();
    }
    while ((file.ReadLine()) == randnum.ToString());
    file.Close();
    return randnum;

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