한가지 확실한 건 이건 진짜 개노가다가 맞음. 


블로그를 좀 오래 전부터 보신 분들은 아시겠지만 깃헙에 유일하게 폴더로 분리된 대형 프로젝트가 두 개 있다. 첫번째가 프로젝트 제한효소, 두번째가 프로젝트 워드클라우드. 근데 이게 쌩코드가 올라와서 이걸 복사해서 수정해서 또 올려야되는데 귀찮아요... 그래서 Flask로 웹이랑 묶으려고 생각중이다. (프로젝트 제한효소는 DB로 쓰는 csv파일도 포함)

근데 또 고려할 게 생겼어...OTL 

1) 코드 통합 문제
프로젝트 워드클라우드의 경우 한글/영어/Entrez 세 개의 코드가 있고, 프로젝트 제한효소에는 커터/파인더/서쳐 세 개의 코드가 있다. 프로젝트 제한효소는 세 코드가 사용 목적이 다 달라서 통합은 안되고, 한 페이지 안에서 세개를 다 띄우려면 아코디언 패널이나 탭 메뉴가 있어야 한다. 워드클라우드도 Entrez는 입력 인자가 달라서 분리해야 하고, 한글과 영어는 언어에 따른 처리 인자만 달라서 코드를 통합하고 언어 선택을 라디오버튼으로 줄 수 있다. (페이지를 합칠거면 역시나 탭이나 아코디언 패널이 필요하다)

사실 이 부분은 별로 고민을 안한게 우리에겐 개쩌는 부트스트랩이 있다. 

2) 파일 브라우징 문제
내가 저걸 돌리는 OS가 리눅스라 이미지 브라우저나 글꼴 폴더를 다 고정해놓고 쓰고 있는데, 일단 이게 왜 문제냐면, 코드를 돌리는 OS가 리눅스라 저장 경로가 리눅스 위주이다. (윈도우는 홈이 없음) 리눅스는 글꼴도 저장해두는 폴더 가서 ttf나 otf 선택해서 쓰면 된다. 그럼 윈도우는요? 윈도우는 글꼴 어떻게 고르는지 모른다. 

프로젝트 제한효소도 FASTA, Genbank파일때문에 브라우저를 띄워야 하고, 워드클라우드는 마스킹 이미지때문에 브라우저를 띄워야 한다. OS 정보를 가져올 수 있으면 거기에 맞춰서 home이건 C드라이브건 띄우면 장땡. 저장은 브라우저 설정 따라가게 하면 된다. 

3) 입력 인자
여기서부터는 프로젝트 워드클라우드의 문제. (제한효소는 저 두개만 처리하면 일단 끝난다) 

Entrez는 Pubmed에서 논문 검색할 때 쓰는 검색어(A[TITLE] and B[TITLE])를 입력받기 때문에 그 부분에 대한 안내만 하면 된다. 문제는 그냥 워드클라우드인데, 이쪽은 현재 While True로 여러개 입력을 받고 있다. (하나만 써도 되긴 된다) IDE에서는 텍스트 편집이 어려워서 이렇게 했다. 

현재 가장 좋은 방법은 textarea를 크게 주고 하나만 입력받는 걸로 제한하는 것. While True 걸어놓고 거기에 대해 안내하는것보단 이게 나을 듯 하다. 샘플 텍스트로 한글 워드클라우드는 윤동주-별 헤는 밤을 제공할 예정. 그게 제일 이쁘게 나왔다. 

4) 글꼴
현재 워드클라우드 코드는 내가 보유한 글꼴을 선택해서 적용하고 있다. 그런데 다른 사람들이 내 글꼴을 쓸 수는 없고... 구글 웹폰트나 텍스트 에디터의 글꼴 선택 메뉴같은 게 있으면 좋을 것 같은데 그걸 어떻게 하는지를 몰라서, 이쪽으로는 더 알아봐야 할 듯 하다. 

예에에에에전에 백준 18108번 풀이를 올렸을 때 댓글로 질문이 하나 왔었다. 

input()대신 sys.stdin.realine()을 쓰는 이유를 알 수 있나요?

백준에서 빠르다고 해서 쓰고 있었음... 아니 농담 아니고 진짜다. 반복문 이런거 잘못 짜면 응애 나 애기시간초과! 가 반기는데 다른데서라도 시간 줄여야져... 


일단 둘 다 입력 받을 때 쓰는 게 맞다. 

input()
#input

import sys
sys.stdin.readline()
#sys.stdin.readline()

각각 이렇게 쓰면 된다. 근데 이렇게만 해 두면 왜 쟤가 빠른지 체감이 잘 안 올 것이다. 

 

우리가 input()을 쓸 때는 저렇게 쓰는 것 보단 

input('메시지를 입력해주세요')

이런 식으로 쓴다. 이 때, 저 안에 있는 텍스트를 '프롬프트 메시지'라고 한다. 그럼 sys.stdin.readline()도 프롬프트 메시지가 있나요? 놉. 이게 sys.stdin.readline()이 input()보다 빠른 이유 중 하나다. 엥? 이유가 또 있어요? 예. 

