python; PI(파이) 근사값 구하기 (몬테카를로)

    몬테카를로 시뮬레이션은 반복된 무작위 추출을 이용하여 함수의 값을 수리적으로 근사하는 알고리즘이다.

    이를 통해 우리는 파이의 값을 추론할 수 있다.

     

    한 변의 길이가 2인 정사각형 안에 내접하는 반지름이 1인 원이 있다고 생각해 보자.

    그러면 원의 넓이는 pi가 될 것이고, 정사각형의 넓이는 4가 될 것이다.

    원의 넓이 : 정사각형 넓이 = pi : 4 이므로, 따라서 pi=(원의 넓이)/(정사각형 넓이) *4이다.

     

    점을 많이 찍을수록 정사각형과 원 안에 점이 많이 찍힐 것이므로 자연스레 근삿값도 정확해질 것이다.

     

     

    이때 시각적으로 보기 위해 터틀그래픽을 이용한다.

    기본 준비: 정사각형과 원, x축과 y축을 그린다.

    def t_square(x, y, l): #(x,y)를 중점으로 하는 한 변의 길이가 l인 정사각형 그리는 함수
        t.pu()
        t.goto(x - l / 2, y - l / 2)
        t.pd()
        for _ in range(4):
            t.forward(l)
            t.left(90)
    
    def t_circle(x, y, r): #(x,y)를 중점으로하는 반지름의 길이가 r인 원 그리는 함수
        t.pu()
        t.goto(x, y - r)
        t.pd()
        t.circle(r)
    
    def t_cross(x, y, l): #(x,y)를 중점으로 하는 한 선분의 길이가 l인 십자 그리는 함수
        t.pu()
        t.goto(x - l / 2, y)
        t.pd()
        t.forward(l)
        t.pu()
        t.goto(x, y + l / 2)
        t.right(90)
        t.pd()
        t.forward(l)
        t.left(90)

     

    원안에 찍은 점은 파란색 원의 바깥쪽에 찍은 점은 빨간색으로 나타낸다.

    이때 원 안에 찍히는지 판별하는 방법은 여러 가지가 있겠지만 우리가 알고 있는 x좌표와 y좌표로 가장 간단히 구할 수 있는 방법은 원의 방정식이다.

    (0,0)을 중점으로 하는 원의 방정식은 x^2 + y^2 = r^2이다. 이는 원의 중점으로부터 반지름만큼 떨어진 거리의 점을 모두 나타낸 것이다. 그렇다면 원 안쪽에 있는 점을 식으로 나타내면 x^2 + y^2 <= r^2이다.

    x=0
    y=0
    
    r=100 #원의 반지름
    l=200 #정사각형 한 변의 길이
    
    t_square(0,0,l)
    t_circle(0,0,r)
    t_cross(0,0,l)
    
    red_dots=0 #빨간색 점의 갯수
    blue_dots=0 #파란색 점의 갯수
    
    for _ in range(10000): #시행 횟수가 많아질수록 값이 정확해짐.
        randx = random.randint(-r, r)
        randy = random.randint(-r, r)
        t.pu()
        t.goto(randx, randy)
        t.pd()
    
        if(math.sqrt(randx**2 + randy**2) <= r): #원의 방정식
            t.dot(3,'blue')
            blue_dots+=1
        else:
            t.dot(3,'red')
            red_dots+=1

     

     

    파란 점의 개수와 (파란 점 개수)+(빨간 점 개수)의 비는 원의 넓이와 정사각형의 넓이의 비와 근사하다.

    따라서 우리는 pi근사값을 구할 수 있다.

    estimated_pi = (blue_dots / (blue_dots+red_dots)) * 4
    print(f"pi 근사값: {estimated_pi}")

     

     

     

    전체코드:

    import math
    import random
    import turtle
    
    t = turtle.Turtle()
    t.shape('turtle')
    
    
    
    def t_square(x, y, l):
        t.pu()
        t.goto(x - l / 2, y - l / 2)
        t.pd()
        for _ in range(4):
            t.forward(l)
            t.left(90)
    
    def t_circle(x, y, r):
        t.pu()
        t.goto(x, y - r)
        t.pd()
        t.circle(r)
    
    def t_cross(x, y, l):
        t.pu()
        t.goto(x - l / 2, y)
        t.pd()
        t.forward(l)
        t.pu()
        t.goto(x, y + l / 2)
        t.right(90)
        t.pd()
        t.forward(l)
        t.left(90)
    
    x=0
    y=0
    
    r=100
    l=200
    
    t_square(0,0,l)
    t_circle(0,0,r)
    t_cross(0,0,l)
    
    red_dots=0 #빨간색 점의 갯수
    blue_dots=0 #파란색 점의 갯수
    
    for _ in range(10000): #시행 횟수가 많아질수록 값이 정확해짐.
        randx = random.randint(-r, r)
        randy = random.randint(-r, r)
        t.pu()
        t.goto(randx, randy)
        t.pd()
    
        if(math.sqrt(randx**2 + randy**2) <= r): #원의 방정식
            t.dot(3,'blue')
            blue_dots+=1
        else:
            t.dot(3,'red')
            red_dots+=1
    
    estimated_pi = (blue_dots / (blue_dots+red_dots)) * 4
    print(f"pi 근사값: {estimated_pi}")

     

    댓글