이 문서는 더는 지원되지 않는 이전 버전의 파이썬 용입니다. 업그레이드하고, 다음 설명서를 읽어야 합니다: 현재 안정 버전에 대한 파이썬 설명서.

# `random` — 의사 난수 생성

소스 코드: Lib/random.py

이 모듈은 다양한 분포에 대한 의사 난수 생성기를 구현합니다.

정수에 대해서는, 범위에서 균일한 선택이 있습니다. 시퀀스에 대해서는, 무작위 요소의 균일한 선택, 리스트를 제자리(in-place)에서 임의 순열을 생성하는 함수 및 중복 없는(without replacement) 무작위 표본 추출(sampling)을 위한 함수가 있습니다.

실수에 대해서는, 균일(uniform), 정규(normal) (가우시안(Gaussian)), 로그 정규(lognormal), 음의 지수(negative exponential), 감마(gamma) 및 베타(beta) 분포를 계산하는 함수가 있습니다. 각도 분포를 생성하기 위해, 폰 미제스(von Mises) 분포를 사용할 수 있습니다.

거의 모든 모듈 함수는 기본 함수 `random()`에 의존하는데, 이 함수는 반 열린 구간(semi-open range) [0.0, 1.0) 무작위 float를 균일하게 생성합니다. 파이썬은 메르센 트위스터(Mersenne Twister)를 핵심 생성기로 사용합니다. 53비트 정밀도의 float를 생성하며, 주기는 2**19937-1 입니다. C로 작성된 하부 구현은 빠르고 스레드 안전합니다. 메르센 트위스터는 가장 광범위하게 테스트 된 난수 생성기 중 하나입니다. 그러나, 완전히 결정적이므로, 모든 목적에 적합하지는 않으며, 암호화 목적에는 전혀 적합하지 않습니다.

이 모듈에서 제공하는 함수는 실제로는 `random.Random` 클래스의 숨겨진 인스턴스에 대해 연결된 메서드입니다. `Random` 인스턴스를 직접 인스턴스 화하여 상태를 공유하지 않는 생성기를 얻을 수 있습니다.

스스로 고안한 다른 기본 생성기를 사용하기 원한다면, 클래스 `Random`을 서브 클래싱 할 수도 있습니다: 이 경우, `random()`, `seed()`, `getstate()` 및 `setstate()` 메서드를 재정의하십시오. 선택적으로, 새로운 생성기는 `getrandbits()` 메서드를 제공할 수 있습니다 — 이것은 `randrange()`가 임의로 넓은 범위에서 선택을 생성할 수 있도록 합니다.

`random` 모듈은 운영 체제에서 제공하는 소스에서 난수를 생성하는 시스템 함수 `os.urandom()`을 사용하는 `SystemRandom` 클래스도 제공합니다.

경고

이 모듈의 의사 난수 생성기를 보안 목적으로 사용해서는 안 됩니다. 보안이나 암호화 용도를 위해서는, `secrets` 모듈을 참조하십시오.

더 보기

M. Matsumoto and T. Nishimura, “Mersenne Twister: A 623-dimensionally equidistributed uniform pseudorandom number generator”, ACM Transactions on Modeling and Computer Simulation Vol. 8, No. 1, January pp.3–30 1998.

긴 주기와 비교적 간단한 업데이트 연산을 하는 호환 가능한 대체 난수 생성기를 위한 Complementary-Multiply-with-Carry recipe

## 관리 함수

`random.``seed`(a=None, version=2)

  난수 생성기를 초기화합니다.

  a가 생략되거나 `None`이면, 현재 시스템 시간이 사용됩니다. 운영 체제에서 임의성 소스(randomness sources)를 제공하면, 시스템 시간 대신 사용됩니다 (가용성에 대한 자세한 내용은 `os.urandom()` 함수를 참조하십시오).

  a가 int이면, 직접 사용됩니다.

  버전(version) 2(기본값)에서는, `str`, `bytes` 또는 `bytearray` 객체가 `int`로 변환되어 모든 비트가 사용됩니다.

  버전(version) 1(이전 버전의 파이썬에서 온 임의의 시퀀스를 재현하기 위해 제공됩니다)에서는, `str`과 `bytes`를 위한 알고리즘은 더 좁은 범위의 시드(seed)를 생성합니다.

  버전 3.2에서 변경: 문자열 시드의 모든 비트를 사용하는 버전 2 체계로 이동했습니다.

  버전 3.9부터 폐지: 향후에, seed는 다음 형 중 하나여야 합니다: NoneType, `int`, `float`, `str`, `bytes` 또는 `bytearray`.

