실습_3. 확률과 통계_경우의 수와 확률

Updated:

실습

순열

주어진 n개의 원소 중 r개를 뽑아 나열하는 것을 순열이라고 하고 그 개수를 nPr로 표기합니다.

체육대회에서 릴레이 계주에 나갈 사람을 반에서 정하기로 했습니다. 계주에서 달릴 순서도 정합니다. 반의 인원과 계주에 나갈 사람의 수가 주어졌을 때, 계주에 나가는 팀의 경우의 수를 구하는 함수를 작성해 봅시다.

  • 입력
n: 반 인원 (자연수)  
r: 계주 인원 (자연수)
  • 출력
n명 중 계주에 나갈 사람 r명을 뽑아 순서를 정하는 경우의 수
  • 실행 결과
>>> running_team(5, 2)
20
>>> running_team(10, 3)
720
>>> running_team(30, 6)
427518000
  • 이렇게 해보세요!

릴레이 계주 문제를 순열 문제로 생각해보세요.

이론에서 배운 순열의 개수 구하는 공식을 코드로 구현해 보세요.

  • Tip

팩토리얼의 계산을 위해 math라이브러리의 math.factorial을 사용할 수 있습니다.

예를 들어,

print(math.factorial(5))

위의 코드는 팩토리얼 5!인 120을 출력합니다.

def running_team(n, r):
    # 2. 순열의 공식(n!//(n-r)!)을 작성하고, 이를 반환해봅시다.
    mul = 1
    for i in range(r):
        mul*=(n-i)
    return mul
    
print(running_team(5, 2))

같은 것이 있는 순열

순열에 같은 것이 있는 경우에는 순열의 경우의 수를 계산하는 방법이 조금 달라집니다. 같은 것끼리는 순서를 매기는 의미가 없기 때문입니다.

사과의 개수와 오렌지의 개수가 주어졌을 때 주어진 모든 과일을 나열하는 경우의 수를 계산하는 함수를 작성해 봅시다.

  • 입력
a: 사과의 개수 (자연수)  
o: 오렌지의 개수 (자연수)
  • 출력
모든 과일들을 나열하는 경우의 수
  • 실행 결과
>>> fruit(2, 2)
6
>>> fruit(3, 4)
35
>>> fruit(15, 10)
3268760
  • 이렇게 해보세요!

우선 주어진 사과와 오렌지를 나열하는 방법의 가짓수를 계산해봅시다.

1에서 구한 값에서 같은 과일간의 순서를 의미없게 하기 위해 (개수)! 만큼을 나누어보세요.

# 1. factorial을 사용하기 위해 math 모듈을 불러와봅시다.
import math

def fruit(a, o):
    # 2-1. 주어진 과일들을 모두 나열하는 방법의 가짓수((a+o)!)를 계산해봅시다.
    # 2-2. 같은 과일간의 순서를 의미없게 하기 위해 각 과일의 개수(a! * o!)만큼 나누어봅시다.

    return math.factorial(a+o) // math.factorial(a) // math.factorial(o)
    
print(fruit(15, 10))

조합

주어진 n개의 원소 중 순서에 상관없이 r개를 뽑는 방법을 조합이라고 하고 경우의 수를 nCr로 표현합니다.

로또 번호를 만드는 문제는 대표적인 조합 문제입니다. 뽑은 번호의 조합은 순서는 상관이 없기 때문입니다. 우리나라의 로또6/45는 45개의 숫자중 6개의 숫자를 선택하는 조합 문제입니다. 로또의 전체 숫자의 개수과 맞추어야할 번호의 개수가 주어졌을때, 로또 숫자의 조합의 경우의 수를 구하는 함수를 작성해 봅시다.

  • 입력
n: 로또 전체 숫자 개수 (자연수)
r: 선택할 수 있는 번호 개수 (자연수)
  • 출력
선택 가능한 로또 숫자 조합의 경우의 수
<Example>

>>> lotto(45, 6)
8145060
>>> lotto(38, 5)
501942
>>> lotto(63, 29)
759510004936100355
import math

def lotto(n, r):
    # To-do
    # 2. 조합의 공식(n!//((n-r)! * r!))을 작성하고, 이를 반환해봅시다.
    return math.factorial(n)//math.factorial(n-r)//math.factorial(r)
    
print(lotto(45, 6))

통계적 확률

