Java:0 <= x <nの範囲の長い乱数


135

ランダムクラスには、指定された範囲でランダムな整数を生成するメソッドがあります。例えば:

Random r = new Random(); 
int x = r.nextInt(100);

これは、0以上100未満のint数を生成します。長い数でもまったく同じようにしたいと思います。

long y = magicRandomLongGenerator(100);

ランダムクラスにはnextLong()しかありませんが、範囲を設定することはできません。


関連、役に立つかもしれません:stackoverflow.com/questions/2290057/…–
TJ Crowder

1
長いランダムを取得し、範囲のmodを取得することを検討しましたか?(もちろん、範囲が100しかない場合は、intランダムを生成し、それをlongにキャストします。)
Hot Licks

java.util.Random48ビットのディストリビューション(実装の詳細を参照)のみを使用するため、正規のディストリビューションはありません。
Geoffrey De Smet

1
現代では、org.apache.commons.lang3.RandomUtils#nextLongの使用を検討できました。
15

回答:


149

始まったJava 7あなたが直接使用することができます(または= 5.0以上のAndroid APIレベル21)ThreadLocalRandom.current().nextLong(n)(≤X <nは0)とThreadLocalRandom.current().nextLong(m, n)(≤X <N Mのため)。詳細については、@ Alexの回答を参照してください。


あなたがで立ち往生している場合はJava 6の(またはAndroid 4.xの)あなたは外部ライブラリを使用する必要がある(例えばorg.apache.commons.math3.random.RandomDataGenerator.getRandomGenerator().nextLong(0, n-1)、参照@mawaldneの答えを)、または自分自身を実装しますnextLong(n)

https://docs.oracle.com/javase/1.5.0/docs/api/java/util/Random.htmlに よるとnextInt

 public int nextInt(int n) {
     if (n<=0)
                throw new IllegalArgumentException("n must be positive");

     if ((n & -n) == n)  // i.e., n is a power of 2
         return (int)((n * (long)next(31)) >> 31);

     int bits, val;
     do {
         bits = next(31);
         val = bits % n;
     } while(bits - val + (n-1) < 0);
     return val;
 }

したがって、これを変更して実行することができますnextLong

long nextLong(Random rng, long n) {
   // error checking and 2^x checking removed for simplicity.
   long bits, val;
   do {
      bits = (rng.nextLong() << 1) >>> 1;
      val = bits % n;
   } while (bits-val+(n-1) < 0L);
   return val;
}

1
「2 ^ xチェック」部分に問題があります。何か案は?
Vilius Normantas

@Vilius:直接使用rng.nextLong() % nすると値が均一になるため、2 ^ xチェックは生成を高速化します(すべてのビットが良好であると想定)。必要に応じて、その部分を無視できます。
kennytm 2010年

必要に応じてm <= x <= n、ソリューションをどのように変更しますか?
BJ Peter DeLaCruz 2014

6
@BJPeterDeLaCruz:Aは、乱数の間mnの間の乱数を得ることができる0n-m、次に追加しますm
kennytm 2014

84

ThreadLocalRandom

ThreadLocalRandom持っているnextLong(long bound)方法を。

long v = ThreadLocalRandom.current().nextLong(100);

またnextLong(long origin, long bound)、0以外の原点が必要な場合もあります。原点(包括的)と境界(排他的)を渡します。

long v = ThreadLocalRandom.current().nextLong(10,100); // For 2-digit integers, 10-99 inclusive.

SplittableRandom同じnextLong方法があり、再現可能な数列が必要な場合はシードを選択できます。


5
この回答は、最も投票された回答よりもはるかに単純で、したがってより有用です。
yurin 2015年

2
:アンドロイド、それはAPI 21(ロリポップ、アンドロイド5.0)からのみ利用可能だという通知のために開発したものについてはdeveloper.android.com/reference/java/util/concurrent/...
Androidデベロッパー

75

範囲内で(ユーティリティメソッドなしで)数値を生成する標準的な方法は、範囲でdoubleを使用することです。

long range = 1234567L;
Random r = new Random()
long number = (long)(r.nextDouble()*range);

0(包括的)と範囲(排他的)の間のlongを提供します。同様に、xとyの間の数値が必要な場合:

long x = 1234567L;
long y = 23456789L;
Random r = new Random()
long number = x+((long)(r.nextDouble()*(y-x)));

1234567(両端を含む)から123456789(両端を含まない)までの長い

注:longへのキャストは乗算よりも優先されるため、括弧を確認してください。


