15wk-2: 기말고사

Author

최규빈

Published

June 19, 2023

제출은 *.ipynb, *.html, *.pdf 파일로 제출할 것

imports

import numpy as np
import matplotlib.pyplot as plt
import datetime

1. 묵찌빠 (150점)

묵찌빠

ref: https://namu.wiki/w/묵찌빠

묵찌빠는 가위바위보의 변형 놀이이다. 보통 가위바위보의 게임과 연이어 진행되는데, 가위바위보 승부 이후 이긴 사람이 공격권을 가지고, 묵(바위)/찌(가위)/빠(보자기) 가운데 하나를 외치는 동시에 말한 것과 일치하도록 손 모양을 바꾼다. 공격권을 가진 사람의 손 모양이 상대(수비권을 가진 사람)의 손 모양과 일치하면 공격권을 가진 사람의 승리한다. 승부가 갈리지 않았을 경우에는 다시 가위바위보를 하는 것이 아니라, 현 상태에서 가위바위보 규칙 상 이긴 사람이 공격권을 가져가게 된다.

(1) 아래는 RPS_BASE 클래스의 구현예시이다.

class RPS_BASE:
    def __init__(self,candidate):
        self.candidate = candidate
        self.actions = list() 
    def __setitem__(self,index,val):
        self.actions[index] = val
    def __getitem__(self,item):
        return self.actions[item]        
    def __len__(self):
        return len(self.actions)
    def __eq__(self,other):
        return self[-1] == other[-1]
    def __gt__(self,other):
        return [self[-1],other[-1]] in [['묵','찌'],['찌','빠'],['빠','묵']]
    def __ge__(self,other):
        return (self == other) or (self > other)
    def __lt__(self,other):
        return not (self >= other)
    def __le__(self,other):
        return (self == other) or (self < other)    
    def _repr_html_(self):
        html_str = """
        낼 수 있는 패: {} <br/>
        기록: {}
        """        
        return html_str.format(self.candidate,self.actions)       
    def pick(self):
        self.actions.append(np.random.choice(self.candidate))        

이 클래스에서 아래와 같은 2명의 플레이어 인스턴스를 생성하라.

  • a: [‘묵’,‘찌’] 중에 하나를 랜덤으로 선택
  • b: [‘찌’,‘빠’] 중에 하나를 랜덤으로 선택

두 인스턴스를 100회 랜덤대결하고 결과를 기록하라. 어떠한 플레이어가 더 유리한가?

답안은 100회중 a몇회 승리, b몇회 승리와 같은 숫자형식으로만 나오면 인정한다. (코드를 정리하거나 별도의 클래스를 만드는 것을 요구하지 않음)


(2) RPS_BASE에서 아래와 같은 두명의 플레이어 인스턴스를 생성하라.

  • a: [‘묵’,‘찌’,‘빠’] 중 하나를 랜덤으로 선택
  • b: [‘찌’,‘빠’] 중 하나를 랜덤으로 선택

아래와 같은 a,b의 attribute을 변경하라.

a.modes = [None]
a.actions = [None] 
b.modes = [None]
b.actions = [None]

플레이어 a,b를 이용하여 반복적으로 가위바위보 대결을 수행하고 아래와 같이 대결결과에 따라 공격권을 결정하는 함수 mul을 만들어라.

경우1: 플레이어 a가 승리할 경우

  • a가 공격모드, b가 수비모드가 된다.
  • a.modes에는 ‘Attack’ 이 b.modes에는 Defence가 추가된다.
  • a.actionsb.actions는 각각의 플레이어가 선택한 패(묵,찌,빠)가 기록된다.

경우2: 플레이어 b가 승리할 경우

  • b가 공격모드, a가 수비모드가 된다.
  • b.modes에는 ‘Attack’ 이 a.modes에는 Defence가 추가된다.
  • a.actionsb.actions는 각각의 플레이어가 선택한 패(묵,찌,빠)가 기록된다.

경우3: 비길경우

  • 공격권의 변화는 없다.
  • a.modes,b.modes는 각각 이전의 값이 추가된다.
  • a.actionsb.actions는 각각의 플레이어가 선택한 패(묵,찌,빠)가 기록된다.

아래는 함수 mul을 사용한 예시이다.

시점1: 둘다 찌를 내어 공격권을 아무도 획득하지못함

mul(a,b)
a
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None, '찌']
b
낼 수 있는 패: ['찌', '빠']
기록: [None, '찌']
a.modes
[None, None]
b.modes
[None, None]

