programing

포함 범위에서 임의 부동 소수점 두 배

skycolor 2023. 10. 9. 22:26
반응형

포함 범위에서 임의 부동 소수점 두 배

의 수 .[X,Y)X는이고 Y는하십시오)다 이후 으로 X는 됩니다.Math.random()의 의사 는 기를 들어, AFAIK)를 생성합니다.[0,1):

function randomInRange(min, max) {
  return Math.random() * (max-min) + min;
}
// Notice that we can get "min" exactly but never "max".

어떻게 하면 두 경계를 포함하는 원하는 범위의 난수를 얻을 수 있을까요?[X,Y]?

를 "" 합니다로부터 를 " 수 합니다.Math.random()IEE-754 부동 소수점의 비트를 "롤링"하여 가능한 최대 값을 정확하게 1.0으로 두 배 정밀도를 높였지만, 특히 비트 조작에 적합하지 않은 언어에서는 이를 바로잡기가 쉽지 않은 것으로 보입니다.더 쉬운 방법이 있습니까?

왜과 같은 숫자를 생성합니까? (, 합니까?[0,1)[0,1]?)

[편집] 저는 이에 대한 필요성이 없으며 그 구별이 현학적이라는 것을 충분히 알고 있음을 유의하시기 바랍니다.단지 호기심이 많고 흥미로운 대답을 기대하는 것입니다.이 질문이 부적절한 경우 언제든지 종료 투표를 할 수 있습니다.

훨씬 더 나은 결정이 있으리라 생각하지만 이번 결정이 효과가 있을 것입니다 :)

function randomInRange(min, max) {
  return Math.random() < 0.5 ? ((1-Math.random()) * (max-min) + min) : (Math.random() * (max-min) + min);
}

우선 코드에 문제가 있습니다. 시도해 보십시오.randomInRange(0,5e-324)아니면 그냥 들어가기만 하면 됩니다.Math.random()*5e-324브라우저의 자바스크립트 콘솔에 저장할 수 있습니다.

오버플로/언더플로/디노멀이 없더라도 부동 소수점 연산에 대해 안정적으로 추론하기는 어렵습니다.조금만 파헤쳐도 반례를 찾을 수 있습니다.

>>> a=1.0
>>> b=2**-54
>>> rand=a-2*b
>>> a
1.0
>>> b
5.551115123125783e-17
>>> rand
0.9999999999999999
>>> (a-b)*rand+b
1.0

a=2와 b=0.5: 2-1이 그 다음으로 대표되는 숫자인 경우에 이러한 현상이 발생하는 이유를 보다 쉽게 설명할 수 있습니다.기본 반올림 모드("가장 가까운 짝수로 반올림")는 2-0.5를 위로 반올림합니다(2는 "짝수" [LSB = 0]이고 2-1은 "홀수" [LSB = 1]이므로). 따라서 차감합니다.b그리고 2를53 얻고 곱해서 2-1을53 얻고 추가합니다.b다시 2점을53 받기 위해서.


두 번째 질문에 대답하기기본 PRNG는 거의 항상 [0,2-1n] 구간에서 난수를 생성하므로, 즉, 난수 비트를 생성합니다.적합한 n(부유점 표현의 정밀도 비트)을 선택하고 2로n 나누어 예측 가능한 분포를 얻는 것은 매우 쉽습니다.몇 가지 숫자가 있습니다.[0,1)이 메서드를 사용하여 생성할 수 없습니다(IEEE 더블이 있는 (0,2-53)의 모든 것).

그것은 또한 당신이 할 수 있다는 것을 의미합니다.a[Math.floor(Math.random()*a.length)]오버플로우에 대한 걱정이 없습니다(homework:합니다임을 b < 1a*b < aa).

다른 좋은 점은 각 랜덤 출력 x가 구간 [x,x+2-53]을 나타내는 것으로 생각할 수 있다는 것입니다(반드시 반환되는 평균 값이 0.5보다 약간 작다는 것입니다).[0,1]에서 반환하는 경우 다른 모든 것과 동일한 확률로 엔드포인트를 반환합니까, 아니면 구간을 다른 모든 것과 동일하게 절반만 나타내므로 절반의 확률만 반환해야 합니까?

[0,1]에서 숫자를 반환하는 간단한 문제에 답하기 위해 아래 방법은 정수 [0,2n]를 효과적으로 생성하고 ([0,2-1n+1]에서 정수를 생성하고 너무 크면 버림으로써) 2로n 나눕니다:

function randominclusive() {
  // Generate a random "top bit". Is it set?
  while (Math.random() >= 0.5) {
    // Generate the rest of the random bits. Are they zero?
    // If so, then we've generated 2^n, and dividing by 2^n gives us 1.
    if (Math.random() == 0) { return 1.0; }
    // If not, generate a new random number.
  }
  // If the top bits are not set, just divide by 2^n.
  return Math.random();
}

