import numpy as np
import matplotlib.pyplot as plt
import datetime
15wk-2: 기말고사 풀이
제출은 *.ipynb
, *.html
, *.pdf
파일로 제출할 것
ipynb
파일형태제출을 권장함.
imports
1
. 묵찌빠 (150점)
묵찌빠는 가위바위보의 변형 놀이이다. 보통 가위바위보의 게임과 연이어 진행되는데, 가위바위보 승부 이후 이긴 사람이 공격권을 가지고, 묵(바위)/찌(가위)/빠(보자기) 가운데 하나를 외치는 동시에 말한 것과 일치하도록 손 모양을 바꾼다. 공격권을 가진 사람의 손 모양이 상대(수비권을 가진 사람)의 손 모양과 일치하면 공격권을 가진 사람의 승리한다. 승부가 갈리지 않았을 경우에는 다시 가위바위보를 하는 것이 아니라, 현 상태에서 가위바위보 규칙 상 이긴 사람이 공격권을 가져가게 된다.
(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몇회 승리와 같은 숫자형식으로만 나오면 인정한다. (코드를 정리하거나 별도의 클래스를 만드는 것을 요구하지 않음)
(풀이)
= RPS_BASE(['묵','찌'])
a = RPS_BASE(['찌','빠']) b
= []
rslt for _ in range(100):
a.pick()
b.pick()if a>b:
'a')
rslt.append(elif a==b:
'-')
rslt.append(else:
'b') rslt.append(
'a의승리':rslt.count('a'),'무승부':rslt.count('-'),'b의승리':rslt.count('b')} {
{'a의승리': 53, '무승부': 21, 'b의승리': 26}
(2)
RPS_BASE에서 아래와 같은 두명의 플레이어 인스턴스를 생성하라.
a
: [‘묵’,‘찌’,‘빠’] 중 하나를 랜덤으로 선택b
: [‘찌’,‘빠’] 중 하나를 랜덤으로 선택
아래와 같은 a,b의 attribute을 변경하라.
= [None]
a.modes = [None]
a.actions = [None]
b.modes = [None] b.actions
플레이어 a,b를 이용하여 반복적으로 가위바위보 대결을 수행하고 아래와 같이 대결결과에 따라 공격권을 결정하는 함수 mul
을 만들어라.
경우1: 플레이어 a가 승리할 경우
- a가 공격모드, b가 수비모드가 된다.
a.modes
에는 ‘Attack’ 이b.modes
에는Defence
가 추가된다.a.actions
와b.actions
는 각각의 플레이어가 선택한 패(묵,찌,빠)가 기록된다.
경우2: 플레이어 b가 승리할 경우
- b가 공격모드, a가 수비모드가 된다.
b.modes
에는 ‘Attack’ 이a.modes
에는Defence
가 추가된다.a.actions
와b.actions
는 각각의 플레이어가 선택한 패(묵,찌,빠)가 기록된다.
경우3: 비길경우
- 공격권의 변화는 없다.
a.modes
,b.modes
는 각각 이전의 값이 추가된다.a.actions
와b.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)번 풀이로 대체
(3)
RPS_BASE를 상속받아 MookjjibbaPlayer라는 새로운 클래스를 정의하라. MookjjibbaPlayer 클래스에서 아래의 메소드를 새롭게 정의 혹은 재정의하여
__init__
_repr_html_
__mul__
reset
인스턴스가 아래와 같은 동작을 하도록 설계하라.
시점0: 생성예시 (__init__
)
=MookjjibbaPlayer(['묵','찌','빠'])
a=MookjjibbaPlayer(['묵','찌','빠']) b
a.actions
[None]
a.modes
[None]
__init__
의 동작
- 슈퍼클래스(RPS_BASE)의
__init__
을 동작시킴 - MookjjibbaPlayer의 인스턴가 가지는 actions, modes 값을 [None] 으로 초기화
시점0: 출력예시(_repr_html_
)
a
기록: [None]
모드: [None]
b
기록: [None]
모드: [None]
_repr_html_
의 동작
위와 같이 “낼 수 있는 패”, “기록”, “모드” 가 함께 출력되도록 설계할 것
시점1: 대결 및 결과출력 (__mul__
, _repr_html_
)
*b a
a
기록: [None, '묵']
모드: [None, None]
b
기록: [None, '묵']
모드: [None, None]
__mul__
의 동작
- 두 플레이어의 대결을 진행
- 결과를 보고 modes 의 값을 update
위의 상황은 두 플레이어 모두 “묵”을 내어 어느쪽도 공격권을 가지지 못한 상태를 의미
시점2: 대결 및 결과출력 (__mul__
, _repr_html_
)
*b a
a
기록: [None, '묵', '묵']
모드: [None, None, 'Defence']
b
기록: [None, '묵', '빠']
모드: [None, None, 'Attack']
__mul__
의 동작
- 두 플레이어의 대결을 진행
- 결과를 보고 modes 의 값을 update
위의 상황은 a가 묵, b가 빠를 내어 b가 공격권을 획득한 상황을 의미
시점3: 대결결과의 초기화 (reset
)
a
기록: [None, '묵', '묵']
모드: [None, None, 'Defence']
a.reset()
a
기록: [None]
모드: [None]
reset
의 동작
- 플레이어의 기록을 초기화
- 플레이어의 모드를 초기화
(풀이)
class MookjjibbaPlayer(RPS_BASE):
def __init__(self,candidate):
super().__init__(candidate)
self.modes = [None]
self.actions = [None]
def __mul__(self,other):
self.pick()
other.pick()if self > other:
self.modes.append('Attack')
'Defence')
other.modes.append(elif self < other:
self.modes.append('Defence')
'Attack')
other.modes.append(else:
self.modes.append(self.modes[-1])
-1])
other.modes.append(other.modes[def _repr_html_(self):
= """
html_str 낼 수 있는 패: {} <br/>
기록: {} <br/>
모드: {} <br/>
"""
return html_str.format(self.candidate,self.actions,self.modes)
def reset(self):
self.__init__(self.candidate)
__init__
, _repr_html_
체크
=MookjjibbaPlayer(['묵','찌','빠'])
a=MookjjibbaPlayer(['묵','찌','빠']) b
a
기록: [None]
모드: [None]
b
기록: [None]
모드: [None]
__mul__
, _repr_html_
, reset
체크
for _ in range(3):
*b a
a
기록: [None, '묵', '찌', '찌']
모드: [None, 'Attack', 'Attack', 'Defence']
b
기록: [None, '찌', '찌', '묵']
모드: [None, 'Defence', 'Defence', 'Attack']
a.reset()
a
기록: [None]
모드: [None]
(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']
처음 2번의 가위,바위,보는 비겼으나 이후에 b가 승리하여 초기공격권을 b가 획득하였음.
(풀이)
– 생략, (5)번풀이 참고
(5)
지금까지 코드를 바탕으로
- a: [‘묵’,‘찌’] 중 하나를 랜덤으로 고르는 플레이어
- b: [‘찌’,‘빠’] 중 하나를 랜덤으로 고르는 플레이어
를 설정하여 100회 가상대결을 진행하라. 100회 가상대결결과를 제시하라.
(참고) – 아래는 제가 구현한 예시입니다. 참고용일 뿐이며 이와 같은 방식으로 구현할 필요는 없습니다.
1. a,b 두명의 플레이어 생성
=MookjjibbaPlayer(['묵','찌'])
a=MookjjibbaPlayer(['찌','빠']) b
a
기록: [None]
모드: [None]
b
기록: [None]
모드: [None]
a,b 플레이어가 초기화
2. a,b 두명의 플레이어를 입력으로 하여 게임1을 생성후 1회 게임진행
= PlayMookjjibba(a,b) game
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']
(풀이)
class PlayMookjjibba:
def __init__(self,a,b):
self.a = a
self.b = b
self.records = list()
def reset_player_history(self):
self.a.reset()
self.b.reset()
def record(self):
if self.a.modes[-1] == 'Attack':
self.records.append('a')
else:
self.records.append('b')
def jumpball(self):
self.a * self.b
while self.a == self.b:
self.a * self.b
def play(self):
self.jumpball()
while self.a != self.b:
self.a * self.b
self.record()
=MookjjibbaPlayer(['묵','찌'])
a=MookjjibbaPlayer(['찌','빠'])
b= PlayMookjjibba(a,b) game
for _ in range(1000):
game.play()
'a':game.records.count('a'),'b':game.records.count('b')} {
{'a': 678, 'b': 322}
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의 입력으로 받고 시각화하는 분석을 수행하라.
= np.linspace(0,1,100)
x = 2*x + np.random.normal(size=100)
y 'o') plt.plot(x,y,
(풀이)
class LinearRegression:
def __init__(self,x,y):
self.x = x
self.y = y
def fit(self):
= len(self.x)
n self.X = np.stack([np.ones(n),self.x],axis=1)
self.yhat = self.X@np.linalg.inv(self.X.T@self.X)@self.X.T@self.y
def plot(self):
self.x,self.y,'o',label=r'$(x_i,y_i)$')
plt.plot(self.x,self.yhat,'--',label=r'$(x_i,\hat{y}_i)$')
plt.plot( plt.legend()
= LinearRegression(x,y)
linreg
linreg.fit() linreg.plot()
5월24일 12wk-2 숙제참고
(2)
앞면과 뒷면이 나올 확률이 각각 1/2인 동전을 생각하자. 하니와 규빈은 이 동전을 연속으로 던져서 아래와 같은 룰을 정하여 내기를 하였다.
- 동전을 연속으로 반복하여 던진다. 최근 2회의 결과가 (뒷면,앞면) 이 나오면 하니의 승리
- 동전을 연속으로 반복하여 던진다. 최근 2회의 결과가 (뒷면,뒷면) 이 나오면 규빈의 승리
이 내기는 하니가 유리한가? 규빈이 유리한가? 시뮬레이션을 통해 검증하라.
hint: 똑같이 유리하다
(풀이)
Step1 CoinFlipper 설계
class CoinFlipper:
def __init__(self):
self.coins = list()
self.win_cond = {'하니':[],'규빈':[]}
self.winner = str()
def __iter__(self):
return self
def __next__(self):
self.coins.append(np.random.choice(['앞면','뒷면']))
if self.coins[-2:] == self.win_cond['하니']:
self.winner = '하니'
raise StopIteration
elif self.coins[-2:] == self.win_cond['규빈']:
self.winner = '규빈'
raise StopIteration
else:
pass
def __call__(self,win_cond):
self.win_cond = win_cond
for _ in self:
pass
def __repr__(self):
= '하니의 승리조건: {}\n규빈의 승리조건: {}\n동전을 던진결과들: {}\n최종승리자: {}'.format(
repr_str self.win_cond['하니'],
self.win_cond['규빈'],
self.coins,
self.winner
)return repr_str
= CoinFlipper() coin_fliper
coin_fliper
하니의 승리조건: []
규빈의 승리조건: []
동전을 던진결과들: []
최종승리자:
= {
win_cond '하니':['앞면','뒷면'],
'규빈':['뒷면','앞면']
} coin_fliper(win_cond)
coin_fliper
하니의 승리조건: ['앞면', '뒷면']
규빈의 승리조건: ['뒷면', '앞면']
동전을 던진결과들: ['뒷면', '뒷면', '뒷면', '뒷면', '앞면']
최종승리자: 규빈
Step2: 100회 대결진행
= [CoinFlipper() for _ in range(100)]
coinfliper_lst = []
winners for coinfliper in coinfliper_lst:
coinfliper(win_cond) winners.append(coinfliper.winner)
'하니':winners.count('하니'), '규빈':winners.count('규빈')} {
{'하니': 51, '규빈': 49}
참고로 \(n\)회 대결결과는 아래와 같이 조회가능
3] coinfliper_lst[
하니의 승리조건: ['앞면', '뒷면']
규빈의 승리조건: ['뒷면', '앞면']
동전을 던진결과들: ['앞면', '앞면', '뒷면']
최종승리자: 하니
(3)
앞면과 뒷면이 나올 확률이 각각 1/2인 동전을 생각하자. 하니와 규빈은 이 동전을 연속으로 던져서 아래와 같은 룰을 정하여 내기를 하였다.
- 동전을 연속으로 반복하여 던진다. 최근 2회의 결과가 (앞면,뒷면) 이 나오면 하니의 승리
- 동전을 연속으로 반복하여 던진다. 최근 2회의 결과가 (뒷면,뒷면) 이 나오면 규빈의 승리
이 내기는 하니가 유리한가? 규빈이 유리한가? 시뮬레이션을 통해 검증하라.
hint: 이 내기는 하니가 유리하다.
(풀이)
= {
win_cond '하니':['앞면','뒷면'],
'규빈':['뒷면','뒷면']
} coin_fliper(win_cond)
= [CoinFlipper() for _ in range(100)]
coinfliper_lst = []
winners for coinfliper in coinfliper_lst:
coinfliper(win_cond) winners.append(coinfliper.winner)
'하니':winners.count('하니'), '규빈':winners.count('규빈')} {
{'하니': 73, '규빈': 27}
(4)
Time을 상속받아 Init 클래스를 만들고 __repr__
을 조작하여 아래와 같이 인스턴스 생성시점을 출력하는 기능을 구현하라.
class Time:
def time(self):
return datetime.datetime.now().strftime('%y-%m-%d %X')
(풀이)
class Init(Time):
def __init__(self):
self.init_time = self.time()
def __repr__(self):
return '인스턴스생성시점: {}'.format(self.init_time)
= Init() a
a
인스턴스생성시점: 23-06-20 14:05:58
= Init() b
a,b
(인스턴스생성시점: 23-06-20 14:05:58, 인스턴스생성시점: 23-06-20 14:05:59)
(5)
tuple 클래스와 아래의 Check를 상속받아 아래와 같은 역할을 하는 새로운 Tuple 클래스를 만들라.
class Check:
def ckeck(self):
return [l for l in dir(self) if l[0]!='_']
(풀이)
class Tuple(Check,tuple):
def freq(self):
return {s:self.count(s) for s in set(self)}
def __repr__(self):
return super().__repr__() + '\n' +'methods={}'.format(self.ckeck())
= Tuple('asdfassdfsasdf')
tpl # 값과 함께 사용가능한 메소드가 함께 출력 tpl
('a', 's', 'd', 'f', 'a', 's', 's', 'd', 'f', 's', 'a', 's', 'd', 'f')
methods=['ckeck', 'count', 'freq', 'index']
tpl.freq()
{'f': 3, 'd': 3, 's': 5, 'a': 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
Plus5
와 Times2
를 상속하여 적당한 클래스 Times2Plus5
를 정의하고 생성과 동시에 \(x \to (x\times 2)+5\) 를 수행도록 하라.
(풀이)
class Times2Plus5(Plus5,Times2):
def __init__(self,value):
super().__init__(value)
사용예시
=Times2Plus5(0)
a a.value
5
=Times2Plus5(1)
a a.value
7
=Times2Plus5(5)
a a.value
15
(7)
아래의 함수가 있다고 하자.
def f(x):
return np.sin(x)
적당한 함수 derivate
를 정의하여 함수를 입력으로 받으면 그 도함수를 출력으로 리턴하도록 하라. 아래의 코드를 이용하여 검증하라.
= np.linspace(-6,6,100)
x =r'$f(x)=\sin(x)$')
plt.plot(x,f(x),label=r'$f\'(x)=\cos(x)$')
plt.plot(x,(derivate(f))(x),label plt.legend()
<matplotlib.legend.Legend at 0x7effa80130a0>
(풀이)
def f(x):
return np.sin(x)
def derivate(f):
# step1: 함수오브젝트 f는 입력으로 받은상태
# step2: 함수오브젝트 f를 이용하여 df라는 함수를 정의
def df(x):
=0.000000000001
hreturn (f(x+h)-f(x))/h
# step3: 정의된 df를 리턴
return df
= np.linspace(-6,6,100)
x =r'$f(x)=\sin(x)$')
plt.plot(x,f(x),label=r'$f\'(x)=\cos(x)$')
plt.plot(x,(derivate(f))(x),label plt.legend()
<matplotlib.legend.Legend at 0x7effa7ec2f10>
(8)
Student 클래스를 생성지침 및 사용예시를 참고하여 설계하라.
생성지침
attributes
name
: 이름을 저장하는 변수age
: 나이를 저장하는 변수semester
: 학기를 저장하는 변수
methods
__init__
: name, age, semester 세 가지 매개변수를 입력받아 인스턴스의 attribute로 저장__str__
: 인스턴스의 정보(이름,나이,학기)를 문자열 형태로 반환
사용예시
# 사용 예시
= Student(name='김보람', age=20, semester=1)
boram print(boram)
이름: 김보람
나이: 20
학기: 1
(풀이)
class Student:
def __init__(self,name='김보람',age=20,semester=0):
self.name = name
self.age = age
self.semester = semester
def __str__(self):
= '''이름: {}\n나이: {}\n학기: {}'''.format(self.name,self.age,self.semester)
print_str return print_str
# 사용 예시
= Student(name='김보람', age=20, semester=1)
boram print(boram)
이름: 김보람
나이: 20
학기: 1
(9)
8의 클래스를 상속받아 Student2 만들라. __add__
재정의하여 Student2의 인스턴스가 아래와 같이 동작하도록 하라.
= Student2() boram
입학을 축하합니다. 당신의 나이는 20이고 현재 학기는 0학기 입니다.
+ '등록'+ '휴학' + '등록' + '휴학'
boram boram
학기: 2
4학기가 지났으므로 나이는 22살이 된다. 4학기중 2학기만 등록하였으므로 현재는 2학기를 마친상태이다.
(풀이)
class Student2(Student):
def __init__(self,name='김보람',age=20,semester=0):
super().__init__()
print("입학을 축하합니다. 당신의 나이는 {}이고 현재 학기는 {}학기 입니다.".format(self.age,self.semester))
def __add__(self,registration_status):
if registration_status=='휴학':
self.age=self.age+0.5
elif registration_status=='등록':
self.age=self.age+0.5
self.semester= self.semester+1
return self
def _repr_html_(self):
= """
html_str 나이: {} <br/>
학기: {} <br/>
"""
return html_str.format(self.age,self.semester)
= Student2() boram
입학을 축하합니다. 당신의 나이는 20이고 현재 학기는 0학기 입니다.
+ '등록'+ '휴학' + '등록' + '휴학'
boram boram
학기: 2
(10)
적당한 클래스를 선언하여 \(f(x)=x+{\tt const}\)를 수행하는 함수를 생성하도록 하라.
사용예시1
= AddConstant(5) # f(x) = x+5 f
10) f(
15
사용예시2
= AddConstant(-3) # f(x) = x-3 f
10) f(
7
(풀이)
class AddConstant:
def __init__(self,const):
self.const = const
def __call__(self,x):
return x + self.const
= AddConstant(5) # f(x) = x+5
f 10) f(
15
= AddConstant(-3) # f(x) = x-3
f 10) f(
7