2011-06-22 11 views
31

範囲内で乱数を生成して除外するにはどうすればよいですか?生成した数値が除外したい数値の1つであるかどうかを生成してチェックしないでください。ある範囲内で乱数を生成することができますが、いくつかを除外することはできますか?

+0

であなたがintまたはダブル乱数をしたいと数字がどうあるべき除外しますか? – Snicolas

+0

除外された数字に当てはまる可能性が非常に高い範囲で除外範囲が非常に多いですか? –

答えて

41

再生せずに一つの可能​​な解決策:

public int getRandomWithExclusion(Random rnd, int start, int end, int... exclude) { 
    int random = start + rnd.nextInt(end - start + 1 - exclude.length); 
    for (int ex : exclude) { 
     if (random < ex) { 
      break; 
     } 
     random++; 
    } 
    return random; 
} 

このメソッドは、配列参照で呼び出すこともできます。

int[] ex = { 2, 5, 6 }; 
val = getRandomWithExclusion(rnd, 1, 10, ex) 

または直接呼び出しに番号を挿入することにより、

val = getRandomWithExclusion(rnd, 1, 10, 2, 5, 6) 

それはstartend(両方を含む)の間の乱数(int)を生成し、あなたに含まれる任意の数を与えるものではありません配列excludeにあります。その他の数はすべて等しい確率で発生します。次の制約が保持されなければならないことに注意してください。excludeは昇順にソートされ、すべての数値は指定された範囲内にあり、すべてが互いに異なります。

+0

+1ここで同じ考えです。しかし、ループ内で 'random

+0

@あなたの提案を組み込むための致命的な調整コード。 – Howard

+0

@Fatal、@Howard、これは非常に巧妙な解決策です。あなたはそれをどこで知ったのですか?それともどのように思いつきましたか?最初は私はこれがうまくいかないと確信していましたが、それを踏んだ後、私はそれが確かにうまくいくとわかりました。毎日のコードでは、私たちのコードベースでこれを見たいとは思わないでしょう。読みやすさの高さでスピードを出すからです。 – Paul

1

範囲制限なしでランダム関数の出力を取得し、制限付きで必要な範囲にマップするマップを作成します。私は1-10からランダムint型をしたいが、決して7場合

は例えば、私はこのような何かができる:

int i = rand(1, 9); 
if i>=7 
    i++; 
return i; 

を限り、あなたはあなたのマッピングが1であることを確認するよう:1、あなたは避けることができますrand関数のランダム性を歪めます。

+0

もう1つの方法は、より良い方法です:1~8の数字を作成し、7と8を8と8と9にマップします。 – keuleJ

+0

@keuleJ:マッピング9〜10を忘れた さらに、それはちょっとした例として意味がありました。どちらのマップでも、あなたのアルゴリズムに特有のものになるでしょう。あなたのマップが一貫している限り(それぞれの可能な入力は同じ数の出力にマップされ、それぞれの望ましい出力は同じ数の可能な入力によってマップされます)、その分布を維持します。 –

+0

@Axあなたは正しいです。しかし、あなたは7と8を8にマッピングしました。この種のアルゴリズムでは、分布を混乱させるのは簡単です... – keuleJ

0

あなたが除外している乱数のリストの大きさに応じて、私はあなたの番号を生成し、それが除外された数字の配列に含まれているかどうかを確認します。毎回チェックしたくないのは分かっていますが、範囲を明示的に指定する以外には別の方法は考えられません.5つ以上の数値がある場合は除外しています。

0

仕事ができる何かとintにして、二重番号は次のようになる可能性の両方を適用します。毎回ランダムには、以下のアルゴリズムを使用することです

public int getRandomNumberWithExclusion(int start, int end) 
{ 
    Random r = new Random(); 
    int result = -1; 

    do 
    { 
     result = start + r.nextInt(end - start); 
    }//do 
    while(!isAllowed(result)); 

    return result; 

}//met 

private boolean isAllowed(int number) 
{ 
    //your test for restricted values here 
}//met 

よろしく、 ステファン

+0