동전을 던져서 앞면이 나올 확률은 일반적이라면 1/2입니다. 그러나 이는 앞면이 나올 확률과 뒷면이 나올 확률이 같다는 전제하에 계산된 확률입니다.실제로 동전을 던져보아 나온 결과를 바탕으로 계산한 통계적 확률은 다를 수도 있습니다.

동전이 하나 있습니다. 이 동전은 각 면의 무게가 다르게끔 제작되어 던졌을때 앞면과 뒷면이 나올 확률이 다릅니다. 앞면이 나올 확률을 구하는 방법은 실제로 던져보아 빈도를 세는 방법 뿐입니다. 동전의 앞면이 나올 확률이 0.5±0.001의 범위 내에서라면 정상적인 동전이라고 판단합니다. 주어진 동전이 정상인지 판단하는 함수를 작성해 봅시다.

  • 입력
coin: 동전
  • 출력
정상적인 동전이면 True, 아니면 False

Coin

동전을 던진 결과를 알아보기 위해 다음과 같은 코드를 사용합니다. 앞면이면 ‘H’, 뒷면이면 ‘T’를 리턴합니다.

# 동전을 던져 결과를 확인한다
>> coin.toss()
'H'
>> coin.toss()
'T'
  • 실행 결과
# 정상적인 동전의 경우
>> stat_prob(real_coin)
True
# 확률이 조작된 동전의 경우
>> stat_prob(fake_coin)
False
  • 이렇게 해보세요!

coin.toss()로 동전을 던져 결과를 확인해보세요.

충분한 횟수만 큼 던져 그중 몇 번이나 앞면이 나왔는지 세어보세요.

전체 중 앞면이 나온 횟수를 사용해 앞면이 나오는 통계적 확률을 계산해보세요.

통계적 확률이 0.4999와 0.5001사이라면 진짜 동전이라고 생각할 수 있습니다.

import coin

def stat_prob(coin):
    # 1. n회 반복하는 for문을 만들기 위해 n을 선언하고, 충분히 큰 수(ex) 1000000)를 대입해보세요.
    n = 1000000
    
    # 2. 앞면이 나온 횟수를 담는 변수 h를 선언하고, 0을 대입해봅시다. (아직 시행을 하지 않았으니까요!)
    h = 0
    
    # 3. n회 반복하는 반복문을 만들어봅시다.
    # 3-1. 조건문을 선언하고, 만약 coin을 던졌을 때 H가 나온다면 h를 1개 증가합니다.
    for i in range(n):
        if coin.toss() == 'H':
            h+=1
    
    # 4. 확률을 담는 변수 p를 선언하고, 앞면이 나온 횟수(h)/던진 횟수(n)를 대입해봅시다.
    p = h/n
    
    # 5. 만약 p와 0.5 사이의 차이(오차)가 0.001 이내라면 True, 아니라면 False 반환
    #    0.5와 p 중 무엇이 더 큰지 모르므로 절댓값함수 abs()를 사용
    if abs(0.5 - p) <= 0.001:
        return True
    else:
        return False
    
real_coin = coin.Coin(0.5)
fake_coin = coin.Coin(0.8)
print(stat_prob(real_coin))
print(stat_prob(fake_coin))

독립시행

어떤 독립시행을 여러번 행하였을때, 특정 사건이 몇번이나 일어나는 지를 계산하는 법을 이론에서 배웠습니다.

한 축구팀의 에이스가 프리킥을 했을때 골을 할 확률이 p라고 합니다. 이 선수는 평소 멘탈관리가 확실해서 자신이 슛을 성공했든 아니든 다음 슛에 영향이 없다고 합니다. 이 선수가 총 n번의 슛을 했을때 r번 성공할 확률을 계산하는 함수를 작성해 봅시다.

  • 입력
p: 슛이 성공할 확률 (0 \le p \le 10≤p≤1인 실수) n: 주어진 슛 기회 (자연수) r: 슛 성공 개수 (자연수)
  • 출력
슛 성공 확률이 p인 선수가 n 번의 슛 중 r번 성공할 확률
  • 실행 결과
>>> shoot_prob(0.5, 10, 3)
0.1171875
>>> shoot_prob(0.3, 5, 1)
0.3601499999999999
>>> shoot_prob(0.9, 10, 10)
0.3486784401000001
  • 이렇게 해보세요!

슈팅 문제를 독립시행을 여러번하는 문제로 생각해보세요.

독립시행에서 특정 횟수만큼 일어나는 확률 계산 공식을 코드로 구현해보세요.

  • Tip!

독립시행을 구할때 제곱 연산자 (**) 를 활용해보세요.