파이썬에서는 입력을 받을 때 개행 문자(\n)가 같이 딸려온다. input()은 개행 문자를 떼버리는데 sys.stdin.readline()은 개행문자를 안 떼고 그냥 받는다. 그래서 sys.stdin.readline()으로 받고 나면 항상 rstrip()이나 strip() 메소드가 따라와야 한다. 

a = sys.stdin.readline().rstrip() 
a = sys.stdin.readline().strip()

그러니까 이렇게 써야 개행 문자를 떼고 입력을 받는다. 정확히 말하자면 rstrip()은 개행문자를 떼 주고, strip()은 공백을 떼버린다.

 

즉, sys.stdin.readline()이 빠른 이유는

1. 개행문자를 안 떼고

2. 프롬프트 메시지도 안 받기 때문

이라고 할 수 있다.

참고로 일부 IDE나 에디터에서 sys.stdin.readline()은 안먹으니 시간이 정말 급한거다 잘못하면 응애 나 애기시간초과! 가 반긴다 이런거 아님 걍 인풋 쓰자. 대표적인 예로 Jupyter에서는 sys.stdin.readline()이 입력을 안 받고(그냥 안받음) Spyder에서는 형변환 에러가 뜬다. 그냥 런던올림픽 펜싱심판 데려오면 안되나 아니 그냥 파이참을 써 파이참은 안해봤는데 VScode는 sys.stdin.readline()이 먹힌다.

JSON은 JavaScript Object Notation을 줄인 것으로, 보통 제이슨으로 읽는다. 일반적으로 클라이언트가 서버에서 가져오는 데이터는 이 형식이고, 현재 표준 형식으로 많이 쓰이고 있다. 원류는 자바스크립트지만 현재는 언어 독립적인 개별 포맷. 그러니까 누가 제이슨 파일 주세요 하면 JSON파일 달라는거지 제이슨씨 파일 주세요가 아니다 남의 파일을 왜 달라고 해

키-값 쌍으로 이루어진 데이터를 텍스트 형태로 기록해둔 것이라 휴먼 가독성은 좋다.


오늘의 도우미

{
	"Water": {
		"molecular weight": "18.016",
		"molecular formula": "H2O"
	},
	"Ethanol": {
		"molecular weight": "46.07",
		"molecular formula": "C2H5OH"
	},
	"Glucose": {
		"molecular weight": "180.156",
		"molecular formula": "C6H12O6"
	},
	"Sucrose": {
		"molecular weight": "342.3",
		"molecular formula": "C12H22O11"
	}
}

짜잔 이래뵈도 수제다 

그리고 이거 하려면 모듈 불러야 한다. 별도로 설치는 안해도 되고 

import json

이거 한줄만 추가합시다. 

 

JSON파일 읽기

with open('/home/koreanraichu/test.json') as f:
    json_data = json.load(f)
print(json.dumps(json_data))

그냥 이렇게 쓰면 알아서 불러온다. 

 

{"Water": {"molecular weight": "18.016", "molecular formula": "H2O"}, "Ethanol": {"molecular weight": "46.07", "molecular formula": "C2H5OH"}, "Glucose": {"molecular weight": "180.156", "molecular formula": "C6H12O6"}, "Sucrose": {"molecular weight": "342.3", "molecular formula": "C12H22O11"}}

형식에서 그렇게 큰 기대는 하지 말자. (참고로 VScode가 가끔 키보드 먹통이 되는 문제가 있어서 spyder로 함...)

그럼 저기서 특정 데이터만 볼 수 있어요? 예, 됩니다. 