시점2: 이번에도 둘다 찌를 내어 아무도 공격권을 획득하지 못함

mul(a,b)
a
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None, '찌', '찌']
b
낼 수 있는 패: ['찌', '빠']
기록: [None, '찌', '찌']
a.modes
[None, None, None]
b.modes
[None, None, None]

시점3: 이번에는 a가 공격권을 획득 (묵>찌)

mul(a,b)
a
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None, '찌', '찌', '묵']
b
낼 수 있는 패: ['찌', '빠']
기록: [None, '찌', '찌', '찌']
a.modes
[None, None, None, 'Attack']
b.modes
[None, None, None, 'Defence']

시점4: 이번에는 b가 공격권을 획득 (찌>빠)

mul(a,b)
a
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None, '찌', '찌', '묵', '빠']
b
낼 수 있는 패: ['찌', '빠']
기록: [None, '찌', '찌', '찌', '찌']
a.modes
[None, None, None, 'Attack', 'Defence']
b.modes
[None, None, None, 'Defence', 'Attack']

(3) RPS_BASE를 상속받아 MookjjibbaPlayer라는 새로운 클래스를 정의하라. MookjjibbaPlayer 클래스에서 아래의 메소드를 새롭게 정의 혹은 재정의하여

  1. __init__
  2. _repr_html_
  3. __mul__
  4. reset

인스턴스가 아래와 같은 동작을 하도록 설계하라.

시점0: 생성예시 (__init__)

a=MookjjibbaPlayer(['묵','찌','빠'])
b=MookjjibbaPlayer(['묵','찌','빠'])
a.actions
[None]
a.modes
[None]
__init__ 의 동작
  1. 슈퍼클래스(RPS_BASE)의 __init__을 동작시킴
  2. MookjjibbaPlayer의 인스턴가 가지는 actions, modes 값을 [None] 으로 초기화

시점0: 출력예시(_repr_html_)

a
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None]
모드: [None]
b
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None]
모드: [None]
_repr_html_ 의 동작

위와 같이 “낼 수 있는 패”, “기록”, “모드” 가 함께 출력되도록 설계할 것

시점1: 대결 및 결과출력 (__mul__, _repr_html_)

a*b
a
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None, '묵']
모드: [None, None]
b
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None, '묵']
모드: [None, None]
__mul__ 의 동작
  1. 두 플레이어의 대결을 진행
  2. 결과를 보고 modes 의 값을 update

위의 상황은 두 플레이어 모두 “묵”을 내어 어느쪽도 공격권을 가지지 못한 상태를 의미

시점2: 대결 및 결과출력 (__mul__, _repr_html_)

a*b
a
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None, '묵', '묵']
모드: [None, None, 'Defence']
b
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None, '묵', '빠']
모드: [None, None, 'Attack']
__mul__ 의 동작
  1. 두 플레이어의 대결을 진행
  2. 결과를 보고 modes 의 값을 update

위의 상황은 a가 묵, b가 빠를 내어 b가 공격권을 획득한 상황을 의미

시점3: 대결결과의 초기화 (reset)

a
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None, '묵', '묵']
모드: [None, None, 'Defence']
a.reset()
a
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None]
모드: [None]
reset 의 동작
  1. 플레이어의 기록을 초기화
  2. 플레이어의 모드를 초기화

(4) (3) 에서 생성된 MookjjibbaPlayer의 두 개의 인스턴스 a,b를 입력으로 받고 최초공격권을 결정하는 함수 jumpball을 설계하라.

아래는 jumpball함수의 사용예시이다.

a
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None]
모드: [None]
b
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None]
모드: [None]
jumpball(a,b)
a
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None, '찌', '찌', '찌']
모드: [None, None, None, 'Defence']
b
낼 수 있는 패: ['묵', '찌', '빠']
기록: [None, '찌', '찌', '묵']
모드: [None, None, None, 'Attack']

hint: 아래의 코드를 관찰

a = [] 
b = [] 
a.append(np.random.choice([0,1,2]))
b.append(np.random.choice([0,1,2]))
while a==b: 
    a.append(np.random.choice([0,1,2]))
    b.append(np.random.choice([0,1,2]))
a
[0, 0, 0, 1]
b
[0, 0, 0, 0]

(5) 지금까지 코드를 바탕으로

  • a: [‘묵’,‘찌’] 중 하나를 랜덤으로 고르는 플레이어
  • b: [‘찌’,‘빠’] 중 하나를 랜덤으로 고르는 플레이어