5
私の最初のアイデアはまさにこれでした。しかし、それは少しエレガントではないようです。そして、分布の均一性が心配です(本当に必要なわけではありません。正しく実行したいだけです)
Vilius Normantas

6
絶対に使用しないでください。出力はまったく均一ではありません。
Navin、2015

2
最大の問題は、丸めによって最下位ビットが非常に不均一になることです。また、bound2でエンコードできる最大の整数2 ^ 53未満である必要があります。
Aleksandr Dubinsky 2017年

12

上記の方法はうまく機能します。Apache Commons(org.apache.commons.math.random)を使用している場合は、RandomDataを確認してください。それには次のメソッドがあります:nextLong(長い下、長い上)

http://commons.apache.org/math/userguide/random.html

http://commons.apache.org/math/api-1.1/org/apache/commons/math/random/RandomData.html#nextLong(long,%20long)


3
後世のために:RandomDataは4.0で非推奨になりました。commons.apache.org/proper/commons-math/apidocs/org/apache/…を
Michael Tontchev

11

'%'演算子を使用する

resultingNumber = (r.nextLong() % (maximum - minimum)) + minimum;

'%'演算子を使用することで、最大値で除算したときの余りが使用されます。これにより、0(包括的)から除数(排他的)までの数値のみが残ります。

例えば:

public long randLong(long min, long max) {
    return (new java.util.Random().nextLong() % (max - min)) + min;
}

これはいいですが、チェックする必要がありますif (max == min)
khcpietro

また、チェックif (nextLong() >= 0)
khcpietro 2014

6
参考:これは常に均一な分布を与えるわけではなく、いくつかの大きな範囲では本当に悪いです。たとえば、との場合min = 0max = 2 * (MAX_LONG / 3)で値を取得する場合と比べて、値を取得する可能性が2倍高くなり[0, MAX_LONG / 3]ます[MAX_LONG / 3, 2 * (MAX_LONG / 3)]
ニック

このコードは機能しません。nextLongが負の値を返す場合、残りは負になり、値は範囲外になります。
2015年

3

kennytmの答えをさらに改善する:Java 8での実際の実装を考慮したサブクラス実装は次のようになります。

public class MyRandom extends Random {
  public long nextLong(long bound) {
    if (bound <= 0) {
      throw new IllegalArgumentException("bound must be positive");
    }

    long r = nextLong() & Long.MAX_VALUE;
    long m = bound - 1L;
    if ((bound & m) == 0) { // i.e., bound is a power of 2
      r = (bound * r) >> (Long.SIZE - 1);
    } else {
      for (long u = r; u - (r = u % bound) + m < 0L; u = nextLong() & Long.MAX_VALUE);
    }
    return r;
  }
}

私はこれが古い答えであり、使用される可能性が低いことを知っていますが、この部分は明らかに不正確です:if ((bound & m) == 0) { r = (bound * r) >> (Long.SIZE - 1); } 最初に、これが実際に[0、bound)の範囲の数値を生成しないことを単体テストで示すのは簡単です。第二に、それは不必要に手の込んだものです:r = r & m望ましい結果を達成するでしょう、そしてそれは基本的に現在のJava 8実装がすることです。この回答が書かれたときに実装が異なっていた可能性がありますが、示されているものではなかった可能性があります。
E.ビショップ

3

[0、m)の範囲の長い均一分布の疑似乱数が必要な場合は、nextLong()以下に示すように、モジュロ演算子と絶対値メソッドをメソッドと組み合わせて使用​​してみてください。

Math.abs(rand.nextLong()) % m;

randランダムオブジェクトはどこにありますか。

モジュロ演算子は2つの数値を除算し、それらの数値の余りを出力します。例えば、3 % 2ある13及び2の余りが1であるためです。

nextLong()[-(2 ^ 48)、2 ^ 48)の範囲(またはその範囲のどこか)で均一に分散された疑似乱数を生成するので、その絶対値を取る必要があります。そうでない場合、nextLong()メソッドのモジュロは50%の確率で負の値を返しますが、これは[0、m)の範囲外です。

最初に要求したのは、[0,100)の範囲の長い均一に分布した疑似ランダムでした。次のコードはそうします:

Math.abs(rand.nextLong()) % 100;

1
剰余がバイアスされて、ランダムのためにそれを使用していないstackoverflow.com/a/10984975/1166266
サイレン

2

これはどう:

public static long nextLong(@NonNull Random r, long min, long max) {
    if (min > max)
        throw new IllegalArgumentException("min>max");
    if (min == max)
        return min;
    long n = r.nextLong();
    //abs (use instead of Math.abs, which might return min value) :
    n = n == Long.MIN_VALUE ? 0 : n < 0 ? -n : n;
    //limit to range:
    n = n % (max - min);
    return min + n;
}


フレームワークに属している部分を除いて大丈夫です(私は推測します)。
Damir Olejar

2

以下のメソッドは、10000000000から9999999999の間の値を返します

long min = 1000000000L
long max = 9999999999L    

public static long getRandomNumber(long min, long max){

    Random random = new Random();         
    return random.nextLong() % (max - min) + max;

}

私が長い分をリセットしたとき= 1L; 長い最大= 10L; 結果の乱数は最大値を超えています!
Raj Rajen 2018

random.nextLong()%(max-min)+ min;である必要があります。
ジェイジョディワル

2

Java 8 API から

それから、実際の実装を取るために容易になる可能性がAPIドキュメント https://docs.oracle.com/javase/8/docs/api/java/util/Random.html#longs-long-long-long-を 、彼らはそれを使用していますlongストリームを生成します。そして、あなたの起源は質問のように「0」にすることができます。

long nextLong(long origin, long bound) {
  long r = nextLong();
  long n = bound - origin, m = n - 1;
  if ((n & m) == 0L)  // power of two
    r = (r & m) + origin;
  else if (n > 0L) {  // reject over-represented candidates
    for (long u = r >>> 1;            // ensure nonnegative
         u + m - (r = u % n) < 0L;    // rejection check
         u = nextLong() >>> 1) // retry
        ;
    r += origin;
  }
  else {              // range not representable as long
    while (r < origin || r >= bound)
      r = nextLong();
  }
  return r;
}

1

ランダムのページから:

nextLongメソッドは、次のようにRandomクラスによって実装されます。

public long nextLong() {
   return ((long)next(32) << 32) + next(32);
}

クラスRandomは48ビットのみのシードを使用するため、このアルゴリズムはすべての可能なlong値を返しません。

したがって、を取得したい場合Long、64ビットの範囲全体を取得することはできません。

2の累乗に近い範囲がある場合Long、次のようにそのスニペットのようにを構築することをお勧めします。

next(32) + ((long)nextInt(8) << 3)

たとえば、35ビットの範囲を取得します。


2
しかし、ドキュメントには、「2 ^ 64の可能なすべての長い値が(ほぼ)等しい確率で生成される」と記載されています。したがって、明らかにnextLong()メソッドはすべての可能な値を返す必要があります。ところで、シードの長さは値の分布にどのように関係していますか?
Vilius Normantas

0

を使用するメソッドでは、以下を使用するr.nextDouble()必要があります。

long number = (long) (rand.nextDouble()*max);


long number = x+(((long)r.nextDouble())*(y-x));

0
public static long randomLong(long min, long max)
{
    try
    {
        Random  random  = new Random();
        long    result  = min + (long) (random.nextDouble() * (max - min));
        return  result;
    }
    catch (Throwable t) {t.printStackTrace();}
    return 0L;
}

1
Randomインスタンスをその場で作成しないでください。必要でない場合は、Throwablesやその他の例外をキャッチしないでくださいprintStackTrace。を使用する代わりに、なんらかのロギングフレームワーク(SLF4Jなど)でエラーをログに記録する必要があります。
偽の

0

Javaストリームを使用できる場合は、以下を試すことができます。

Random randomizeTimestamp = new Random();
Long min = ZonedDateTime.parse("2018-01-01T00:00:00.000Z").toInstant().toEpochMilli();
Long max = ZonedDateTime.parse("2019-01-01T00:00:00.000Z").toInstant().toEpochMilli();
randomizeTimestamp.longs(generatedEventListSize, min, max).forEach(timestamp -> {
  System.out.println(timestamp);
});

これにより、指定された範囲のlongの数値が生成されます。


0
import java.util*;

    Random rnd = new Random ();
    long name = Math.abs(rnd.nextLong());

これはうまくいくはずです


-4

//システム時間をシード値として使用して、適切な乱数を取得します

   Random random = new Random(System.currentTimeMillis());
              long x;
             do{
                x=random.nextLong();
             }while(x<0 && x > n); 

// 0以上でnより小さい数になるまでループします


1
これは非常に無能な場合があります。n1の場合、または2の場合はどうなりますか?ループは多くの反復を実行ます。
Magnilex、2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.