{"Water": {"molecular weight": "18.016", "molecular formula": "H2O"}

형식이 잘 보면 딕셔너리다. 파이썬은 JSON파일을 불러올 때 딕셔너리로 불러오는데, 이 파일의 경우 다중 딕셔너리(2중)이다. 그리고 딕셔너리는 뭐다? 키-밸류로 되어 있고 키값으로 픽이 된다 그죠? 

 

print(json_data['Water'])
print(json_data['Water']['molecular formula'])

걍 이렇게 부르면 된다. 정렬도 가능하다. 

 

JSON파일 수정하기

{"Water": {"molecular weight": "18.016", "molecular formula": "H2O"}, "Ethanol": {"molecular weight": "46.07", "molecular formula": "C2H5OH"}, "Glucose": {"molecular weight": "180.156", "molecular formula": "C6H12O6"}, "Sucrose": {"molecular weight": "342.3", "molecular formula": "C12H22O11"}, "Methanol": {"molecular weight": "", "molecular formula": ""}}

여기에 메탄올에 대한 정보를 새로 추가했는데, 문제가 하나 있다. 

"Methanol": {"molecular weight": "", "molecular formula": ""}

뭐야 메탄올 정보 어디갔어요? 

이거 그럼 raw file 수정해야 하나요? ㄴㄴ 파이썬에서 커버칠 수 있다. 

json_data['Methanol']['molecular weight'] = '32.04'
json_data['Methanol']['molecular formula'] = 'CH3OH'

이런 식으로 키값을 픽해서 밸류를 수정하고 

with open ('/home/koreanraichu/test.json','w', encoding='utf-8') as s:
    json.dump(json_data,s)

이렇게 하면 들여쓰기가 가출한다. 

 

with open ('/home/koreanraichu/test.json','w', encoding='utf-8') as s:
    json.dump(json_data,s,indent = '\t')

indent = \t를 주자. 

 

JSON파일 쓰기

JSON파일을 읽고 수정해봤으면 이제 쓸 시간이다. 아니 거기 에디터 꺼요... 

 

cellphone = dict()
cellphone['Samsung'] = "Galaxy Z flip"
cellphone['Apple'] = "iPhone"
tabletpc = dict()
tabletpc['Samsung'] = 'Galaxy tab'
tabletpc['Apple'] = 'iPad'

핸드폰과 태블릿PC에 대한 딕셔너리를 생성해준다. (핸드폰 영어로 cellphone임)

 

mobile_device = dict()
mobile_device['Cellphone'] = cellphone
mobile_device['Tablet PC'] = tabletpc

그리고 두 개의 딕셔너리를 밸류로 갖는 큰 딕셔너리(모바일 기기)를 하나 만든다. 

 

with open ('/home/koreanraichu/test2.json','w', encoding='utf-8') as s:
    json.dump(mobile_device,s,indent = '\t')

그리고 묶은 딕셔너리를 그대로 JSON파일로 쓰면 이렇게 된다. 참 쉽죠? 

재귀함수는 정의할 때 자기자신을 재참조한다고 했는데, 정의하는데 자기자신이 들어가면 대체 어떻게 돌아가는지 알아보자. 라이츄가 라이츄를 꺼내고 그 라이츄가 라이츄를 꺼내고 


팩토리얼

def factorial(n):
  if n == 0:
    return 1
  return n * factorial(n-1)

5!을 저걸로 계산하게 되면 

1. factorial(5) = 5 * factorial(4)
2. factorial(4) = 4 * factorial(3)
3. factorial(3) = 3 * factorial(2)
4. factorial(2) = 2 * factorial(1)
5. factorial(1) = 1 * factorial(0)
6. factorial(0) = 1
7. 오케이 가릿

여기까지 왔더니 factorial(0)이 1이네? 그럼 호출 중단하고 싹 다 곱하면 된다. 이중계승의 경우 홀수와 짝수의 종착점이 다르기때문에 n이 0일때와 1일때 둘 다 정의해줘야 한다. 

 

피보나치 수열

def fibonacci(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fibonacci(n-2) + fibonacci(n-1)

 

코드 돌아가는거 보여주는걸로 돌렸더니 74단계 걸린거 실화다. 

1. 피보나치(5) = 피보나치(4) + 피보나치(3)
2. 피보나치(3) = 피보나치(2) + 피보나치(1)
3. 피보나치(1) = 1
4. 피보나치(2) = 피보나치(1) + 피보나치(0)
5. 피보나치(0) = 0
6. 오케이 가릿 

대충 이런 느낌이다. 재귀가 끝나는 조건문을 만날때까지는 계속 함수를 호출하고, 재귀가 끝나면 싹 계산하는 방식. 

'Coding > Python' 카테고리의 다른 글

input vs sys.stdin.readline()  (0) 2022.08.22
Python으로 JSON파일 읽기  (0) 2022.08.22
감마 함수+팩토리얼 코드에서 유리수 처리  (0) 2022.08.22
이중계승 추가  (0) 2022.08.22
팩토리얼 로직 또 수정  (0) 2022.08.22

아, 정확히는 정수가 아닌 유리수 말하는거다. 


감마 함수? 

일단 팩토리얼(n!)이 n부터 1까지 쫘라락 곱한다는것과 정수가 아닌 유리수는 그걸 감마 함수에 때려박아야 한다는 걸 대충 알고 계실 것이다. 감마함수는 팩토리얼의 상위호환으로, 

대충 이런 식을 쓴다. 사실 식이 네 갠데 일반적으로 알려진 게 저 적분식인거고, 그나마 저 형태가 이해하기 쉽다. 나머지는 뭐 파이(원주율 말고 곱의 기호 파이) 뜨고 난리났음. 

 

코드

from sympy import *
from mpmath import mp
import sys
a = complex(sys.stdin.readline())
# 복소수는 complex로 입력해야 합니다. (int: 정수, float: 소수라고 생각하면 됨)
t = symbols("t")
expr = t ** (a - 1) * exp(-t)
try: 
    print(Integral((expr),(t,0,oo)).doit())
except: 
    print("Cannot calculate Gamma function")

일단 어제 심파이랑 mpmath 올리고 해 본 결과 expr에 저 식 때려박고 음수로 입력하면 에러가 뜬다. 즉, 예외처리가 가능하다. 근데 예외 처리에는 문제가 몇 개 있는데 

1) 원래 감마 함수는 0에서도 정의가 안 된다. (정의역이 0보다 크다) 
2) 그리고 try except는 if랑 같이 못 쓴다. 