의견은 기본 2를 암시하지만, 가정은 다음과 같습니다.

  • 0과 1은 등식으로 반환되어야 합니다(즉, Math.random()은 0에 가까운 부동 소수점 숫자의 가까운 간격을 사용하지 않습니다).
  • math.random() > = 확률 1/2의 0.5 (짝수 기저의 경우 참이어야 함)
  • 기본 PRNG는 우리가 이것을 할 수 있을 만큼 충분합니다.

: .while (a)다에 것 에 나옵니다.if또는 끝에 있는 것(b).0 0는 0.5하는 PRNG할 수 .

  • a=0   b=0  : 0
  • a=0   b=0.5: 0.5
  • a=0.5 b=0  : 1
  • a=0.5 b=0.5: 고리 모양의

문제:

  • 가정이 사실이 아닐 수도 있습니다.특히 일반적인 PRNG는 48비트 LCG의 상위 32비트를 취하는 것입니다(Firefox and Java do this).더블을 생성하려면 연속된 두 출력에서 53비트를 가져와서 2로 나누어야53 하지만 일부 출력은 불가능합니다(48비트 상태에서는 2개의53 출력을 생성할 수 없습니다!).그 중 일부는 (싱글 스레드 액세스를 가정할 때) 0을 반환하지 않는 것으로 의심되지만, 지금 당장은 Java의 구현을 확인하고 싶지 않습니다.
  • Math.random()은 여분의 비트를 얻어야 하는 결과로 모든 잠재적 출력에 대해 두 번이지만, 이것은 PRNG에 더 많은 제약을 가합니다(위의 LCG의 연속적인 출력 4개 정도를 추론해야 함).
  • math.random()은 출력당 평균 약 4번 호출됩니다.조금 느려요.
  • 결과를 결정적으로 버리기 때문에(단일 스레드 액세스를 가정), 출력 공간을 줄일 수 있습니다.

이 문제에 대한 저의 해결책은 항상 당신의 상한 대신 다음을 사용하는 것이었습니다.

Math.nextAfter(upperBound,upperBound+1)

아니면

upperBound + Double.MIN_VALUE

당신의 코드는 다음과 같습니다.

double myRandomNum = Math.random() * Math.nextAfter(upperBound,upperBound+1) + lowerBound;

아니면

double myRandomNum = Math.random() * (upperBound + Double.MIN_VALUE) + lowerBound;

