2011-02-04 25 views
5

To pytanie: How to generate a random BigInteger opisuje sposób osiągnięcia tej samej semantyki co Random.nextInt (int n) w przypadku BigIntegers.Jak utworzyć losowy BigDecimal w Javie?

Chciałbym zrobić to samo dla BigDecimal i Random.nextDouble().

Jedna z odpowiedzi w powyższym pytaniu sugeruje utworzenie losowego BigInteger, a następnie utworzenie BigDouble z niego z losową skalą. Bardzo szybki eksperyment pokazuje, że jest to bardzo zły pomysł :)

Moja intuicja jest to, że za pomocą tej metody wymagałoby całkowitą być skalowane przez coś podobnego n-log10(R), gdzie n oznacza liczbę cyfr precyzji wymaganych w wyjście i R jest losowym BigIntegerem. Powinno to pozwolić na obecność prawidłowej liczby cyfr, aby (na przykład) 1 -> 10^-64 i 10^64 -> 1.

Wartość skalowania również musi być wybrana poprawnie, aby wynik spadł w zakresie [0,1].

Czy ktoś to zrobił wcześniej i czy wie, czy wyniki są prawidłowo dystrybuowane? Czy istnieje lepszy sposób na osiągnięcie tego?

EDYTOWANIE: Dzięki @biziclop za poprawienie mojego rozumienia argumentu skali. Powyższe nie jest konieczne, stały współczynnik skali ma pożądany efekt.

do późniejszego wykorzystania, mój (kod najwyraźniej pracuje) wynosi:

private static BigDecimal newRandomBigDecimal(Random r, int precision) { 
    BigInteger n = BigInteger.TEN.pow(precision); 
    return new BigDecimal(newRandomBigInteger(n, r), precision); 
} 

private static BigInteger newRandomBigInteger(BigInteger n, Random rnd) { 
    BigInteger r; 
    do { 
     r = new BigInteger(n.bitLength(), rnd); 
    } while (r.compareTo(n) >= 0); 

    return r; 
} 

Odpowiedz

3

To na pewno bardzo łatwe ... gdybym tylko wiedział, czego chcesz. W przypadku równomiernie rozłożonej liczby w zakresie [0, 1) i dokładności N cyfr dziesiętnych generuje jednolity BigInteger mniejszy niż 10 * N i skaluje go o 10 * N.

+0

To była część "oryginalnej skali", która była błędna. Ta metoda powinna być w porządku. – DJClayworth

+0

Możesz utworzyć jednolity BigInteger mniej niż 10^N kupić, tworząc wiele liczb całkowitych, które są [0, 10^m) i łącząc je. –

+0

Racja, ale jest odpowiednie pytanie z ładną odpowiedzią powiązaną w pierwszym zdaniu tego pytania. Twoja propozycja może również dobrze działać, szczególnie przy m = 9, więc random może być użyty random.nextInt(). – maaartinus

1

I może brakować oczywiste tutaj, ale jak o utworzenie dwóch losowych BigInteger s, jedna jest częścią całkowitą, a druga ułamkową ? Oczywiście zakres "ułamkowego" biginta byłby podyktowany precyzją, na którą chcesz pozwolić, której nie możesz uciec od przypinania.

Aktualizacja: Można jeszcze bardziej uprościć pracę z jednym przypadkowym bigintem. Jeśli chcesz uzyskać liczbę losową z przedziału od 0 do n z dokładnością dziesiętną k (gdzie k jest stałą), po prostu wygeneruj liczbę losową z przedziału od 0 do n * 10^k i podziel ją przez 10^k.

+0

Wynik tego nie jest równomiernie rozłożony. Próbowałem tego, a wynik jest równomiernie rozłożony na części ułamkowej, co oznacza, że ​​10^-27 jest tak samo prawdopodobne, jak liczba od 0,01 do 0,1, która pojawia się w wynikach. 10^-27 powinno być o około 26 rzędów wielkości mniej prawdopodobne niż liczba z przedziału 0.1-0.01 –

+0

@Mike Houston Brakuje mi tego oczywistego, ponieważ nadal go nie dostaję.Czy chcesz, aby był równomiernie dystrybuowany, czy nie? – biziclop

+0

@Mike Houston Nope, nadal nie rozumiem. Jeśli weźmiesz równomiernie rozproszoną zmienną, która ma co najwyżej n cyfr i podzielisz ją na 10^n, nadal jest ona równomiernie rozłożona. – biziclop

2