`random.``getstate`()

  생성기의 현재 내부 상태를 포착하는 객체를 반환합니다. 이 객체는 `setstate()`로 전달되어 상태를 복원 할 수 있습니다.

`random.``setstate`(state)

  state는 `getstate()`에 대한 이전 호출에서 얻은 것이어야 하고, `setstate()`는 생성기의 내부 상태를 `getstate()`가 호출될 당시의 상태로 복원합니다.

## 바이트열 함수

`random.``randbytes`(n)

  n 무작위 바이트를 생성합니다.

  이 메서드를 사용하여 보안 토큰을 생성해서는 안 됩니다. 대신 `secrets.token_bytes()`를 사용하십시오.

  버전 3.9에 추가.

## 정수 함수

`random.``randrange`(stop)
`random.``randrange`(start, stop[, step])

  `range(start, stop, step)`에서 임의로 선택된 요소를 반환합니다. 이것은 `choice(range(start, stop, step))`와 동등하지만, 실제로 range 객체를 만들지는 않습니다.

  위치 인자 패턴은 `range()`와 일치합니다. 함수가 예상치 못한 방식으로 키워드 인자를 사용할 수 있기 때문에 키워드 인자를 사용해서는 안 됩니다.

  버전 3.2에서 변경: `randrange()`는 균일하게 분포된 값을 생성하는 데 있어 더욱 정교합니다. 이전에는 약간 고르지 않은 분포를 생성 할 수 있는 `int(random()*n)`와 같은 스타일을 사용했습니다.

`random.``randint`(a, b)

  `a <= N <= b`를 만족하는 임의의 정수 N을 반환합니다. `randrange(a, b+1)`의 별칭.

`random.``getrandbits`(k)

  k 임의의 비트를 갖는 음이 아닌 파이썬 정수를 반환합니다. 이 메서드는 메르센 트위스터(Mersenne Twister) 생성기와 함께 제공되며, 일부 다른 생성기도 API의 선택적 부분으로 제공할 수 있습니다. 사용 가능할 때, `getrandbits()`는 `randrange()`가 임의로 넓은 범위를 처리 할 수 있도록 합니다.

  버전 3.9에서 변경: 이 메서드는 이제 k에 0을 허용합니다.

## 시퀀스 함수

`random.``choice`(seq)

  비어 있지 않은 시퀀스 seq에서 임의의 요소를 반환합니다. seq가 비어 있으면, `IndexError`를 발생시킵니다.

`random.``choices`(population, weights=None, *, cum_weights=None, k=1)

  population에서 중복을 허락하면서(with replacement) 선택한 k 크기의 요소 리스트를 반환합니다. population이 비어 있으면, `IndexError`를 발생시킵니다.

  weights 시퀀스가 지정되면, 상대 가중치에 따라 선택됩니다. 대안적으로, cum_weights 시퀀스가 제공되면, (아마도 `itertools.accumulate()`를 사용하여 계산된) 누적 가중치(cumulative weights)에 따라 선택이 이루어집니다. 예를 들어, 상대 가중치 `[10, 5, 30, 5]`는 누적 가중치 `[10, 15, 45, 50]`과 동등합니다. 내부적으로, 상대 가중치는 선택하기 전에 누적 가중치로 변환되므로, 누적 가중치를 제공하면 작업이 줄어듭니다.

  weights나 cum_weights를 지정하지 않으면, 같은 확률로 선택됩니다. weights 시퀀스가 제공되면, population 시퀀스와 길이가 같아야 합니다. weights와 cum_weights를 모두 지정하는 것은 `TypeError`입니다.

  weights나 cum_weights는 `random()`이 반환하는 `float` 값과 상호 운용되는 모든 숫자 형을 사용할 수 있습니다 (정수, 부동 소수점(float) 및 유리수(fractions)는 포함하지만, 십진수(decimal)는 제외합니다). 가중치가 음수이면 동작이 정의되지 않습니다. 모든 가중치가 0이면 `ValueError`가 발생합니다.

  주어진 시드에 대해, 균등한 가중치를 갖는 `choices()` 함수는 일반적으로 `choice()`에 대한 반복 호출과는 다른 시퀀스를 생성합니다. `choices()`에서 사용하는 알고리즘은 내부 일관성과 속도를 위해 부동 소수점 산술을 사용합니다. `choice()`에서 사용하는 알고리즘은 자리 올림 오차로 인한 작은 바이어스(bias)를 피하려고 반복 선택을 통한 정수 산술로 기본 설정됩니다.

  버전 3.6에 추가.

  버전 3.9에서 변경: 모든 가중치가 0이면 `ValueError`를 발생시킵니다.