대충 이런 문제가 있다. 

 

from sympy import *
from mpmath import mp
import sys
a = complex(sys.stdin.readline())
# 복소수는 complex로 입력해야 합니다. (int: 정수, float: 소수라고 생각하면 됨)
t = symbols("t")
expr = t ** (a - 1) * exp(-t)
gamma_function = integrate((expr),(t,0,oo))
if a.real > 0:
    if gamma_function % 1 == 0:
        print(round(gamma_function))
    else: 
        print(round(gamma_function,3))
else: 
    print("Cannot calculate Gamma function")

그래서 0도 계산 못 하는걸로 처리하느라 if 줬다. 

참고로 복소수의 경우 실수부가 음수여도 알파신 때려박으면 계산은 해 준다. 결과가 복소수긴 한데... 근데 왜 저기서는 실수부가 양수일 때만 처리했느냐... Sympy는 복소수 실수부가 음수일 때 적분 객체만 띡 생성하고 끝난다. 

 

팩토리얼 코드 수정

def gamma_function(a):
    t = symbols("t")
    expr = t ** (a - 1) * exp(-t)
    if a.real > 0:
        return integrate((expr),(t,0,oo))
    else: 
        return False

함수 자체는 어렵지 않은데, 문제가 하나 있다면 감마함수는 

이다. 그래서 저기에 1.5를 넣고 저 함수 그대로 돌리면 0.5!을 계산해준다. 

 

elif a % 1 != 0:
    print(round(gamma_function(a+1),3))

그래서 계산할 때 1을 더해줘야 한다. (1.5!을 구할거면 2.5를 넣어야 한다) 복소수도 마찬가지로 실수부에 1을 더해야 하지만 저 코드에서는 복소수 지원 안 한다. 


from sympy import *
from mpmath import mp
import sys

def gamma_function(a):
    t = symbols("t")
    expr = t ** (a - 1) * exp(-t)
    if a.real > 0:
        return integrate((expr),(t,0,oo))
    else: 
        return False

a = float(sys.stdin.readline().strip())
# Factorial(계승): 일반적으로 n! = 1*2*3*...*n-1*n이다. (5!=1*2*3*4*5)
factorial = 1
if a < 0:
    print("Can't calculate factorial")
    # 음수는 factorlal이 없음
elif a == 0:
    print(factorial)
    # 0! = 1
elif a % 1 != 0:
    print(round(gamma_function(a+1),3))
else:
    for i in range(int(a),0,-1):
        factorial *= i
    print(factorial)

For

 

from sympy import *
from mpmath import mp
import sys

def gamma_function(a):
    t = symbols("t")
    expr = t ** (a - 1) * exp(-t)
    if a.real > 0:
        return integrate((expr),(t,0,oo))
    else: 
        return False

a = float(sys.stdin.readline().strip())
# 와 팩토리얼이 생각보다 빡센거였구나... 
# Factorial(계승): 일반적으로 n! = 1*2*3*...*n-1*n이다. (5!=1*2*3*4*5)
factorial = 1
if a < 0: 
    print("Can't calculate factorial")
    # 음수는 factorial이 없음
elif a == 0: 
    factorial = 1
    # 0! = 1
elif a % 1 != 0:
    print(round(gamma_function(a+1),3))
else: 
    while a >= 1:
        factorial *= a
        a -= 1
    print(factorial)

While

'Coding > Python' 카테고리의 다른 글

Python으로 JSON파일 읽기  (0) 2022.08.22
재귀함수가 돌아가는 방식을 알아보자  (0) 2022.08.22
이중계승 추가  (0) 2022.08.22
팩토리얼 로직 또 수정  (0) 2022.08.22
팩토리얼 로직 수정+계승 소수  (0) 2022.08.22

일단 이중계승이 뭐냐... 

우리가 알고있는 팩토리얼은 n! = n*(n-1)*(n-2) 이런 식으로 곱하는거기때문에 종착점이 무조건 1이다. 근데 이중 계승은 곱하는 숫자의 공차가 2이기 때문에 입력한 숫자가 홀수냐 짝수냐에 따라 종착점이 갈린다. 홀수의 경우 종착점이 1이고, 짝수의 경우 종착점이 2가 된다. 그래서 6!!=6*4*2=48, 5!!=5*3*1=15가 된다. (그냥 계승은 6!이 720 5!이 120)


import sys
a = float(sys.stdin.readline())
factorial = 1
if a < 0:
    print("Can't calculate factorial")
elif a == 0 or a == 1:
    print(factorial)
    # 0! = 1
elif a % 1 != 0:
    print("정수가 아닌 유리수는 일반적인 방법으로 팩토리얼을 구할 수 없습니다. ")
else:
    for i in range(int(a),0,-2):
        factorial *= i
    print(factorial)