Zrobiłem post o generowaniu losowego BigInteger Andy Turner's answer about generating a random BigInteger. Nie używam tego bezpośrednio do generowania losowego BigDecimal. Zasadniczo moją obawą jest wykorzystanie niezależnych instancji Random do generowania każdej cyfry w liczbie. Jednym z problemów, które zauważyłem jest to, że z Random jest tylko tyle wartości i konkretnej liczby, które dostajesz z rzędu. Również pokolenie stara się utrzymać coś w równomiernym rozkładzie wygenerowanych wartości. Moje rozwiązanie zależy od czegoś przechowującego tablicę lub kolekcję przypadkowych instancji i wywołując je. Sądzę, że jest to dobry sposób, aby o tym porozmawiać i próbuję się dowiedzieć, więc jestem zainteresowany, jeśli ktoś ma jakieś wskazówki lub krytykę tego podejścia.

/** 
* 
* @param a_Random 
* @param decimalPlaces 
* @param lowerLimit 
* @param upperLimit 
* @return a pseudo randomly constructed BigDecimal in the range from 
* lowerLimit to upperLimit inclusive and that has up to decimalPlaces 
* number of decimal places 
*/ 
public static BigDecimal getRandom(
     Generic_Number a_Generic_Number, 
     int decimalPlaces, 
     BigDecimal lowerLimit, 
     BigDecimal upperLimit) { 
    BigDecimal result; 
    BigDecimal range = upperLimit.subtract(lowerLimit); 
    BigDecimal[] rangeDivideAndRemainder = 
      range.divideAndRemainder(BigDecimal.ONE); 
    BigInteger rangeInt = rangeDivideAndRemainder[0].toBigIntegerExact(); 
    BigInteger intComponent_BigInteger = Generic_BigInteger.getRandom(
      a_Generic_Number, 
      rangeInt); 
    BigDecimal intComponent_BigDecimal = 
      new BigDecimal(intComponent_BigInteger); 
    BigDecimal fractionalComponent; 
    if (intComponent_BigInteger.compareTo(rangeInt) == 0) { 
     BigInteger rangeRemainder = 
       rangeDivideAndRemainder[1].toBigIntegerExact(); 
     BigInteger fractionalComponent_BigInteger = 
       Generic_BigInteger.getRandom(a_Generic_Number, rangeRemainder); 
     String fractionalComponent_String = "0."; 
     fractionalComponent_String += fractionalComponent_BigInteger.toString(); 
     fractionalComponent = new BigDecimal(fractionalComponent_String); 
    } else { 
     fractionalComponent = getRandom(
       a_Generic_Number, decimalPlaces); 
    } 
    result = intComponent_BigDecimal.add(fractionalComponent); 
    result.add(lowerLimit); 
    return result; 
} 

/** 
* Provided for convenience. 
* @param a_Generic_BigDecimal 
* @param decimalPlaces 
* @return a random BigDecimal between 0 and 1 inclusive which can have up 
* to decimalPlaces number of decimal places 
*/ 
public static BigDecimal getRandom(
     Generic_Number a_Generic_Number, 
     int decimalPlaces) { 
    //Generic_BigDecimal a_Generic_BigDecimal = new Generic_BigDecimal(); 
    Random[] random = a_Generic_Number.get_RandomArrayMinLength(
      decimalPlaces); 
    //System.out.println("Got Random[] size " + random.length); 
    String value = "0."; 
    int digit; 
    int ten_int = 10; 
    for (int i = 0; i < decimalPlaces; i++) { 
     digit = random[i].nextInt(ten_int); 
     value += digit; 
    } 
    int length = value.length(); 
    // Tidy values ending with zero's 
    while (value.endsWith("0")) { 
     length--; 
     value = value.substring(0, length); 
    } 
    if (value.endsWith(".")) { 
     value = "0"; 
    } 
    BigDecimal result = new BigDecimal(value); 
    //result.stripTrailingZeros(); 
    return result; 
} 
+0

Nie rozumiem, co masz na myśli mówiąc "z Random istnieje tylko tyle wartości [a] określonej liczby, które dostajesz z rzędu". Zgodnie ze źródłem Javy dla Random, "Ogólną umową następną jest to, że zwraca wartość int, a jeśli bity argumentu zawierają się w przedziale od 1 do 32 (włącznie), wtedy wiele bitów niższego rzędu zwróconej wartości będzie ... niezależnie wybrane wartości bitów, z których każdy jest równy 0 lub 1. " Wydaje mi się to sugerować, że nie ma ograniczeń co do tego, ile identycznych wartości pojawi się z rzędu, jest to po prostu coraz mniej prawdopodobne, jak można by się spodziewać. –