Как я могу создать случайный BigDecimal в Java? - PullRequest
5 голосов
/ 04 февраля 2011

Этот вопрос: Как сгенерировать случайный BigInteger описывает способ достижения той же семантики, что и Random.nextInt (int n) для BigIntegers.

Я хотел бы сделать то же самоедля BigDecimal и Random.nextDouble ().

В одном ответе на предыдущий вопрос предлагается создать случайный BigInteger, а затем создать из него BigDouble со случайным масштабом.Очень быстрый эксперимент показывает, что это очень плохая идея:)

Моя интуиция заключается в том, что использование этого метода потребует масштабирования целого числа до значения, подобного n-log10(R), где n - числов выходных данных требуются цифры точности, а R - случайный BigInteger.Это должно обеспечить правильное количество цифр, чтобы (например) 1 -> 10 ^ -64 и 10 ^ 64 -> 1.

Значение масштабирования также должно быть правильно выбрано для результатапопасть в диапазон [0,1].

Кто-нибудь делал это раньше, и знают ли они, правильно ли распределены результаты?Есть ли лучший способ добиться этого?

РЕДАКТИРОВАТЬ: Спасибо @biziclop за исправление моего понимания аргумента масштаба.Выше не требуется, постоянный масштабный коэффициент имеет желаемый эффект.

Для дальнейшего использования мой (очевидно рабочий код):

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

Ответы [ 3 ]

3 голосов
/ 04 февраля 2011

Это, конечно, очень легко ... если бы я только знал, чего ты хочешь. Для равномерно распределенного числа в диапазоне [0, 1) и точности N десятичных цифр сгенерируйте равномерный BigInteger меньше 10 * N и уменьшите его на 10 * N.

2 голосов
/ 17 февраля 2011

Я сделал сообщение о генерации случайного BigInteger Ответ Энди Тернера о генерации случайного BigInteger .Я не использую это непосредственно для генерации случайного BigDecimal.По сути, моя задача - использовать независимые экземпляры Random для генерации каждой цифры в числе.Одна проблема, которую я заметил, состоит в том, что в Random есть только так много значений и определенного числа, которые вы получаете подряд.Также поколение пытается поддерживать что-то из равномерного распределения сгенерированных ценностей.Мое решение зависит от того, что хранит массив или коллекцию экземпляров Random и вызывает их.Я думаю, что это хороший способ сделать это, и я пытаюсь выяснить, поэтому мне интересно, есть ли у кого-нибудь какие-либо указатели или критика этого подхода.

/**
 *
 * @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;
}
1 голос
/ 04 февраля 2011

Я мог бы упустить очевидное здесь, но как насчет создания двух случайных BigInteger с, одна из которых является целой частью, а другая дробной? Очевидно, что диапазон «дробного» бигинта будет продиктован точностью, которую вы хотите разрешить, которую вы не можете избежать прижиманием вниз.

Обновление: это может быть еще более упрощено для работы только с одним случайным bigint. Если вам нужно случайное число от 0 до n с десятичной точностью k (где k - константа), вы просто генерируете случайное число от 0 до n * 10 ^ k и делите его на 10 ^ k.

...