web-dev-qa-db-ja.com

外部関数を使用せずに乱数を生成する

これは、私が最近参加したインタビューの1つで尋ねられた質問でした。

私の知る限り、2つの数の間の乱数は次のように生成できます。

public static int Rand(int low, int high) {
    return low + (int)(Math.random() * (high - low + 1));
}

しかし、ここでは、Math.random()を使用して0から1までの乱数を生成し、それを使用して低から高までの乱数を生成しています。外部関数を使用せずに直接実行できる他の方法はありますか?

18
Usha

一般的な疑似乱数ジェネレーターは、以前の数値に基づいて新しい数値を計算するため、理論的には完全に決定論的です。唯一のランダム性は、適切なシード(乱数生成アルゴリズムの初期化)を提供することによって保証されます。乱数がセキュリティ上それほど重要でない限り(これには「実際の」乱数が必要になります)、このような再帰的な乱数ジェネレーターは多くの場合、ニーズを満たします。

シードが提供されると、再帰的な生成は「外部」関数なしで表現できます。この問題を解決するアルゴリズムがいくつかあります。良い例は Linear Congruential Generator です。

擬似コードの実装は次のようになります。

long a = 25214903917;   // These Values for a and c are the actual values found
long c = 11;            // in the implementation of Java.util.Random(), see link
long previous = 0;

void rseed(long seed) {
    previous = seed;
}

long Rand() {
    long r = a * previous + c;
    // Note: typically, one chooses only a couple of bits of this value, see link
    previous = r;
    return r;
}

このジェネレータに初期値をシードする必要があります。これは、次のいずれかを実行することで実行できます。

  • 現在の時刻のようなものを使用する(ゲームのようなセキュリティが重要ではないほとんどの場合に適しています)
  • ハードウェアノイズの使用(セキュリティが重要なランダム性に適しています)
  • 定数を使用する(常に同じシーケンスを取得するため、デバッグに適しています)
  • any関数を使用できず、定数シードを使用したくない場合、およびこれを許可する言語を使用している場合は、初期化されていないものを使用することもできますメモリ。たとえば、CおよびC++では、新しい変数を定義し、それに何かを割り当てず、その値を使用してジェネレーターをシードします。ただし、これは「良い種」ではなく、要件を満たすためのハックにすぎないことに注意してください。これを実際のコードで使用しないでください。

アルゴリズムなしがあり、differentdifferentの値を生成できることに注意してください。同じ入力一部のアクセスなし外部ソースシステム環境のように。よくシードされた乱数ジェネレーターはすべて、いくつかの外部ソースを利用します。

38
leemes

ここで私はコメント付きのいくつかの情報源があなたが役立つと思うかもしれないことを提案しています:

  • システム時間:1日で単調でランダムではありません。速くて簡単。
  • マウスポイント:ランダムですが、スタンドアロンシステムでは役に立ちません。
  • Raw Socket/Local Network(Packet's info-part):優れたランダム技術と時間がかかる-ランダム性を減らすために攻撃モードをモデル化することが可能です。
  • 順列のある入力テキスト:高速で、一般的な方法で、(私の意見では)良いです。
  • キーボード、ディスクドライブ、その他のイベントによる割り込みのタイミング:一般的な方法–注意深く使用しないとエラーが発生しやすくなります。
  • 別のアプローチは、アナログノイズ信号を供給することです:tempのような例。
  • /procファイルデータ:Linuxシステムの場合。これを使うべきだと思います。

    /proc/sys/kernel/random:このディレクトリには、ファイル/dev/randomの操作を制御するさまざまなパラメータが含まれています。

    文字特殊ファイル/dev/randomおよび/dev/urandomLinux 1.3.30以降に存在)は、カーネルの乱数ジェネレーターへのインターフェースを提供します。

    このコンマを試してください:

    $cat /dev/urandom   
    

    そして

    $cat /dev/random
    

    このファイルから読み取るファイル読み取り関数を作成できます。

    読む(また提案する): / dev/urandomからのランドはログインキーに対して安全ですか?

`

7
Grijesh Chauhan

System.currentTimeMillis()は外部としてカウントされますか?いつでもこれを取得して、最大値でmodを計算できます。

int Rand = (int)(System.currentTimeMillis()%high)+low;
5
Krease

変数のアドレスを使用するか、より多くの変数のアドレスを組み合わせて、より複雑なものを作成することができます...

1
Alireza Soori

ロジスティック写像x = 4x(1-x)から、01の間の「非合理的」xで始まるランダム性(実際には混沌とし、完全に均一ではない*)に近づくことができます。

「ランダム性」が表示されますので浮動小数点表現の精度のエッジでの丸め誤差。

(*)スキューが存在することがわかったら、スキューを元に戻すことができます。

1
Mark Hurd

現在のシステム時刻を取得できますが、ほとんどの言語の関数も必要になります。

0
user000001
public class randomNumberGenerator {

    int generateRandomNumber(int min, int max) {
        return (int) ((System.currentTimeMillis() % max) + min);
    }

    public static void main(String[] args) {
        randomNumberGenerator rn = new randomNumberGenerator();
        int cv = 0;
        int min = 1, max = 4;
        Map<Integer, Integer> hmap = new HashMap<Integer, Integer>();

        int count = min;
        while (count <= max) {
            cv = rn.generateRandomNumber(min, max);
            if ((hmap.get(cv) == null) && cv >= min && cv <= max) {
                System.out.print(cv + ",");
                hmap.put(cv, 1);
                count++;
            }
        }

    }
}
0

外部状態の使用が許可されている場合(たとえば、現在のシステム時刻で長い初期化)、外部関数なしでそれを行うことができます。これは、単純な疑似乱数ジェネレーターを実装するのに十分です。

ランダム関数を呼び出すたびに、状態を使用して新しいランダム値を作成し、状態を更新して、後続の呼び出しで異なる結果が得られるようにします。

これは、通常のJava算術演算やビット演算、あるいはその両方で実行できるため、外部関数は必要ありません。

0
mikera