`random.``shuffle`(x[, random])

  시퀀스 x를 제자리에서 섞습니다.

  선택적 인자 random은 [0.0, 1.0) 구간에서 임의의 float를 반환하는 0-인자 함수입니다; 기본적으로 이것은 `random()` 함수입니다.

  불변 시퀀스를 섞고 새로운 섞인 리스트를 반환하려면, 대신 `sample(x, k=len(x))`를 사용하십시오.

  작은 `len(x)`의 경우에조차, x의 총 순열 수는 대부분의 난수 생성기 주기보다 빠르게 커질 수 있습니다. 이것은 긴 시퀀스의 대부분 순열이 절대로 생성될 수 없음을 의미합니다. 예를 들어, 길이가 2080인 시퀀스가 메르센 트위스터 난수 생성기의 주기 안에 들어갈 수 있는 최대입니다.

  Deprecated since version 3.9, will be removed in version 3.11: 선택적 매개 변수 random.

`random.``sample`(population, k, *, counts=None)

  population 시퀀스나 집합에서 선택한 고유한 요소의 k 길이 리스트를 반환합니다. 중복 없는(without replacement) 무작위 표본 추출(sampling)에 사용됩니다.

  원래 population을 변경하지 않고, population의 요소를 포함하는 새 리스트를 반환합니다. 결과 리스트는 선택 순서를 따라서, 모든 서브 슬라이스도 유효한 임의의 표본이 됩니다. 이것은 추첨 당첨자(표본)를 대상(grand prize)과 차점자들(서브 슬라이스)로 나눌 수 있도록 합니다.

  population의 멤버는 해시 가능하거나 고유할 필요가 없습니다. population이 반복을 포함하면, 각 등장(occurrence)은 표본에서 가능한 선택입니다.

  반복되는 요소는 한 번에 하나씩 또는 선택적 키워드 전용 counts 매개 변수로 지정할 수 있습니다. 예를 들어 `sample(['red', 'blue'], counts=[4, 2], k=5)`는 `sample(['red', 'red', 'red', 'red', 'blue', 'blue'], k=5)`와 동등합니다.

  정수 범위에서 표본을 선택하려면, `range()` 객체를 인자로 사용하십시오. 이는 큰 population에서 표본 추출할 때 특히 빠르고 공간 효율적입니다: `sample(range(10000000), k=60)`.

  표본 크기가 population 크기보다 크면 `ValueError`가 발생합니다.

  버전 3.9에서 변경: counts 매개 변수를 추가했습니다.

  버전 3.9부터 폐지: 향후에, population은 시퀀스여야 합니다. `set` 인스턴스는 더는 지원되지 않습니다. 집합은 먼저 `list`나 `tuple`로 변환되어야 합니다, 표본을 재현할 수 있도록 결정론적 순서가 선호됩니다.

## 실수 분포

다음 함수는 특정 실수 분포를 생성합니다. 함수 매개 변수는 일반적인 수학적 관행에 사용되는 분포 방정식에서 해당 변수의 이름을 따서 명명됩니다; 이러한 방정식의 대부분은 모든 통계 교과서에서 찾을 수 있습니다.

`random.``random`()

  [0.0, 1.0) 구간에서 다음 임의의 부동 소수점 숫자를 반환합니다.

`random.``uniform`(a, b)

  `a <= b` 일 때 `a <= N <= b`, `b < a` 일 때 `b <= N <= a`를 만족하는 임의의 부동 소수점 숫자 N을 반환합니다.

  종단 값 `b`는 방정식 `a + (b-a) * random()`의 부동 소수점 자리 올림에 따라 범위에 포함되거나 포함되지 않을 수 있습니다.