import math
# 1. factorial을 사용하기 위해 math 모듈을 불러와봅시다.

def shoot_prob(p, n, r):
    # 2. 독립시행의 확률을 구하기 위한 공식을 사용하여 확률을 반환해보세요.
    #   2-1. 골을 r번 넣을 확률 pa = p ** r
    pa = p ** r
    
    #   2-2. 골을 넣지 못할 나머지 확률 pac = (1-p) ** (n-r)
    pac = (1-p) ** (n-r)
    
    #   2-3. 골을 r번 넣을 모든 경우의 수 num = n! // (r! * (n-r)!)
    num = math.factorial(n) // (math.factorial(r) * math.factorial(n-r))
    
    #   2-4. 한번의 시행의 성공 확률이 p일 때 n번 중 r번 성공할 확률
    #     prob = pa * pac * num
    prob = pa * pac * num
    
    #   2-5. 이 prob이 우리가 구하기 원했던 확률이므로 반환
    return prob
    
print(shoot_prob(0.5, 10, 3))

베이지안 확률

베이지안 확률은 주어진 정보를 바탕으로 미래를 예측하는 방법으로 널리 사용되는 확률론 중 하나입니다.

P(A1B)=P(A)P(B1A)/P(B)

베이지안 확률을 사용해서 스팸메일 분류기를 구현해 봅시다.

먼저 스팸메일 리스트가 주어집니다. 각각의 메일은 영어 소문자와 공백문자만으로 이루어진 문자열입니다.

'hello my name is elice'
'free money for you'
'we all lie'

그리고 각 메일의 스팸메일 여부가 담긴 리스트가 주어집니다. True 라면 스팸메일입니다.

False
True
False

이러한 정보가 제공되었을때 특정 키워드를 포함한 메일을 받았을 때 이 메일이 스팸메일일 확률을 구하는 함수를 작성해 봅시다. 이 때 주어지는 키워드를 포함한 메일이 적어도 한 통은 존재한다고 가정합니다.

  • 입력
mails: 문자열 리스트  
spam: 각 메일의 스팸 여부  
key: 스팸 메일 확률을 구할 단어
  • 출력
key를 포함한 메일을 받았을 때 이 메일이 스팸 메일일 확률
  • 실행 결과
>>> bayesian(['hello my name is elice', 'free money for you', 'we all lie', 'free for all'], [False, True, False, False], 'free')
1.0
  • 이렇게 해보세요!

A를 메일이 스팸메일인 경우, B를 메일에 key가 포함된 경우라고 가정해보세요.

베이지안 공식 계산을 위한 P(A), P(B), P(B1A)P(A),P(B),P(B1A)를 먼저 계산해보세요.

베이지안 공식에 구한 값들을 대입하여 P(A1B)P(A1B)을 계산해보세요.

  • Tip

리스트에서 특정 원소의 개수를 세기위해 list.count 함수를 사용할 수 있습니다.

def bayesian(mails, spam, key):
    # 1. 전체 메일 중 스팸메일을 고르는 사건을 A라 하고,
    # P(A) = 스팸 메일의 수 / 전체 메일의 수 를 구해봅시다.
    s_cnt = spam.count(True)
    P_A = s_cnt/len(spam)
    print(P_A)
    
    # 2. 전체 메일 중 문자열 key이 포함된 메일을 고르는 사건을 B라 하고,
    # P(B) = key가 들어있는 메일의 수 / 전체 메일의 수를 구해봅시다.
    m_cnt = 0
    for mail in mails:
        if key in mail:
            m_cnt+=1
    P_B = m_cnt/len(mails)
    print(P_B)
    
    # 3. 스팸메일을 골랐을 때(A) 그 메일에 key가 포함(B)되어있을 확률을 구해봅시다.
    # 즉, P(B|A) = P(A∩B) / P(A)를 구해봅시다.
    # P(A) : 고른 메일이 스팸메일인 확률
    # P(A∩B) : 고른 메일이 스팸메일이면서 메일에 key가 포함되어있을 확률
    a_b_cnt = 0
    for i in range(len(mails)):
        if key in mails[i] and spam[i]:
            a_b_cnt+=1
    A_and_B = a_b_cnt / len(mails)
    
    B_A = A_and_B/P_A
    
    print(B_A)
    
    # 4. 메일에 key가 포함되어 있을 때 그 메일이 스팸메일일 확률을 구합니다.
    # 베이즈 정리에 의해 구하고자 하는 확률은
    # P(A|B) = P(A) * P(B|A) / P(B)입니다.
    P_A * A_and_B / P_B
    
    return P_A * B_A / P_B
    