를 설정하여 100회 가상대결을 진행하라. 100회 가상대결결과를 제시하라.

(참고) – 아래는 제가 구현한 예시입니다. 참고용일 뿐이며 이와 같은 방식으로 구현할 필요는 없습니다.

1. a,b 두명의 플레이어 생성

a=MookjjibbaPlayer(['묵','찌'])
b=MookjjibbaPlayer(['찌','빠'])
a
낼 수 있는 패: ['묵', '찌']
기록: [None]
모드: [None]
b
낼 수 있는 패: ['찌', '빠']
기록: [None]
모드: [None]

a,b 플레이어가 초기화

2. a,b 두명의 플레이어를 입력으로 하여 게임1을 생성후 1회 게임진행

game = PlayMookjjibba(a,b)
game.play()
a
낼 수 있는 패: ['묵', '찌']
기록: [None, '찌', '묵', '묵', '찌']
모드: [None, 'Attack', 'Defence', 'Defence', 'Defence']
b
낼 수 있는 패: ['찌', '빠']
기록: [None, '빠', '빠', '빠', '찌']
모드: [None, 'Defence', 'Attack', 'Attack', 'Attack']

a가 찌, b가 빠를 내어 최초 공격권을 a가 획득하였지만 이후 공격권을 상실한 상실함. 이후 4번째 가위바위보에서 b가 a가 동시에 찌를 내며 b의 승리로 마무리됨

3. game.records 에 b의 승리가 기록되어 있음

game.records 
['b']

4. 1회 대결기록을 삭제하고 또 다른 게임을 진행: 이번에는 a의 승리

game.reset_player_history()
a
낼 수 있는 패: ['묵', '찌']
기록: [None]
모드: [None]
b
낼 수 있는 패: ['찌', '빠']
기록: [None]
모드: [None]
game.play()
a
낼 수 있는 패: ['묵', '찌']
기록: [None, '찌', '찌', '찌']
모드: [None, None, 'Attack', 'Attack']
b
낼 수 있는 패: ['찌', '빠']
기록: [None, '찌', '빠', '찌']
모드: [None, None, 'Defence', 'Defence']

공격권은 2번만에 A가 획득, 1번의 공격을 통하여 마무리

5. 이번에는 a의 승리가 기록됨

game.records
['b', 'a']

2. 종합문항 (50점)

(1) LinearRegression 이라는 이름의 클래스를 만들고 아래의 기능을 넣어라.

__init__: “클래스 \(\to\) 인스턴스” 인 시점에 길이가 \(n\)인 numpy array \({\bf x}=(x_1,\dots,x_n)\), \({\bf y}=(y_1,\dots,y_n)\)을 입력으로 받아 내부에 저장한다.

fit: fit은 내부에 저장된 \({\bf x}\), \({\bf y}\)를 이용하여 \(\hat{\bf y}=(\hat{y}_1,\dots,\hat{y}_n)\)을 계산하는 역할을 한다. 계산은 아래의 수식을 이용한다. \[\hat{\bf y}= {\bf X}({\bf X}^T {\bf X})^{-1}{\bf X}^T {\bf y}, \quad {\bf X}=\begin{bmatrix} 1 & x_1 \\ 1 & x_2 \\ \dots \\ 1 & x_n \end{bmatrix}\]

plot: plot은 \((x_i,y_i)\)\((x_i,\hat{y}_i)\)를 시각화하는 역할을 한다.

아래의 자료를 LinearRegression의 입력으로 받고 시각화하는 분석을 수행하라.

x = np.linspace(0,1,100)
y = 2*x + np.random.normal(size=100)
plt.plot(x,y,'o')

(2) 앞면과 뒷면이 나올 확률이 각각 1/2인 동전을 생각하자. 하니와 규빈은 이 동전을 연속으로 던져서 아래와 같은 룰을 정하여 내기를 하였다.

  • 동전을 연속으로 반복하여 던진다. 최근 2회의 결과가 (뒷면,앞면) 이 나오면 하니의 승리
  • 동전을 연속으로 반복하여 던진다. 최근 2회의 결과가 (뒷면,뒷면) 이 나오면 규빈의 승리

이 내기는 하니가 유리한가? 규빈이 유리한가? 시뮬레이션을 통해 검증하라.

Note

hint: 똑같이 유리하다