`random.``triangular`(low, high, mode)

  `low <= N <= high`를 만족하고 이 경계 사이에 지정된 모드(mode)를 갖는 임의의 부동 소수점 숫자 N을 반환합니다. low 및 high 경계는 기본적으로 0과 1입니다. mode 인자는 기본적으로 경계 사이의 중간 점으로, 대칭 분포를 제공합니다.

`random.``betavariate`(alpha, beta)

  베타 분포. 매개 변수의 조건은 `alpha > 0`과 `beta > 0`입니다. 반환된 값의 범위는 0에서 1입니다.

`random.``expovariate`(lambd)

  지수 분포. lambd는 1.0을 원하는 평균으로 나눈 값입니다. 0이 아니어야 합니다. (매개 변수는 “lambda”라고 부르지만, 파이썬에서는 예약어입니다.) 반환된 값의 범위는, lambd가 양수이면 0에서 양의 무한대이고, lambd가 음수이면 음의 무한대에서 0입니다.

`random.``gammavariate`(alpha, beta)

  감마 분포. (Not 감마 함수가 아닙니다!) 매개 변수의 조건은 `alpha > 0`과 `beta > 0`입니다.

  확률 분포 함수는 다음과 같습니다:

                x ** (alpha - 1) * math.exp(-x / beta)
      pdf(x) =  --------------------------------------
                  math.gamma(alpha) * beta ** alpha

`random.``gauss`(mu, sigma)

  가우시안 분포. mu는 평균이고, sigma는 표준 편차입니다. 이것은 아래에 정의된 `normalvariate()` 함수보다 약간 빠릅니다.

  다중 스레딩 참고: 두 스레드가 이 함수를 동시에 호출하면, 같은 반환 값을 받을 수 있습니다. 이것은 세 가지 방법으로 피할 수 있습니다. 1) 각 스레드가 난수 생성기의 다른 인스턴스를 사용하도록 합니다. 2) 모든 호출에 록을 둡니다. 3) 더 느리지만, 스레드 안전한 `normalvariate()` 함수를 대신 사용합니다.

`random.``lognormvariate`(mu, sigma)

  로그 정규 분포. 이 분포의 자연로그를 취하면, 평균 mu와 표준 편차 sigma를 갖는 정규 분포를 얻게 됩니다. mu는 아무 값이나 될 수 있으며, sigma는 0보다 커야 합니다.

`random.``normalvariate`(mu, sigma)

  정규 분포. mu는 평균이고, sigma는 표준 편차입니다.

`random.``vonmisesvariate`(mu, kappa)

  mu는 0과 2*pi 사이의 라디안으로 표현된 평균 각도이며, kappa는 0 이상이어야 하는 집중도(concentration) 매개 변수입니다. kappa가 0이면, 이 분포는 0에서 2*pi에 걸친 균등한 임의의 각도로 환원됩니다.

`random.``paretovariate`(alpha)

  파레토 분포. alpha는 모양(shape) 매개 변수입니다.

`random.``weibullvariate`(alpha, beta)

  베이불 분포. alpha는 크기(scale) 매개 변수이고 beta는 모양(shape) 매개 변수입니다.

## 대체 생성기

class `random.``Random`([seed])

  `random` 모듈에서 사용하는 기본 의사 난수 생성기를 구현하는 클래스.

  버전 3.9부터 폐지: 향후에, seed는 다음 형 중 하나여야 합니다: `NoneType`, `int`, `float`, `str`, `bytes` 또는 `bytearray`.

class `random.``SystemRandom`([seed])

  운영 체제에서 제공하는 소스에서 난수를 생성하기 위해 `os.urandom()` 함수를 사용하는 클래스. 모든 시스템에서 사용 가능한 것은 아닙니다. 소프트웨어 상태에 의존하지 않으며, 시퀀스는 재현되지 않습니다. 따라서, `seed()` 메서드는 효과가 없으며, 무시됩니다. `getstate()`와 `setstate()` 메서드는 호출되면 `NotImplementedError`를 발생시킵니다.

## 재현성에 대한 참고 사항

때때로 의사 난수 생성기가 만든 시퀀스를 재현하는 것이 유용 할 수 있습니다. 시드 값을 재사용하면, 여러 스레드가 실행되고 있지 않은 한 실행할 때마다 같은 시퀀스를 재현할 수 있어야 합니다.