for

 

import sys
a = float(sys.stdin.readline())
factorial = 1
if a < 0:
    print("Can't calculate factorial")
elif a == 0 or a == 1:
    print(factorial)
    # 0! = 1
elif a % 1 != 0:
    print("정수가 아닌 유리수는 일반적인 방법으로 팩토리얼을 구할 수 없습니다. ")
else:
    while a > 0:
        factorial *= a
        a -= 2
    print(factorial)

while

근데 저거때문에 뭐가 별도로 추가되는 건 아니라 걍 2씩 빼고 곱하게 해놨음. 참고로 0이랑 1은 내가 직접 울프럼알파에서 돌려봤다. (감마함수도 몇개 돌려보긴 했는데 갸는 적분이라 코딩 못함... 일단 식 자체를 내가 이해를 못했어요)

이건 또 언제 떠올랐냐면 밥 사러 편의점 가다가 떠오름... ㅋㅋㅋㅋㅋㅋㅋㅋ 

아니 농담 아니고 진짜로 그래요 뜬금없이 샐러드 고르고 계산하다가 아 근데 자연수 아닌거 어캄? 이러고 떠오름 


들어가기 전에... 팩토리얼이 되는 범위가 어디까지인가에 대해 설명을 좀 하자면... 팩토리얼(n!)은 n부터 1까지 쫙 곱하는 그게 맞는데, 0!은 0이 아니라 1이고(...) 음수에 대해서는 정의가 안 되어 있다. 그리고 정수가 아닌 유리수(즉 q/p꼴로 나타내는 수 중 p가 1이 아닌 것...아니 음수도 아냐 치워)에 대해서는 일반적으로 고등학교 과정에서 배우는 팩토리얼이 아니라 감마 함수를 써서 계산한다. 그러니까

저기다 때려박으면 된다. 수기로 하지 말고 알파신 부르자. (울프램알파도 저거 해준다) 

감마(n)은 (n-1)!, 즉 감마(3) 하면 2!이고 감마(4) 하면 3!이다. 
그리고 저기에 소수를 넣으면... 어...... 나 루트 파이 본 거 같음... 
복소수? 된다. 허수부가 0이 아니어도 되는데 실수부는 0보다 커야 한다. 

아무튼 저기다 때려박으려면 일단 0보다 커야 한다. 0보다 작으면 감마함수 할아버지가 와도 답이 없다. 아무튼, 그래서 일차적으로 음수가 들어가면 팩토리얼이 없다고 나오는것. 그럼 이번에는 뭐때문에 수정했냐고? 아니 정수가 아닌 유리수 처리때문에 했다니까요. 저건 코딩 못해... 


For문

import sys
a = float(sys.stdin.readline())
# Factorial(계승): 일반적으로 n! = 1*2*3*...*n-1*n이다. (5!=1*2*3*4*5)
factorial = 1
if a < 0:
    print("Can't calculate factorial")
    # 음수는 factorlal이 없음
elif a == 0:
    print(factorial)
    # 0! = 1
elif a % 1 != 0:
    print("정수가 아닌 유리수는 일반적인 방법으로 팩토리얼을 구할 수 없습니다. ")
else:
    for i in range(int(a),0,-1):
        factorial *= i
    print(factorial)

기본적인 로직은 비슷하고 밑에 정수가 아닌 유리수를 판별하는 코드가 추가되었다. 정확히는 0보다 크면서 정수가 아닌 유리수(음수는 위에서 거른다). 저것도 밥 먹다가 0.5 이런건 1보다 작은거니까 1로 나누면 나머지가 나오겠지? 해서 시험삼아 해 보고 오케이 가릿 한 거. 회사에서 점심먹다 한 거라 노션에 올린 코드도 수기로 적은 거다. 날코딩에 이은 손코딩인가 

참고로 입력이 float로 받는거라 for문 돌릴 때는 int로 형변환 한번 해 줘야 한다. 안그러면 에러난다. 

 

While문

import sys
a = float(sys.stdin.readline())
# 와 팩토리얼이 생각보다 빡센거였구나... 
# Factorial(계승): 일반적으로 n! = 1*2*3*...*n-1*n이다. (5!=1*2*3*4*5)
factorial = 1
if a < 0: 
    print("Can't calculate factorial")
    # 음수는 factorial이 없음
elif a == 0: 
    factorial = 1
    # 0! = 1
elif a % 1 != 0:
    print("정수가 아닌 유리수는 일반적인 방법으로 팩토리얼을 구할 수 없습니다. ")
else: 
    while a >= 1:
        factorial *= a
        a -= 1
    print(factorial)

While은 for문과 달리 조건부 반복문이라 팩토리얼을 구하는 로직 자체가 달라서, a가 양수(자연수)이면 factorial이라는 변수에 a를 곱하고 a에 1을 빼는 걸 a가 1이 될 때까지(그래서 조건에 a >= 1이라고 되어 있다) 하면 된다. 그래서 따로 형변환을 할 필요가 없다. 

