2011-06-22 39 views
31

Làm cách nào để tạo một số ngẫu nhiên trong một phạm vi nhưng loại trừ một số, mà không tiếp tục tạo và kiểm tra xem số được tạo có phải là số mà tôi muốn loại trừ không?Làm cách nào để tạo một số ngẫu nhiên trong một phạm vi nhưng loại trừ một số?

+0

bạn muốn có một int hoặc hai số ngẫu nhiên? – Snicolas

+0

Bạn có quá nhiều loại trừ trong phạm vi của mình mà rất có thể sẽ bị trúng một số bị loại trừ? –

Trả lời

41

Một giải pháp khả thi mà không cần tái tạo các ngẫu nhiên mỗi lần là sử dụng các thuật toán sau đây :

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

Phương pháp này có thể được gọi với tham chiếu mảng, ví dụ:

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

hoặc bằng cách trực tiếp chèn số vào cuộc gọi:

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

Nó tạo ra một số ngẫu nhiên (int) giữa startend (cả bao gồm) và không cung cấp cho bạn bất kỳ số đó được chứa trong mảng exclude. Tất cả các số khác xảy ra với xác suất bằng nhau. Lưu ý rằng các ràng buộc sau đây phải giữ: exclude được sắp xếp tăng dần và tất cả các số nằm trong phạm vi được cung cấp và tất cả các số này đều khác nhau.

+0

+1 cùng một suy nghĩ tại đây không. Nhưng tôi muốn kiểm tra 'ngẫu nhiên

+0

@ Mã điều chỉnh được sinh ra để kết hợp đề xuất của bạn. – Howard

+0

@Fatal, @Howard, đây là giải pháp cực kỳ thông minh. Bạn đã học nó ở đâu hoặc bạn đã nghĩ ra sao? Lúc đầu, tôi đã chắc chắn điều này sẽ không làm việc nhưng sau khi bước qua nó tôi thấy nó chắc chắn không hoạt động và tôi rất ấn tượng. Đối với mã hàng ngày tôi sẽ không muốn nhìn thấy điều này trong codebase của chúng tôi bởi vì nó cho tốc độ tại đắt tiền dễ đọc. – Paul

1

Tạo bản đồ lấy đầu ra của một hàm ngẫu nhiên không có giới hạn phạm vi và ánh xạ nó tới phạm vi bạn muốn với giới hạn.

Ví dụ, nếu tôi muốn có một int ngẫu nhiên từ 1-10 nhưng không bao giờ 7 tôi có thể làm một cái gì đó như thế này:

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

Chừng nào bạn đảm bảo rằng bản đồ của bạn là 1: 1, bạn có thể tránh được xoay chiều ngẫu nhiên của hàm rand.

+0

Đường vòng khác sẽ tốt hơn: tạo số từ 1 đến 8 và bản đồ 7 và 8 đến 8 và 9. Cách này bạn giữ bản phân phối. – keuleJ

+0

@keuleJ: bạn quên lập bản đồ từ 9 đến 10 Bên cạnh đó, nó chỉ là một ví dụ tầm thường. Bất kỳ bản đồ nào bạn đưa ra sẽ cụ thể cho thuật toán của bạn. Miễn là bản đồ của bạn nhất quán (mỗi bản đồ đầu vào có thể có cùng số lượng đầu ra và mỗi đầu ra mong muốn được ánh xạ tới bởi cùng một số đầu vào có thể), bạn sẽ giữ bản phân phối. –

+0

@Ax bạn nói đúng. Nhưng bạn đã vẽ bản đồ 7 và 8 thành 8. Thật dễ dàng để làm hỏng sự phân phối trong các loại thuật toán này ... – keuleJ

0