random 모듈의 알고리즘과 시딩(seeding) 함수의 대부분은 파이썬 버전에 따라 변경될 수 있지만, 두 가지 측면은 변경되지 않음이 보장됩니다:

- 새로운 시딩 메서드가 추가되면, 이전 버전과 호환되는 시더(seeder)가 제공될 것입니다.

- 호환 시더에 같은 시드가 제공되면 생성기의 `random()` 메서드는 같은 시퀀스를 계속 생성할 것입니다.

## 예제

기본 예제:

    >>> random()                             # Random float:  0.0 <= x < 1.0
    0.37444887175646646

    >>> uniform(2.5, 10.0)                   # Random float:  2.5 <= x <= 10.0
    3.1800146073117523

    >>> expovariate(1 / 5)                   # Interval between arrivals averaging 5 seconds
    5.148957571865031

    >>> randrange(10)                        # Integer from 0 to 9 inclusive
    7

    >>> randrange(0, 101, 2)                 # Even integer from 0 to 100 inclusive
    26

    >>> choice(['win', 'lose', 'draw'])      # Single random element from a sequence
    'draw'

    >>> deck = 'ace two three four'.split()
    >>> shuffle(deck)                        # Shuffle a list
    >>> deck
    ['four', 'two', 'ace', 'three']

    >>> sample([10, 20, 30, 40, 50], k=4)    # Four samples without replacement
    [40, 10, 50, 30]

시뮬레이션:

    >>> # Six roulette wheel spins (weighted sampling with replacement)
    >>> choices(['red', 'black', 'green'], [18, 18, 2], k=6)
    ['red', 'green', 'black', 'black', 'red', 'black']

    >>> # Deal 20 cards without replacement from a deck
    >>> # of 52 playing cards, and determine the proportion of cards
    >>> # with a ten-value:  ten, jack, queen, or king.
    >>> dealt = sample(['tens', 'low cards'], counts=[16, 36], k=20)
    >>> dealt.count('tens') / 20
    0.15

    >>> # Estimate the probability of getting 5 or more heads from 7 spins
    >>> # of a biased coin that settles on heads 60% of the time.
    >>> def trial():
    ...     return choices('HT', cum_weights=(0.60, 1.00), k=7).count('H') >= 5
    ...
    >>> sum(trial() for i in range(10_000)) / 10_000
    0.4169

    >>> # Probability of the median of 5 samples being in middle two quartiles
    >>> def trial():
    ...     return 2_500 <= sorted(choices(range(10_000), k=5))[2] < 7_500
    ...
    >>> sum(trial() for i in range(10_000)) / 10_000
    0.7958

표본의 평균에 대한 신뢰 구간을 추정하기 위해 중복을 허용하는(with replacement) 재표본추출(resampling)을 사용하는 통계적 부트스트래핑(statistical bootstrapping)의 예:

    # http://statistics.about.com/od/Applications/a/Example-Of-Bootstrapping.htm
    from statistics import fmean as mean
    from random import choices

    data = [41, 50, 29, 37, 81, 30, 73, 63, 20, 35, 68, 22, 60, 31, 95]
    means = sorted(mean(choices(data, k=len(data))) for i in range(100))
    print(f'The sample mean of {mean(data):.1f} has a 90% confidence '
          f'interval from {means[5]:.1f} to {means[94]:.1f}')

약물과 위약의 효과 간에 관찰된 차이의 통계적 유의성 또는 p-값을 결정하기 위한 재표본추출 순열 검증(resampling permutation test)의 예:

    # Example from "Statistics is Easy" by Dennis Shasha and Manda Wilson
    from statistics import fmean as mean
    from random import shuffle

    drug = [54, 73, 53, 70, 73, 68, 52, 65, 65]
    placebo = [54, 51, 58, 44, 55, 52, 42, 47, 58, 46]
    observed_diff = mean(drug) - mean(placebo)

    n = 10_000
    count = 0
    combined = drug + placebo
    for i in range(n):
        shuffle(combined)
        new_diff = mean(combined[:len(drug)]) - mean(combined[len(drug):])
        count += (new_diff >= observed_diff)

    print(f'{n} label reshufflings produced only {count} instances with a difference')
    print(f'at least as extreme as the observed difference of {observed_diff:.1f}.')
    print(f'The one-sided p-value of {count / n:.4f} leads us to reject the null')
    print(f'hypothesis that there is no difference between the drug and the placebo.')