대신 저대로 출력하면 소수점이 붙어서 나오니까 그거 뵈기 싫으면 출력문에서 형변환 하면 된다. 

 

함수 이식판

def factorial(a):
    factorial = 1
    if a < 0:
        return False
    elif a == 0:
        factorial = 1
        return factorial
    elif a % 1 != 0:
        return False
    else:
        for i in range(int(a),0,-1):
            factorial *= i
        return factorial
# Factorial 구하는 로직(...)

어째서인지는 모르겠으나 함수와 클래스(얘도 근데 안에 함수 들었잖음...)에는 print 말고 return이 국룰이다. print는 화면에 출력하라는 얘기이고 return은 반환하라는 얘기. 즉 함수 다 돌리고 결과 반환하라는 얘기다. 덕분에 for나 while보다는 짜기가 편해진게 팩토리얼이 정의가 안 되는 케이스면 걍 return false 때려버리면 돼서 편하다. 

해당 함수는 순열/조합/계승소수 코드에 삽입되어 있다. 

'Coding > Python' 카테고리의 다른 글

감마 함수+팩토리얼 코드에서 유리수 처리  (0) 2022.08.22
이중계승 추가  (0) 2022.08.22
팩토리얼 로직 수정+계승 소수  (0) 2022.08.22
순열조합  (0) 2022.08.22
워드클라우드 코드에 기능 추가  (0) 2022.08.22

Factorial 로직 수정

재밌는 사실을 하나 알려주자면 출근길에 지하철에서 멍때리다 생각난거임... 6호선 생각보다 자리 없어요 여러분. 

팩토리얼(n!) 그니까 계승이 1부터 n까지 쭉 곱하는거인 건 맞다. 맞는데 문제가 두 가지 있다. 
첫째, 0! = 1이다. (0 아님)
둘째, 음수는 팩토리얼이 없다. (정수가 아닌 유리수는 감마함수 때려박으면 된다나...)

그래서 이 두 가지 케이스에 대한 처리를 해야 한다. 

 

import sys
a = int(sys.stdin.readline())
# Factorial(계승): 일반적으로 n! = 1*2*3*...*n-1*n이다. (5!=1*2*3*4*5)
factorial = 1
if a < 0:
    print("Can't calculate factorial")
    # 음수는 factorlal이 없음
elif a == 0:
    print(factorial)
    # 0! = 1
else:
    for i in range(a,0,-1):
        factorial *= i
    print(factorial)

for

 

import sys
a = int(sys.stdin.readline())
# 와 팩토리얼이 생각보다 빡센거였구나... 
# Factorial(계승): 일반적으로 n! = 1*2*3*...*n-1*n이다. (5!=1*2*3*4*5)
factorial = 1
if a < 0: 
    print("Can't calculate factorial")
    # 음수는 factorial이 없음
elif a == 0: 
    factorial = 1
    # 0! = 1
else: 
    while a >= 1:
        factorial *= a
        a -= 1
    print(factorial)

while

 

def factorial(a):
    factorial = 1
    if a < 0:
        return False
    elif a == 0:
        factorial = 1
        return factorial
    else:
        for i in range(a,0,-1):
            factorial *= i
        return

순열/조합에 함수로 이식된 형태

 

계승 소수

이건 뭐냐면 n! - 1이나 n! + 1이 소수인 걸 말한다. 끝. (대충 펀쿨섹좌 짤) 와 이걸 이렇게 

 

import sys
a = int(sys.stdin.readline())
# 역사와 전통의 그거 맞음

def factorial(a):
    factorial = 1
    if a < 0:
        return False
    elif a == 0:
        factorial = 1
        return factorial
    else:
        for i in range(a,0,-1):
            factorial *= i
        return factorial
# Factorial 구하는 로직(...)

def isprime(a):
    sqrt = int(a ** 0.5)
    if a < 2:
        return False
    for i in range(2,sqrt+1):
        if a % i == 0:
            return False
    else: 
        return True
# 소수 정의 함수

plus_factorial = factorial(a) + 1
minus_factorial = factorial(a) - 1

if isprime(minus_factorial) and isprime(plus_factorial):
    print("{}! - 1, {}! + 1 둘 다 {}, {}로 계승 소수입니다. ".format(a,a,minus_factorial,plus_factorial))
elif isprime(minus_factorial):
    print("{}! - 1이 {}로 계승 소수입니다. ".format(a,minus_factorial))
elif isprime(plus_factorial):
    print("{}! + 1이 {}로 계승 소수입니다. ".format(a,plus_factorial))
else:
    print("{}! - 1, {}! + 1 둘 다 {}, {}로 계승 소수가 아닙니다. ".format(a,a,minus_factorial,plus_factorial))

이게 전체 코드다. 코드 자체는 크게 입력/함수/계산/판별로 나뉜다. 

참고로 본인 원래 모듈이랑 함수정의 다 맨 위에서 해버리는 스타일임. 제한효소때도 그래서 class가 위로 갔었다. 

 