(3) 앞면과 뒷면이 나올 확률이 각각 1/2인 동전을 생각하자. 하니와 규빈은 이 동전을 연속으로 던져서 아래와 같은 룰을 정하여 내기를 하였다.

  • 동전을 연속으로 반복하여 던진다. 최근 2회의 결과가 (앞면,뒷면) 이 나오면 하니의 승리
  • 동전을 연속으로 반복하여 던진다. 최근 2회의 결과가 (뒷면,뒷면) 이 나오면 규빈의 승리

이 내기는 하니가 유리한가? 규빈이 유리한가? 시뮬레이션을 통해 검증하라.

Note

hint: 이 내기는 하니가 유리하다.

(4) Time을 상속받아 Init 클래스를 만들고 __repr__을 조작하여 아래와 같이 인스턴스 생성시점을 출력하는 기능을 구현하라.

class Time:
    def time(self):
        return datetime.datetime.now().strftime('%y-%m-%d %X')
a = Init()
a
인스턴스생성시점: 23-06-16 10:27:19
b = Init()
a,b
(인스턴스생성시점: 23-06-16 10:27:19, 인스턴스생성시점: 23-06-16 10:27:28)

(5) tuple 클래스와 아래의 Check를 상속받아 아래와 같은 역할을 하는 새로운 Tuple 클래스를 만들라.

class Check:
    def ckeck(self):
        return [l for l in dir(self) if l[0]!='_']
tpl = Tuple('asdfassdfsasdf')
tpl # 값과 함께 사용가능한 메소드가 함께 출력 
('a', 's', 'd', 'f', 'a', 's', 's', 'd', 'f', 's', 'a', 's', 'd', 'f')

methods=['ckeck', 'count', 'freq', 'index']
tpl.freq()
{'d': 3, 'a': 3, 's': 5, 'f': 3}

(6) 아래와 같은 클래스를 고려하자.

class Init(object):
    def __init__(self,value):
        self.value = value
        
class Times2(Init):
    def __init__(self,value):
        super().__init__(value)
        self.value = self.value * 2
        
class Plus5(Init):
    def __init__(self,value):
        super().__init__(value)
        self.value = self.value + 5

Plus5Times2를 상속하여 적당한 클래스 Times2Plus5를 정의하고 생성과 동시에 \(x \to (x\times 2)+5\) 를 수행도록 하라.

사용예시

a=Times2Plus5(0)
a.value
5
a=Times2Plus5(1)
a.value
7
a=Times2Plus5(5)
a.value
15

(7) 아래의 함수가 있다고 하자.

def f(x): 
    return np.sin(x)

적당한 함수 derivate를 정의하여 함수를 입력으로 받으면 그 도함수를 출력으로 리턴하도록 하라. 아래의 코드를 이용하여 검증하라.

x = np.linspace(-6,6,100)
plt.plot(x,f(x),label=r'$f(x)=\sin(x)$')
plt.plot(x,(derivate(f))(x),label=r'$f\'(x)=\cos(x)$')
plt.legend()
<matplotlib.legend.Legend at 0x7f236da92ee0>

(8) Student 클래스를 생성지침 및 사용예시를 참고하여 설계하라.

생성지침

attributes

  • name: 이름을 저장하는 변수
  • age: 나이를 저장하는 변수
  • semester: 학기를 저장하는 변수

methods

  • __init__: name, age, semester 세 가지 매개변수를 입력받아 인스턴스의 attribute로 저장
  • __str__: 인스턴스의 정보(이름,나이,학기)를 문자열 형태로 반환

사용예시

# 사용 예시
boram = Student(name='김보람', age=20, semester=1)
print(boram)
이름: 김보람
나이: 20
학기: 1

(9) 8의 클래스를 상속받아 Student2 만들라. __add__ 재정의하여 Student2의 인스턴스가 아래와 같이 동작하도록 하라.

boram = Student2()
입학을 축하합니다. 당신의 나이는 20.0이고 현재 학기는 0학기 입니다.
boram + '등록'+ '휴학' + '등록' + '휴학'
boram
나이: 22.0
학기: 2

4학기가 지났으므로 나이는 22살이 된다. 4학기중 2학기만 등록하였으므로 현재는 2학기를 마친상태이다.

(10) 적당한 클래스를 선언하여 \(f(x)=x+{\tt const}\)를 수행하는 함수를 생성하도록 하라.

사용예시1

f = AddConstant(5) # f(x) = x+5 
f(10)
15

사용예시2

f = AddConstant(-3) # f(x) = x-3
f(10)
7