"[...]生成した番号が除外したい番号のものであるかどうかを確認しながら生成し続けます。" –

3

数字をランダム化するのに最適な方法は、最初に必要な数字を選択してから、ランダムに選択した数字を選択することです。擬似コードの例については、「生成された番号は、」許可されているかどうか、それは全く存在しないため、

List<Number> numbers; 

numbers.add(1); 
numbers.add(2); 
numbers.add(3); 
//You can do a "for" without adding the excluded numbers.. 

//Then, your randomizer could be... 

public Number getRandoNumber() { 
    int index = Random.get(0, numbers.size()); 
    return numbers.get(index); 
} 

は今、あなたは、チェックする必要はありません。

あなたがそれらを繰り返したいドント場合は、のような何かを行うことができます。これは、すべての擬似コード、ドントコピー&ペーストである

Collections.shuffle(numbers); 

public Number getRandomNotRepeat() { 
    if(numbers.size() == 0) 
     throw new RuntimeException("No more numbers"); 

     Number n = numbers.get(0); 
     numbers.removeFirst(); 

     return n; 
} 

15
/** 
* @param start start of range (inclusive) 
* @param end end of range (exclusive) 
* @param excludes numbers to exclude (= numbers you do not want) 
* @return the random number within start-end but not one of excludes 
*/ 
public static int nextIntInRangeButExclude(int start, int end, int... excludes){ 
    int rangeLength = end - start - excludes.length; 
    int randomInt = RANDOM.nextInt(rangeLength) + start; 

    for(int i = 0; i < excludes.length; i++) { 
     if(excludes[i] > randomInt) { 
      return randomInt; 
     } 

     randomInt++; 
    } 

    return randomInt; 
} 

アイデアは、乱数が除外され、その範囲内の数字の開始と終了マイナス数との差に生成されている範囲を削減することです。

有効な有効数字の数と同じ範囲の長さが得られます。つまり、範囲からすべての穴を削除しました。

乱数を生成したら、範囲内に「穴」を戻してください。これは、生成された数以下に除外された数が存在する限り、生成された数をインクリメントすることによって達成することができる。下位除外数は、生成された数より前の範囲の「穴」です。生成された番号は、その番号の前のすべての穴に対して右にシフトされます。

+1

+1英語の説明 – deinocheirus

+0

javadocのコメントと意味のあるコードのために素敵な答え –

2

私は追加の質問があると思います:あなたはexlcudeしたい数字は何ですか?彼らはの範囲の何らかの種類を表していますか、それとも完全にランダムなですか?

それはあなたが無視したい番号の範囲だった場合、あなたが唯一の有効な数値を表すいくつかのセットの中からあなたの乱数を生成することがありましたが:

rand(1,9); 
rand(15,19); 
rand(22,26); 

あなたが除外を選択したことがないと確信していますこのよう: < 0,10,11,12,13,14,20,21> 27

次に、3つの数字を取得すると、そのうちの1つをランダムに選択することができます。

除外された数字は、除外された数字のコレクションの何らかの種類に対して毎回チェックしなければならないのではないかと心配しています。

+0

私は以前の記述を撤回しなければなりませんでした。これは良いアイデアのように見えますが、実際には、「選択された」範囲のそれぞれが等しいサイズでない限り、結果が歪んでしまいます。番号2を0から10までの範囲から除外したいとします。数字0と1は残りの数字よりも2回頻繁に表示されます。この問題を解決するには、より大きな範囲に由来する乱数を選ぶことを好む必要がありますが、それをどのように調整し、均等な分布を維持するかはわかりません。 – Carcigenicate

0

は、範囲パラメータ

private int GiveMeANumber(int range,int... exclude) 
{ 

    Set<Integer> integers=new HashSet<>(); 
    int count=range; 

    for(int i=0;i<count;i++) 
     integers.add(i); 

    integers.removeAll(Arrays.asList(exclude)); 


    int index = new Random().nextInt(range - exclude.length); 

    count=0; 

    for (int value:integers){ 
     if(count==index) 
      return value; 

     count++; 
    } 


    return 0; 
} 
関連する問題