def factorial(a):
    factorial = 1
    if a < 0:
        return False
    elif a == 0:
        factorial = 1
        return factorial
    else:
        for i in range(a,0,-1):
            factorial *= i
        return factorial
# Factorial 구하는 로직(...)

def isprime(a):
    sqrt = int(a ** 0.5)
    if a < 2:
        return False
    for i in range(2,sqrt+1):
        if a % i == 0:
            return False
    else: 
        return True
# 소수 정의 함수

함수는 팩토리얼이랑 소수 판별 두 개가 들어가 있다. 

 

if isprime(minus_factorial) and isprime(plus_factorial):
    print("{}! - 1, {}! + 1 둘 다 {}, {}로 계승 소수입니다. ".format(a,a,minus_factorial,plus_factorial))
elif isprime(minus_factorial):
    print("{}! - 1이 {}로 계승 소수입니다. ".format(a,minus_factorial))
elif isprime(plus_factorial):
    print("{}! + 1이 {}로 계승 소수입니다. ".format(a,plus_factorial))
else:
    print("{}! - 1, {}! + 1 둘 다 {}, {}로 계승 소수가 아닙니다. ".format(a,a,minus_factorial,plus_factorial))

이쪽이 판별 부분. 위키피디아에 예시로 나온 것 중에 n! - 1이랑 n! + 1 둘 다 소수인 게 있는데 3이 그 예이다. (6 - 1은 5고 6 + 1은 7)

'Coding > Python' 카테고리의 다른 글

이중계승 추가  (0) 2022.08.22
팩토리얼 로직 또 수정  (0) 2022.08.22
순열조합  (0) 2022.08.22
워드클라우드 코드에 기능 추가  (0) 2022.08.22
List comprehension  (0) 2022.08.22

워드클라우드 py파일 만드는 김에 순열조합도 코딩했음. 

순열: nPr(n개의 원소 중 r개를 중복 없이 늘어놓는 가짓수)
조합: nCr(n개의 원소 중 r개를 픽할 때의 가짓수)


# permutation(순열): 서로 다른 n개의 원소 중 r개를 중복 없이 늘어놓는 것
def factorial(a):
    factorial = 1
    for i in range(1,a+1):
        factorial = factorial * i
    return factorial
# 순열에 팩토리얼이 들어가서 어쩔 수 없어요... 
# nPr(서로 다른 n개의 원소 중 r개를 중복 없이 늫어놓는 것)을 구하는 공식은 n!/(n-r)!입니다. 
n = int(input("n에 들어가는 수를 입력해주세요: "))
r = int(input("r에 들어가는 수를 입력해주세요: "))
P = factorial(n) / factorial(n - r)
print("{}개의 원소 중 {}개를 중복 없이 늘어놓을 수 있는 가짓수의 계산 결과는 {}입니다. ".format(n,r,int(P)))

이게 순열이고 

 

# n개의 원소 중 r개를 택하는 것이 조합입니다. nCr로 표기합니다. 
def factorial(a):
    factorial = 1
    for i in range(1,a+1):
        factorial = factorial * i
    return factorial
# 아 얘는 조합 구하는데 순열이 필요해서 어쩔 수 없음. 
# nCr을 구하는 공식은 n!/r!(n-r)!입니다. 
n = int(input("n에 들어가는 수를 입력해주세요: "))
r = int(input("r에 들어가는 수를 입력해주세요: "))
bunmo = factorial(r) * factorial(n - r)
C = factorial(n)/bunmo
print(C)

이게 조합임다. 

'Coding > Python' 카테고리의 다른 글

팩토리얼 로직 또 수정  (0) 2022.08.22
팩토리얼 로직 수정+계승 소수  (0) 2022.08.22
워드클라우드 코드에 기능 추가  (0) 2022.08.22
List comprehension  (0) 2022.08.22
완전수 찾는 코드  (0) 2022.08.22

일단 Entrez랑 Text랑 추가된 기능이 조금 다름. 기본 맥락은 같습니다. 


공통 기능

워드클라우드용 마스킹 이미지를 불러오기 위한 파일 창 추가

 

from argparse import FileType
import tkinter
from tkinter import filedialog

추가 소환한 모듈(...)

 

root = tkinter.Tk()
root.withdraw()
dir_path = filedialog.askopenfilename(parent=root,initialdir="/home/koreanraichu",title='Please select a directory',filetypes = (("*.png","*png"),("*.jpg","*jpg"),("*.gif","*gif")))
image = np.array(Image.open(dir_path)) 
# 마스킹할 이미지(흰 바탕에 검정색을 권장함)
font_path = '/usr/share/fonts/나눔손글씨 바른히피.ttf'
wordcloud = WordCloud(font_path = font_path,stopwords=STOPWORDS,
                      background_color="#ffffff",colormap="magma",width = 800, height=800,
                      mask=image)
# Font path: 글꼴 설정하실 경우 여기에 쓰세요
# background color: wordcloud 배경 설정할 때 여기서 하세요 
# colormap: 워드클라우드 글자색을 여기서 바꿔주세요(matplotlib colormap 치면 많이 나옵니다)