이렇게 하면 상한이 가장 작은 두 배로 증가합니다(Double.MIN_VALUE상한이 확률 계산에 포함되도록 합니다.

이것은 확률을 어느 한 숫자에 유리하게 왜곡하지 않기 때문에 이를 해결하기 위한 좋은 방법입니다.

이 않는 이 입니다.Double.MAX_VALUE

선택한 닫힌 간격이 부분 집합이 되도록 반열린 간격을 조금 더 크게 선택하면 됩니다.그런 다음 해당 닫힌 구간에 도달할 때까지 랜덤 변수를 계속 생성합니다.

예:[3,8]에서 균일한 것을 원하는 경우 [3,9]에서 균일한 랜덤 변수를 [3,8]에 착지할 때까지 반복적으로 재생성합니다.

function randomInRangeInclusive(min,max) {
 var ret;
 for (;;) {
  ret = min + ( Math.random() * (max-min) * 1.1 );
  if ( ret <= max ) { break; }
 }
 return ret;
}

참고: 반열림 R.V를 생성하는 횟수는 무작위적이고 잠재적으로 무한대입니다. 하지만 그렇지 않으면 예상 호출 횟수를 원하는 만큼 1에 가깝게 만들 수 있고, 잠재적으로 무한대로 호출되지 않는 솔루션은 존재하지 않는다고 생각합니다.

0과 1 사이의 "극도로 큰" 값의 수를 고려할 때, 그것이 정말로 중요합니까?실제로 1을 칠 가능성은 매우 작기 때문에 지금 하고 있는 일에 큰 변화를 가져올 가능성은 매우 낮습니다.

범위에서 "균일한" 부동 소수점 수를 생성하는 것은 사소한 일이 아닙니다.예를 들어, 임의의 정수를 상수로 곱하거나 나누거나, 또는 "균일한" 부동 소수점 수를 원하는 범위로 스케일링함으로써, 부동 소수점 형식이 범위에서 나타낼 수 있는 모든 숫자가 이러한 방식으로 커버될 수 없고, 미묘한 편향 문제가 있을 수 있다는 단점이 있습니다.이러한 문제는 F의 "정수를 분할하여 임의 부동 소수점생성: 사례 연구"에서 자세히 설명합니다.구알라르.

문제가 얼마나 사소한 것인지 보여주기 위해, 다음 의사 코드는 폐쇄 구간 [lo, hi]에서 무작위 "균일 행동하는" 부동 소수점 번호를 생성합니다. 여기서 번호는 FPSign *FPSignific 및 *FPRADIX^FPexponent 형태입니다.아래 의사코드는 부동소수점 번호 생성에 관한 제 섹션에서 재현하였습니다.부동 소수점 숫자의 모든 정밀도와 기본(이진법 및 십진법 포함)에 대해 작동합니다.

METHOD RNDRANGE(lo, hi)
  losgn = FPSign(lo)
  hisgn = FPSign(hi)
  loexp = FPExponent(lo)
  hiexp = FPExponent(hi)
  losig = FPSignificand(lo)
  hisig = FPSignificand(hi)
  if lo > hi: return error
  if losgn == 1 and hisgn == -1: return error
  if losgn == -1 and hisgn == 1
    // Straddles negative and positive ranges
    // NOTE: Changes negative zero to positive
    mabs = max(abs(lo),abs(hi))
    while true
       ret=RNDRANGE(0, mabs)
       neg=RNDINT(1)
       if neg==0: ret=-ret
       if ret>=lo and ret<=hi: return ret
    end
  end
  if lo == hi: return lo
  if losgn == -1
    // Negative range
    return -RNDRANGE(abs(lo), abs(hi))
  end
  // Positive range
  expdiff=hiexp-loexp
  if loexp==hiexp
    // Exponents are the same
    // NOTE: Automatically handles
    // subnormals
    s=RNDINTRANGE(losig, hisig)
    return s*1.0*pow(FPRADIX, loexp)
  end
  while true
    ex=hiexp
    while ex>MINEXP
      v=RNDINTEXC(FPRADIX)
      if v==0: ex=ex-1
      else: break
    end
    s=0
    if ex==MINEXP
      // Has FPPRECISION or fewer digits
      // and so can be normal or subnormal
      s=RNDINTEXC(pow(FPRADIX,FPPRECISION))
    else if FPRADIX != 2
      // Has FPPRECISION digits
      s=RNDINTEXCRANGE(
        pow(FPRADIX,FPPRECISION-1),
        pow(FPRADIX,FPPRECISION))
    else
      // Has FPPRECISION digits (bits), the highest
      // of which is always 1 because it's the
      // only nonzero bit
      sm=pow(FPRADIX,FPPRECISION-1)
      s=RNDINTEXC(sm)+sm
    end
    ret=s*1.0*pow(FPRADIX, ex)
    if ret>=lo and ret<=hi: return ret
  end
END METHOD

상한을 포함하기 위해 부동 소수점 값이 필요한 상황은 어떻게 됩니까?정수의 경우 이해하지만 플로트의 경우 포괄과 배타의 차이는 1.0e-32와 같습니다.

이런 식으로 생각해 보세요. 소수점 있다고 한다면 정확히한다면,다를 이 높습니다.min0다입니다.을 받을 입니다.max 당신 수 그것에 대해 당신 스스로 결론을 내릴 수 있도록 하겠습니다.

이 '문제'는 실제 선에서 0과 1 사이의 임의의 점을 얻는 것과 같습니다.'포괄적'과 '배타적'은 없습니다.

질문은 1.0 직전 부동 소수점 번호가 무엇인지 묻는 것과 같습니다.이러한 부동 소수점 번호가 있지만 2^24 중 하나입니다(IEEE의 경우).float중의 경우) 2^53 (의)double).

실제로는 그 차이는 무시할 수 있습니다.

private static double random(double min, double max) {
    final double r = Math.random();
    return (r >= 0.5d ? 1.5d - r : r) * (max - min) + min;
}

Math.round()바인딩 값을 포함하는 데 도움이 됩니다.에.0 <= value < 1 그 (적),Math.round(value * 100) / 100s0 <= value <= 1(1은 포함).여기서 주의할 점은 이 값이 소수점 자리에 2자리만 있다는 것입니다.3자리를 원하신다면, 시도해보세요.Math.round(value * 1000) / 1000등등.다음 함수에는 소수점 자리의 숫자인 매개 변수가 하나 더 있습니다. 나는 정밀도라고 불렀습니다.

function randomInRange(min, max, precision) {
    return Math.round(Math.random() * Math.pow(10, precision)) /
            Math.pow(10, precision) * (max - min) + min;
}

이건 어때?

function randomInRange(min, max){
    var n = Math.random() * (max - min + 0.1) + min;
    return n > max ? randomInRange(min, max) : n;
}

만약 이것에 스택이 넘치면 제가 선물을 사겠습니다.

-- 편집: 현재는 신경쓰지 마세요.나는 다음과 같이 화가 났습니다.

randomInRange(0, 0.0000000000000000001)

스택 오버플로가 발생했습니다.

저는 경험이 상당히 부족해서 해결책도 찾고 있습니다.

제 생각은 대충 이렇습니다.

난수 생성기는 [0,1] 대신 [0,1]의 숫자를 생성합니다.

[0,1]은 [1,2) 등과 중복되지 않고 뒤에 따를 수 있는 단위 길이이기 때문입니다.

랜덤 [x, y]의 경우 다음 작업을 수행할 수 있습니다.

float randomInclusive(x, y){

    float MIN = smallest_value_above_zero;
    float result;
    do{
        result = random(x, (y + MIN));
    } while(result > y);
    return result;
}

[x, y]의 모든 값이 동일하게 선택될 가능성이 있는 경우, 이제 y에 도달할 수 있습니다.

언급URL : https://stackoverflow.com/questions/9724404/random-floating-point-double-in-inclusive-range

반응형