print(bayesian(['hello my name is elice', 'free money for you', 'we all lie', 'free for all'], [False, True, False, False], 'free'))

미션

순열 2

파이썬의 기본 라이브러리 중에는 순열의 가능한 경우를 모두 구할 수 있는 기능이 존재합니다. itertools.permutations을 이용하면 주어진 리스트의 순열을 구할 수 있습니다.

>>> import itertools
>>> a = ['A', 'B', 'C']
>>> print(list(itertools.permutations(a)))
[('A', 'B', 'C'), ('A', 'C', 'B'), ('B', 'A', 'C'), ('B', 'C', 'A'), ('C', 'A', 'B'), ('C', 'B', 'A')]
>>> print(list(itertools.permutations(a, 2)))
[('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]

permutations을 사용해서 nPr의 값을 구하는 함수를 작성해 봅시다.

  • 입력
n: 자연수  
r: 자연수
  • 출력
순열의 경우의 수 nPr의 값
  • 실행 결과
>> p(5, 2)
20
>> p(10, 3)
720
  • 이렇게 해보세요!

permutations 함수로 수열의 리스트를 구해 개수를 세는 함수를 작성해보세요.

import itertools

def p(n, r):
    # itertools.permutations(list, r)을 사용하여 순열을 구할 수 있습니다.
    # 이를 사용하여 주어진 n개의 원소 중 r개를 뽑아 나열하여 순열을 구하고, 이를 반환해보세요. 
    arr = [0 for i in range(n)]
    return len(list(itertools.permutations(arr,r)))
    
print(p(5, 2))

조합 2

순열과 마찬가지로 조합을 구하는 함수도 존재합니다. itertools.combinations을 사용하면 주어진 리스트에서 조합을 구할 수 있습니다. permutation과 달리 뽑는 개수가 반드시 지정되어야 합니다.

>>> import itertools
>>> a = ['A', 'B', 'C']
>>> print(list(itertools.combinations(a, 3)))
[('A', 'B', 'C')]
>>> print(list(itertools.combinations(a, 2)))
[('A', 'B'), ('A', 'C'), ('B', 'C')]

combinations 함수를 사용하여 nCr의 값을 구하는 함수를 작성해 봅시다.

  • 입력
n: 자연수  
r: 자연수
  • 출력
조합의 경우의 수 nCr의 값
<Example>

>>> c(3, 2)
3
>>> c(5, 3)
10
>>> c(7, 1)
7
  • 이렇게 해보세요!

combinations 함수로 조합의 리스트를 구해 개수를 세는 함수를 작성해보세요.

import itertools

def c(n, r):
    # itertools.combinations(list, r)을 사용하여 조합을 구할 수 있습니다.
    # 이를 사용하여 주어진 n개의 원소 중 r개를 뽑아 나열하여 조합을 구하고, 이를 반환해보세요. 
    
    arr = [0 for i in range(n)]
    
    return len(list(itertools.combinations(arr, r)))
    
print(c(3, 2))

확률 계산

두 사건 A와 B가 독립사건이라면 사건 A와 B가 동시에 일어날 확률은 두 확률의 곱입니다. 그리고 두 사건 A와 B가 배반사건이라면 사건 A 또는 B가 일어날 확률은 두 확률의 합입니다. 이번 문제는 기본적인 확률의 계산 문제입니다.

캐롤과 데이브는 일과를 마치면 각자 주변에 있는 시설에서 여가를 보냅니다. 두 사람이 어디를 가든지 다른 사람에게는 영향이 없습니다. 주변에 있는 시설은 영화관, 볼링장, VR방 등 총 n개가 존재하고 두 사람이 반드시 이 중 한 군데를 간다고 할 때 일과를 마친 두 사람이 같은 시설을 선택 할 확률은 얼마나 될까요? 각 시설을 이용할 확률은 모두 같다고 가정합니다.

  • 입력
n: 시설의 개수 (n)
  • 출력
두 사람이 여가 시간에 만날 확률
  • 실행 결과
>>> meet_prob(1)
1.0
>>> meet_prob(2)
0.5
>>> meet_prob(3)
0.3333333333333333
  • 이렇게 해보세요!

두 사람이 각각의 장소에서 마주칠 확률을 곱사건이라 생각하고 계산해보세요.

그리고 각각의 장소에서 마주칠 확률을 모두 더하여 합사건으로 생각하고 계산해보세요.

def meet_prob(n):
    return 1/n
    
print(meet_prob(1))

조건부 확률

조건부 확률은 어떤 사건에 영향을 받는 다른 사건이 일어날 확률을 말합니다. 사건 A가 일어났을 때 이에 영향을 받는 사건 B가 일어날 확률은 P(B A)P(B A)로 나타냅니다.

기상청에 따르면 전날 비가 왔는냐의 여부는 오늘 비가 오는지에 영향을 끼친다고 합니다. 그 표는 다음과 같습니다.

전날 날씨 오늘 날씨 확률
맑음 맑음 ss
맑음 비 sr
비 맑음 rs
비 비 rr
전날 비가 왔을 때 오늘 비가 올 조건부 확률은 rr이고 전날 날씨가 맑을 때 비가 올 조건부 확률은 sr이 됩니다.

오늘의 날씨가 맑았다고 했을 때 모레 날씨가 맑을 확률을 계산하는 함수를 작성해 봅시다.

  • 입력
ss: P(맑음|맑음)
sr: P(비|맑음)
rs: P(맑음|비)
rr: P(비|비)
  • 출력
오늘 날씨가 맑을 때 모레 날씨가 맑을 확률
  • 실행 결과
>>> prob_wether(0.4, 0.6, 0.7, 0.3)
0.5800000000000001
>>> prob_wether(0.1, 0.9, 0.5, 0.5)
0.46
>>> prob_wether(0.3, 0.7, 0.8, 0.2)
0.6499999999999999

오늘 날씨가 맑다고 가정하고 내일의 날씨가 맑을 확률과 흐릴 확률을 둘다 계산해보세요.

내일의 날씨에 따라 모레 날씨가 맑을 확률을 계산해보세요.

맑음->맑음->맑음일 확률과 맑음->흐림->맑음일 확률을 더하면 모레 맑을 확률이 됩니다.

def prob_wether(ss, sr, rs, rr):
    re = 0
    re += ss * ss
    re += sr * rs

    return re
    
print(prob_wether(0.4, 0.6, 0.7, 0.3))

독립시행 2

이 문제는 [실습5] 독립시행 문제의 업그레이드 버전입니다.

한 축구팀의 에이스가 선수가 총 n번의 슛을 했을때 t번 이상 슛을 성공할 확률을 계산하는 함수를 작성해 봅시다.

  • 입력
p: 슛이 성공할 확률 (0 ≤ p ≤ 1인 실수)  
n: 주어진 슛 기회 (자연수)  
t: 구하고자 하는 슛 성공 개수의 기준 (자연수)
  • 출력
슛 성공 확률이 p인 선수가 n 번의 슛 중 t번 이상 성공할 확률
  • 실행 결과
>>> shoot_prob2(0.5, 10, 3)
0.9453125
>>> shoot_prob2(0.3, 5, 1)
0.8319299999999998
>>> shoot_prob2(0.9, 10, 10)
0.3486784401000001
  • 이렇게 해보세요!

t번 이상 n번 이하에 속하는 모든 횟수에 대해 그 횟수만큼 성공할 확률을 각각 구해보세요. (t번, t+1번, …, n번)

구한 확률을 모두 더하여 t번 이상 n번 이하로 슛을 성공할 확률을 구해보세요.


# 1. factorial을 사용하기 위해 math 모듈을 불러와봅시다.
import math


def shoot_prob2(p, n, t):
    # 2. t번 이상 n번 이하로 반복되는 반복문을 작성해보세요.
    
    # 3. [실습5]와 동일하게 독립시행의 확률을 구하는 코드를 작성해보세요.
    #   3-1. 골을 r번 넣을 확률 pa = p ** r
    #   3-2 골을 넣지 못할 나머지 확률 pac = (1-p) ** (n-r)
    #   3-3. 골을 r번 넣을 모든 경우의 수 num = n! // (r! * (n-r)!)
    #   3-4. 한번의 시행의 성공 확률이 p일 때 n번 중 r번 성공할 확률 prob = pa * pac * num
    Sum = 0
    
    for r in range(t,n+1):
        pa = p ** r
        pac = (1-p) ** (n-r)
        num = math.factorial(n) // (math.factorial(r) * math.factorial(n-r))
        Sum +=(pa*pac*num)
        
    return Sum
    
print(shoot_prob2(0.5, 10, 3))

Leave a comment