글꼴은 고정된 상태이다. 

 

분기

공통적으로 워드클라우드를 저장하는 건 똑같은데, Text랑 Entrez랑 조금 다른 부분이 있다. 일단 텍스트부터 보고 가자. 

 

root = tkinter.Tk()
root.withdraw()
save_path = filedialog.asksaveasfilename(parent=root,initialdir="/home/koreanraichu",title='Please select a directory',filetypes = (("*.png","*png"),("*.jpg","*jpg"),("*.gif","*gif")))
image = np.array(Image.open(dir_path)) 
wordcloud = wordcloud.generate_from_text(text)
plot.figure(figsize=(15,15))
plot.axis('off')
plot.imshow(wordcloud)
plot.savefig(save_path)
plot.show()
print("wordcloud saved where {}".format(save_path))

이쪽이 Text에 적용된 코드. 파일명도 저기서 입력하면 된다. 

 

term = input("Entrez database에서 가져오고 싶은 주제가 뭔가요? (Title로 검색) \n")
year = input("특별히 보고 싶은 년도가 있나요? (optional, 없으면 공백으로 입력해주세요) \n")
if year: 
    terms = "{}[TITLE] AND {}[YEAR]".format(term,year)
else: 
    terms = "{}[TITLE]".format(term)

Entrez의 경우 일단 용어(그니까 논문 제목) 혹은 연도를 입력받되, 연도가 optional이다. 여기는 차후 저자 검색 등의 조건이 추가되면 elif 늘어나서 괴랄해 질 예정... 사실 핸들에 있는 걸 가져올 수 있으면 좋은데, 그게 사실상 불가능하다. 아마 나중에 검색어가 늘어나면 입력을 []까지 해서 받거나 코드단에서 수정하고 text처럼 될 지도 모름... 

 

if year:
    save_dir = "{}/{}&{}.png".format(save_path,term,year)
else: 
    save_dir = "{}/{}.png".format(save_path,term)

저장 경로가 뜨는 기본 골자는 같지만, 위에서 연도와 용어를 입력받기 때문에 파일명이 그걸로 자동 생성된다. 즉, 파일 이름을 입력하는 게 아니라 말 그대로 저장 경로만 선택해주면 된다. 


입력 관련 추가 수정사항

검색어 입력할 때 단어[조건] AND 단어[조건] 이런 식으로 통으로 입력해야 함. 대신 그걸로 파일명 만들어드립니다. 

예: Bacteria[TITLE] AND 2022[YEAR]

 

import re

일단 규식이형부터 불러봅니다

terms = terms.replace('[','-')
terms = terms.replace(']','-')
# 이스케이프 시퀀스 멕여도 안돼서 결국 이게 최선이었음... 
escape_terms = re.findall('-.{0,5}-',terms)
for i in escape_terms:
    terms = terms.replace(i,"")

일단 대괄호가 메타문자기때문에 저게 낑겨있으면 인식을 못해요 
근데 더 골때리는건 이스케이프 뭐시기 처리를 했는데도 불구하고 인식을 못해 
그래서 제한효소 DB때처럼 걍 대괄호를 다 하이픈으로 바꾸고 정규식으로 찾은 다음 빼버림 

그러니까 중간과정이 


Bacteria[TITLE] AND 2022[YEAR]
Bacteria-TITLE- AND 2022-YEAR-
Bacteria AND 2022

이렇게 되는거임. 

 

wordcloud saved where /home/koreanraichu/2022 AND bacteria.png.png

멘트 수정해야겠구만. 

+추가 기능: 그래프 여백 제거


일부 코드의 함수화

def replace_function (a):
    a = a.replace('[','-')
    a = a.replace(']','-')
    escape_terms = re.findall('-.{0,5}-',a)
    for i in escape_terms:
        a = a.replace(i,"")
    if '/' in a:
        a = a.replace('/','-')
    return a
# term 변환하는 거 함수로 뺐습니다. 근데 블록 실행하기 더 귀찮아진 건 기분탓인가... ㅡㅡ

엔트레즈: 입력받은 검색어 파일명으로 바꾸는 코드

 

def munjang_to_noun (a):
    a = ' '.join(a)
    a = okt.nouns(a)
    a = ' '.join(a)
    return a

한글: 텍스트 한글 처리하는 코드(저거 okt 돌리고 다시 join 안하면 작은따옴표 붙어서 나옴)

'Coding > Python' 카테고리의 다른 글

팩토리얼 로직 수정+계승 소수  (0) 2022.08.22
순열조합  (0) 2022.08.22
List comprehension  (0) 2022.08.22
완전수 찾는 코드  (0) 2022.08.22
Project restriction enzyme: 패치노트  (0) 2022.08.22

Profile

Lv. 34 라이츄

요즘 날씨 솔직히 에바참치김치꽁치갈치넙치삼치날치기름치준치학꽁치임..