Tùy thuộc vào danh sách các số ngẫu nhiên bạn loại trừ bao nhiêu, tôi sẽ chỉ tạo các số của bạn và kiểm tra xem nó có nằm trong dãy số bị loại trừ hay không - nếu có, chỉ cần loại bỏ nó. Tôi biết bạn không muốn kiểm tra mọi lúc, nhưng tôi không thể nghĩ ra một cách khác ngoài việc chỉ định phạm vi một cách rõ ràng và nếu bạn có nhiều hơn 5 số bạn đang loại trừ, điều đó có thể tồi tệ hơn một chút.

0

Cái gì mà có thể làm việc và áp dụng cho cả int và số đôi có thể giống như:

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 

Kính trọng, Stéphane

+0

"[...] mà không tiếp tục tạo và kiểm tra xem số được tạo có phải là số mà tôi muốn loại trừ" –

3

Các aproach tốt nhất mà bạn có thể làm theo để ngẫu nhiên số, ngoại trừ một số là chọn các số mà bạn muốn đầu tiên và sau đó chọn ngẫu nhiên các con số được chọn. Ví dụ: trong mã giả:

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

Bây giờ, bạn không cần phải kiểm tra xem "số được tạo" có được phép hay không, vì nó không tồn tại chút nào.

Nếu bạn không muốn họ lặp lại, bạn có thể làm điều gì đó như:

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

này được tất cả các mã giả, sao chép và dán đừng!

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

Ý tưởng là giảm phạm vi trong đó số ngẫu nhiên được tạo ra cho sự khác biệt giữa bắt đầu và kết thúc trừ số lượng trong phạm vi đó bị loại trừ.

Vì vậy, bạn có được độ dài dải ô giống với số lượng các số hợp lệ có thể có. Nói cách khác: Bạn đã xóa tất cả các lỗ khỏi phạm vi.

Sau khi tạo số ngẫu nhiên bạn đã đặt "lỗ" trở lại trong phạm vi. Điều này có thể đạt được bằng cách tăng số lượng được tạo miễn là có số bị loại trừ thấp hơn hoặc bằng số được tạo. Các số loại trừ thấp hơn là "lỗ" trong phạm vi trước số được tạo. Và số được tạo sẽ được chuyển sang phải cho mọi lỗ trước số đó.

+1

+1 cho lời giải thích tiếng Anh – deinocheirus

+0

câu trả lời hay vì nhận xét javadoc và mã có ý nghĩa –

2

Tôi nghĩ câu hỏi bổ sung là: Các con số bạn muốn exlcude là gì? Chúng có đại diện cho một số loại trong phạm vi hoặc chúng là hoàn toàn ngẫu nhiên?

Nếu đó là một loạt các số điện thoại bạn muốn bỏ qua, bạn có thể tạo ra các số ngẫu nhiên của bạn từ bên trong vài bộ đại diện cho số chỉ có giá trị:

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

cách này bạn chắc chắn sẽ không bao giờ chọn loại trừ: < 0,10,11,12,13,14,20,21,> 27

Sau đó, khi bạn nhận được 3 số, bạn lại có thể chọn ngẫu nhiên một trong số chúng.

Nếu số bị loại trừ ở khắp nơi, tôi sợ bạn phải kiểm tra nó mỗi lần đối với một số loại tập hợp số bị loại trừ.

+0

Tôi phải rút lại tuyên bố trước đó của mình. Mặc dù điều này có vẻ như là một ý tưởng hay nhưng nó thực sự làm lệch kết quả trừ khi mỗi phạm vi "được chọn" có kích thước bằng nhau. Giả sử tôi muốn loại trừ số 2 khỏi phạm vi từ 0 đến 10, số 0 và 1 sẽ xuất hiện hai lần và thường xuyên hơn các số còn lại. Để khắc phục điều này, bạn phải ưu tiên chọn số ngẫu nhiên đến từ phạm vi lớn hơn, nhưng tôi không chắc chắn cách điều chỉnh và duy trì phân phối đồng đều. – Carcigenicate

0

loại trừ số nên có trong tham số phạm vi

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; 
} 
Các vấn đề liên quan