다중 서버 큐를 위한 도착 시간과 서비스 제공의 시뮬레이션:

    from heapq import heapify, heapreplace
    from random import expovariate, gauss
    from statistics import mean, median, stdev

    average_arrival_interval = 5.6
    average_service_time = 15.0
    stdev_service_time = 3.5
    num_servers = 3

    waits = []
    arrival_time = 0.0
    servers = [0.0] * num_servers  # time when each server becomes available
    heapify(servers)
    for i in range(1_000_000):
        arrival_time += expovariate(1.0 / average_arrival_interval)
        next_server_available = servers[0]
        wait = max(0.0, next_server_available - arrival_time)
        waits.append(wait)
        service_duration = max(0.0, gauss(average_service_time, stdev_service_time))
        service_completed = arrival_time + wait + service_duration
        heapreplace(servers, service_completed)

    print(f'Mean wait: {mean(waits):.1f}.  Stdev wait: {stdev(waits):.1f}.')
    print(f'Median wait: {median(waits):.1f}.  Max wait: {max(waits):.1f}.')

더 보기

시뮬레이션(simulation), 표본 추출(sampling), 섞기(shuffling) 및 교차 검증(cross-validation)을 포함하는 몇 가지 기본 개념만을 사용한, 통계 분석에 대한 Jake Vanderplas의 비디오 자습서 Statistics for Hackers

Economics Simulation 이 모듈에서 제공하는 많은 도구와 분포(gauss, uniform, sample, betavariate, choice, triangular 및 randrange)의 효과적인 사용을 보여주는 Peter Norvig의 시장(marketplace) 시뮬레이션.

A Concrete Introduction to Probability (using Python) 확률 이론의 기초, 시뮬레이션 작성 방법 및 파이썬을 사용해서 데이터 분석을 수행하는 방법을 다루는 Peter Norvig의 자습서.

## 조리법

기본 `random()`은 0.0 ≤ x < 1.0 범위에서 2⁻⁵³의 배수를 반환합니다. 이러한 모든 숫자는 균등 간격으로 분포되어있고 파이썬 float로 정확하게 표현할 수 있습니다. 그러나, 해당 범위의 다른 많은 표현 가능한 부동 소수점은 가능한 선택이 아닙니다. 예를 들어, `0.05954861408025609`는 2⁻⁵³의 정수배가 아닙니다.

다음 조리법은 다른 접근 방식을 사용합니다. 범위의 모든 부동 소수점이 가능한 선택입니다. 가수(mantissa)는 2⁵² ≤ mantissa < 2⁵³ 범위에 있는 정수의 균등 분포(uniform distribution)에서 옵니다. 지수(exponent)는 -53보다 작은 지수가 다음으로 큰 지수의 절반만큼 자주 발생하는 기하 분포(geometric distribution)에서 옵니다.

    from random import Random
    from math import ldexp

    class FullRandom(Random):

        def random(self):
            mantissa = 0x10_0000_0000_0000 | self.getrandbits(52)
            exponent = -53
            x = 0
            while not x:
                x = self.getrandbits(32)
                exponent += x.bit_length() - 32
            return ldexp(mantissa, exponent)

클래스의 모든 실숫값 분포는 새 메서드를 사용합니다:

    >>> fr = FullRandom()
    >>> fr.random()
    0.05954861408025609
    >>> fr.expovariate(0.25)
    8.87925541791544

조리법은 개념적으로 0.0 ≤ x < 1.0 범위의 2⁻¹⁰⁷⁴의 모든 배수에서 선택하는 알고리즘과 동등합니다. 이러한 모든 숫자는 균등한 간격이지만, 대부분은 가장 가까운 표현 가능한 파이썬 부동 소수점으로 자리 내림해야 합니다. (값 2⁻¹⁰⁷⁴은 가장 작은 양의 정규화되지 않은 부동 소수점이며 `math.ulp(0.0)`과 같습니다.)

더 보기

Generating Pseudo-random Floating-Point Values Allen B. Downey의 논문은 `random()`이 일반적으로 생성하는 것보다 더 세밀한 부동 소수점을 생성하는 방법을 설명합니다.