Yield란 일종의 생성자(Generator)와 같습니다.

이더레이터(iterator)와는 비슷한 결과값을 가져오지만, 다른 것입니다.

이더레이터는 공부하다보니 알게 되었습니다. 일단 먼저 yield를 살펴보도록 하겠습니다.


먼저 Python docs에서 Generator의 정의를 보도록 합시다.



generator


  A function which returns an iterator. It looks like a normal function except that it contains yield statements for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function. Each yield temporarily suspends processing, remembering the location execution state (including local variables and pending try-statements). When the generator resumes, it picks-up where it left-off (in contrast to functions which start fresh on every invocation).



Generator는 Iterator를 생성해주는 함수라고 간단하게 설명할 수 있겠습니다. iterator는 next()함수를 통해 순차적으로 값을 가져오는 object를 말합니다.

(LINK : Iterator & Iterable에 대한 설명)


Generator는 일반함수릉 크게 다를 것은 없지만 yield라는 가장 큰 차이점을 가지고 있습니다. 

아래의 소스는 yield의 기본 예제입니다.

#python 3 version source
#yield generator test source
#yield_Basic_Test.py

def number_generator(n):
	print("Function Start")
	while n < 6:
		yield n
		n += 1
	print("Function End")
	
if __name__ == "__main__":
	for i in number_generator(0):
		print(i)
		


결과 값은 다음과 같습니다.

[그림 01] yield_Basic_Test.py 결과값


for문과 number_generator(n)함수의 합이 만나서 [그림 01]과 같은 결과값이 나옵니다.

number_generator()함수에는 return이 없는데 어떻게 저렇게 while문 내에서 돌아가는 n의 값이 출력이 되는 것일까요?

기존의 함수의 Call 모습과 yield가 포함된 함수의 Call 모습을 비교해보도록 하겠습니다.


[그림 02] Main에서 Normal_function으로의 함수 흐름


Main함수에서 Normal_function으로의 함수 흐름은 기존의 여타 함수의 흐름과 다를 게 없습니다.

Main을 실행하다가 Normal_function으로 가서 함수 내의 루틴을 실행시킨 후 Main으로 돌아가 남은 Main의 루틴이 실행됩니다.

그렇다면 이제 yield가 포함된 함수를 보도록 합시다.


[그림 03] Main에서 number_generator()으로의 함수 흐름


number_generator()으로의 함수 흐름이 일반 함수의 흐름과는 많이 다릅니다.

이 흐름은 yield라는 것 때문입니다. yield는 두 번째 흐름처럼 n의 값을 return 하고 다시 함수로 돌아가게끔해주는 것입니다.

그렇다면 다음 소스를 보겠습니다.


Python docs에서 정의된 yield의 정의를 보도록 합시다.



Yield


The yield statement is only used when defining a generator function, and is only used in the body of the generator function. Using a yield statement in a function definition is sufficient to cause that definition to create a generator function instead of a normal function.


When a generator function is called, it returns an iterator known as a generator iterator, or more commonly, a generator. The body of the generator function is executed by calling the generator’s next() method repeatedly until it raises an exception.


When a yield statement is executed, the state of the generator is frozen and the value of expression_list is returned to next()‘s caller. By “frozen” we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, and the internal evaluation stack: enough information is saved so that the next time next() is invoked, the function can proceed exactly as if the yield statement were just another external call.


As of Python version 2.5, the yield statement is now allowed in the try clause of a try ... finally construct. If the generator is not resumed before it is finalized (by reaching a zero reference count or by being garbage collected), the generator-iterator’s close() method will be called, allowing any pending finally clauses to execute.



yield는 generator가 일반 함수와 구분되는 가장 핵심적인 부분입니다. yield를 사용함으로써 어떤 차이가 있는지는 위의 일반 함수와 yield가 포함된 함수의 흐름으로 설명 드렸습니다.

더 자세하게 설명드리자면, generator 함수가 실행 중 yield를 만날 경우, 해당 함수는 그 상태로 정지되며, 반환 값을 next()를 호출한 쪽으로 전달하게 됩니다. 이후 해당 함수는 일반적인 경우처럼 종료되는 것이 아니라 그 상태로 유지되게 됩니다. 즉, 함수에서 사용된 local 변수나 instruction pointer 등과 같은 함수 내부에서 사용된 데이터들이 메모리에 그대로 유지되는 것입니다.   [bluese05님 글 출처]


#python 3 version source
#yield test source
#yield_Routine_Test.py

def generator_test(n):
	print("-=-=-=-=-=-= Generator Start =-=-=-=-=-=-")
	
	while(n < 3):
		print("<< Before Yield >>")
		yield n
		n += 1
		print("<< After Yield >>")
		
	print("-=-=-=-=-=-= Generator End =-=-=-=-=-=-")
	
if __name__ == "__main__":
	print("---------- Main Function Start ----------")
	
	for i in generator_test(0):
		print("Start For))))))))))))")
		print("Yield i is : ", i)
		print("End For))))))))))))))")
		
	print("---------- Main Function End ----------")
		

이 소스는 Main 함수와 generator_test()함수의 흐름을 보기 위한 함수입니다.

결과 값을 보도록 합시다.

[그림 04] yield_Routine_Test.py


결과값을 보면 먼저 Main이 시작하고 끝은 Main으로 끝납니다.

그리고 Generator 함수의 시작과 끝도 양 끝에 있습니다.

그렇다면 루틴은 <Before yield> -> for문 시작 -> for문 내의 print 실행 -> for문 끝 -> <After yield>로 되어 있고 이렇게 3회 반복됩니다.

이 루틴을 그림으로 그려보면 [그림 05]와 같습니다.


[그림 05] Main에서 for문으로 된 generator 함수의 흐름


먼저 Main이 실행되면서 for문 내의 generator_test함수로 들어갑니다. 그리고 함수가 시작되면서 while문으로 들어가 쭈욱 실행하면서 yield에서 n이라는 변수에 저장된 값을 반환합니다. 그리고, for문 내의 루틴이 실행된 후(print함수를 실행), 다시 yield 함수의 아래의 루틴을 실행 후 다시 while문이 실행되면서 루틴이 반복됩니다.

설명을 겁나 어렵게 했네요. 가볍게 <Before yield> -> for문 시작 -> for문 내의 print 실행 -> for문 끝 -> <After yield>로 루틴이 이루어져 있고, 그 겉에 Main과 generator함수가 있다고 보시면 됩니다.


자 이제, Python docs에서 Generator expression을 살펴보도록 하겠습니다.



generator expression


  An expression that returns an iterator. It looks like a normal expression followed by a for expression defining a loop variable, range, and an optional if expression. The combined expression generates values for an enclosing function:



generator expression이란 generator 함수를 좀 더 쉽게 사용할 수 있도록 제공되는 표현형식입니다. list comprehension과 비슷하지만, [] 대신 ()를 사용하면 됩니다. 아래의 소스는 0 ~ 9 사이 정수 중 홀수를 list와 generator object로 생성한 예제입니다.

[i for i in range(10) if i % 2]
#[1,3,5,7,9]
(i for i in range(10) if i % 2)
#<generator object at 0x......>


활용해보면 다음과 같은 소스와 결과를 볼 수 있습니다.

#python 3 version source
#Yield_Test.py

def generator(wordList):
	wordListLength = 0
	print("Generator Start!!")
	while(wordListLength < len(wordList)):
		yield wordList[wordListLength]
		wordListLength += 1
		
	
if __name__ == "__main__":
	Main_wordList = ["TEST", "IS", "WORK", "GOOD"]
	for i in generator(Main_wordList):
		print(i)
		
	print("Done!!!")


[그림 06] yield_Test.py 결과값





generator를 사용하는 이유는 크게 두 가지로 구분할 수 있습니다.


1. Memory를 효율적으로 사용할 수 있음

list comprehension과 generator expression으로 각각 생성했을 때 메모리 사용 상태를 봅시다.

#python 3 version 기준
import sys

a = [i for i in range(100) if i % 2] # list 
sys.getsizeof(a)
#268

b = [i for i in range(1000) if i % 2]
sys.getsizeof(b)
#2140

c = (i for i in range(100) if i % 2) # generator
sys.getsizeof(c)
#48

d = (i for i in range(1000) if i % 2)
sys.getsizeof(d)
#48


list의 경우 사이즈가 커질 수록 그만큼 메모리 사용량이 늘어납니다. 하지만, generator의 경우는 사이즈가 커진다해도 차지하는 메모리 사이즈는 동일한 것을 볼 수 있습니다. list와 generator의 동작 방식의 차이가 있을 뿐입니다.


list는 list 안에 속한 모든 데이터를 메모리에 적재하기 때문에 list의 크기 만큼 차지하는 메모리 사이즈가 늘어나게 됩니다. 하지만 generator의 경우 데이터 값을 한 번에 메모리에 적재하는 것이 아니라 next() 함수를 통해 차례로 값에 접근할 때마다 메모리에 적재하는 방식입니다.  [bluese05님 글 출처]


2. Lazy evaluation 즉 계산 결과 값이 필요할 때까지 계산을 늦추는 효과가 있음

예제로써 설명하도록 하겠습니다.

먼저 함수는 sleep_function(x)라고 선언하도록 합시다.

def sleep_function(x):
	print("Sleep..")
	time.sleep(1)
	return x


sleep_function()함수는 1초간 sleep을 수행한 후 x값을 return하는 함수입니다. 만약 위 sleep_function()함수를 이용해 list와 generator를 생성하면 어떻게 동작될지를 다음의 예제로 설명하도록 하겠습니다.

import time

def sleep_function(x):
	print("sleep...")
	time.sleep(1)
	return x

# list 생성
list = [sleep_function(x) for x in range(5)]

# generator 생성
generator = (sleep_function(y) for y in range(5))

for i in list:
	print(i)
	
print("=-=-=-=-=-=-=-=-=-=-=-=-=-=")

for j in generator:
	print(j)


위의 소스를 실행시키면 list생성과 generator 생성을 통해 각각 다른 방법으로 결과값을 받아오는 것을 알 수 있습니다.

결과 값은 [그림 07]과 같습니다.


[그림 07] list와 generator의 실행 차이


list의 경우는 list comprehension을 수행할 때, list의 모든 값을 먼저 수행하기 때문에 sleep_function() 함수를 range()만큼 한 번에 수행하게 됩니다.

generator의 경우 생성 시 실제 값을 로딩하지 않고, for문이 수행될 때 하나씩 sleep_function()을 수행하며 값을 불러오게 됩니다. generator의 특징 중 하나는 "수행 시간이 긴 연산을 필요한 순간까지 늦출 수 있다는 점이 특징"이라고 할 수 있습니다.


간단하게 말하면, list는 실행되고 결과 값을 list에 반환되어 [0,1,2,3,4] 값을 가지고 있게 됩니다.

그러나 generator는 실행되지 않고 object로 선언이 되기 때문에 필요한 때마다 실행시킬 수 있게 된 것입니다.


[그림 08] list comprehension과 generator expression 차이



참고 URL : haerakai님의 글

참고 URL : bluese05님의 글



[그림 01] Problem_011 문제


문제 설명


문제 이름 : 격자무늬의 근접한 숫자의 곱 중 가장 큰 값

문제 내용 : 20 x 20의 격자무늬로 이루어진 숫자열 중 빨간색으로 되어 있는 4개의 숫자의 곱은 26 x 63 x 78 x 14 = 1788696입니다. 그렇다면 근접한 4개의 숫자의 곱 중 가장 큰 값은 무엇입니까?(가로, 세로, 위, 아래, 대각선 모두 포함)


매우 힘들었습니다...

특히 대각선...

문제에서 필요한 내용은 일단 각설하고 소스코드로 넘어가도록 하겠습니다.


소스코드


#Problem 011 Largest product in a grid
#Python 3 version source

def getDiagonallyRight(List):
	DiagonallyRight = []
	num = 1
	
	for k in range(0,17):
		for j in range(0,17):
			for i in range(0,4):
				num *= int(List[k+i][j+i])
			DiagonallyRight.append(num)
			num = 1 
	
	return DiagonallyRight
	

def getDiagonallyLeft(List):
	DiagonallyLeft = []
	num = 1
	
	for k in range(0,17):
		for j in range(3,20):
			for i in range(0,4):
				num *= int(List[k+i][j-i])
			DiagonallyLeft.append(num)
			num = 1
			
	return DiagonallyLeft
	
def getRow(List):#Left to Right
	RowList = []
	num = 1
	
	for i in range(0, 17):
		for j in range(0, 17):
			for k in range(0, 4):
				num *= int(List[i][j+k])
			RowList.append(num)
			num = 1
	
	return RowList
	
def getCol(List):#Up to Down
	ColList = []
	num = 1
	
	for i in range(0, 17):
		for j in range(0, 17):
			for k in range(0, 4):
				num *= int(List[i+k][j])
			ColList.append(num)
			num = 1
			
	return ColList
			
if __name__ == "__main__":
	Main_grid = """08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08
49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65
52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91
22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80
24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50
32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70
67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21
24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72
21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95
78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92
16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57
86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58
19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40
04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66
88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69
04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36
20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16
20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54
01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48"""
	Main_gridsplit = Main_grid.split()
	Main_gridint	= []
	for i in Main_gridsplit:
		Main_gridint.append(int(i))
	
	Main_gridList = []
	for i in range(0, 400, 20):
		Main_gridList.append(Main_gridsplit[i:i+20])
		
	#Main_gridList ==> 2 degree Array~~!!
	
	Main_List = []
	
	Main_List += getRow(Main_gridList)
	Main_List += getCol(Main_gridList)
	Main_List += getDiagonallyLeft(Main_gridList)
	Main_List += getDiagonallyRight(Main_gridList)
	
	Main_sort = sorted(Main_List)
	print(Main_sort[-1])
	


Main_grid는 격자무늬 숫자열을 가져온 값입니다.

Main_gridsplit --> Main_gridint --> Main_gridList까지 해서 숫자 2차원 배열을 만들었습니다.

Main_List는 4개의 함수에서 구해진 곱을 모두 더할 리스트입니다.


getRow함수는 가로로 되어 있는 곱을 모두 구하는 함수입니다.

getCol함수는 세로로 되어 있는 곱을 모두 구하는 함수입니다.

getDiagonallyLeft함수는 오른쪽 위에서 왼쪽 아래로 향한 대각선으로 되어 있는 곱을 모두 구하는 함수입니다.

getDiagonallyRight함수는 왼쪽 위에서 오른쪽 아래로 향한 대각선으로 되어 있는 곱을 모두 구하는 함수입니다.


 소스코드가 매우매우 개판이지만, 먼저 나름의 자부심을 가지고 잘하는 분의 소스코드를 가져와 비교해보도록 하겠습니다.

nums = (
    ( 8, 2,22,97,38,15, 0,40, 0,75, 4, 5, 7,78,52,12,50,77,91, 8,),
    (49,49,99,40,17,81,18,57,60,87,17,40,98,43,69,48, 4,56,62, 0,),
    (81,49,31,73,55,79,14,29,93,71,40,67,53,88,30, 3,49,13,36,65,),
    (52,70,95,23, 4,60,11,42,69,24,68,56, 1,32,56,71,37, 2,36,91,),
    (22,31,16,71,51,67,63,89,41,92,36,54,22,40,40,28,66,33,13,80,),
    (24,47,32,60,99, 3,45, 2,44,75,33,53,78,36,84,20,35,17,12,50,),
    (32,98,81,28,64,23,67,10,26,38,40,67,59,54,70,66,18,38,64,70,),
    (67,26,20,68, 2,62,12,20,95,63,94,39,63, 8,40,91,66,49,94,21,),
    (24,55,58, 5,66,73,99,26,97,17,78,78,96,83,14,88,34,89,63,72,),
    (21,36,23, 9,75, 0,76,44,20,45,35,14, 0,61,33,97,34,31,33,95,),
    (78,17,53,28,22,75,31,67,15,94, 3,80, 4,62,16,14, 9,53,56,92,),
    (16,39, 5,42,96,35,31,47,55,58,88,24, 0,17,54,24,36,29,85,57,),
    (86,56, 0,48,35,71,89, 7, 5,44,44,37,44,60,21,58,51,54,17,58,),
    (19,80,81,68, 5,94,47,69,28,73,92,13,86,52,17,77, 4,89,55,40,),
    ( 4,52, 8,83,97,35,99,16, 7,97,57,32,16,26,26,79,33,27,98,66,),
    (88,36,68,87,57,62,20,72, 3,46,33,67,46,55,12,32,63,93,53,69,),
    ( 4,42,16,73,38,25,39,11,24,94,72,18, 8,46,29,32,40,62,76,36,),
    (20,69,36,41,72,30,23,88,34,62,99,69,82,67,59,85,74, 4,36,16,),
    (20,73,35,29,78,31,90, 1,74,31,49,71,48,86,81,16,23,57, 5,54,),
    ( 1,70,54,71,83,51,54,69,16,92,33,48,61,43,52, 1,89,19,67,48,),
)

def seqs(nums, row, col):
    if row + 4 <= len(nums):                                yield list(nums[i][col] for i in xrange(row, row+4))
    if col + 4 <= len(nums[row]):                           yield list(nums[row][i] for i in xrange(col, col+4))
    if row + 4 <= len(nums) and col + 4 <= len(nums[row]):  yield list(nums[row+i][col+i] for i in xrange(0,4))
    if row + 4 <= len(nums) and col >= 3:                   yield list(nums[row+i][col-i] for i in xrange(0,4))

def product(seq):
    n = 1
    for x in seq: n = n * x
    return n

def list_seqs(nums):
    for row in range(0, len(nums)):
        for col in range(0, len(nums[row])):
            for seq in seqs(nums, row, col):
                yield seq
				
print max(product(seq) for seq in list_seqs(nums))

아쉽게도 실행이 안 되네요.

위의 소스는 python2.7.x버전의 소스이기 때문에 2.7.13으로 실행해봤는데... 안 되네요.

일단 Yield라는 게 있네요. 파이썬을 공부하면서 필요한 요소가 하나하나 떠오릅니당 헤헤

이번에 Yield를 공부해서 업로드해야겠습니다.

[그림 01] Problem 010 문제


문제 설명


문제 이름 : 소수의 합

문제 내용 : 10 이하의 소수들의 합을 보면 2 + 3 + 5 + 7로 17이 나옵니다. 그렇다면 2,000,000 미만의 소수들의 합은 무엇입니까?


우리는 조건이 매우 간단하다는 것을 알 수 있습니다. 저도 그렇게 생각했습니다.

그러나... 저는 이 간단함에 여유를 부리고 시간을 매우 소비할 뻔 했습니다.


1. 소수 리스트를 구해야 함

2. 구해진 소수 리스트를 통해 모든 합을 구함


이제 소스를 작성해보도록 합시다.

소수를 구하는 방법이 이 문제의 핵심이라고 생각됩니다.

먼저 제가 가장 빠르다고 생각했던 소수 구하는 방법으로 작성해보았습니다.


소스코드


- vol01

#Problem 010
#Python 3 version source

#Get the sum of all the primes below 2,000,000

def getPrime(Number):
	primeList = [2]
	num = 3
	count = 100000
	
	while(num < Number):
		for x in primeList:
			if num % x == 0:
				break
			elif x == primeList[-1]:
				primeList.append(num)
				break
		if num == count:
			print(primeList)
			count += 100000
		
		num += 1
		
	return primeList
	
if __name__ == "__main__":
	Main_primeList = getPrime(2000000)
	sum = 0
	
	for i in Main_primeList:
		sum += i
	
	print(sum)


간단하게 getPrime으로 작성해보았습니다. 지금까지 제가 애용하던 소수 구하기입니다.



count는 그냥 100,000씩 끊어서 체크해보기 위함입니다. 하하..

시간은 1시간 정도 걸리려나요..? 1분동안 돌려보았지만 400,000까지는 해보았습니다. 그 이상은 하지 못했지요.(너무 오래걸려서..)


저는 여기서 getPrime함수의 한계를 느꼈습니다. 

이제 진정한 에라스토테네스의 함수로 넘어가볼까요???


먼저 다음 소스를 확인해주시기 바랍니다.

#Sieve Test
import time

def sieve(Limit):
	prime = set()
	not_prime = set()

	for i in range(2, Limit+1):
		if i not in not_prime:
			prime.add(i)
			for j in range(i*i, Limit+1, i):
				not_prime.add(j)
				
	return prime

start_time = time.time()	
primeList = sieve(2000000)
print(len(primeList))
end_time = time.time()

print("Time : ", end_time - start_time)


위의 소스는 에라스토테네스의 체를 시험한 것입니다.

참고 URL : http://ledgku.tistory.com/61

위의 URL은 에라스토테네스의 정리를 프로그램적으로 잘 설명해주셨습니다.



시간은 엄청 짧습니다!!

제 태블릿, limit == 2,000,000을 기준으로 1.27초 가량 걸렸습니다.


그렇다면 저 함수를 이용하여 온전한 primeList를 가져오면 좋을 것 같습니다.


- vol 02

두번째 버전의 소스입니다.

#Problem 010
#Python 3 version source

def sieve(Limit):
	prime = set()
	not_prime = set()

	for i in range(2, Limit+1):
		if i not in not_prime:
			prime.add(i)
			for j in range(i*i, Limit+1, i):
				not_prime.add(j)
				
	return prime
	
if __name__ == "__main__":
	Main_primeList = sieve(2000000)
	sum = 0
	
	for i in Main_primeList:
		sum += i
	
	print(sum)

이 함수를 이용하면 2초 가량 걸립니다용. 이번에는 나름 뿌듯합니다.

물론 sieve라는 건 살짝 답안지를 볼까 하는 욕구에서 비롯된 것이지만, 힌트를 얻고 답안지를 안 봤으니 나름 스스로 토닥토닥해줄 수 있지용.


이제 제가 답안지라고 생각하는 소스를 참고해봅시다.

참고 URL : http://www.s-anand.net/euler.html

sieve = [True] * 2000000    # Sieve is faster for 2M primes

def mark(sieve, x):
    for i in range(x+x, len(sieve), x):
        sieve[i] = False

for x in range(2, int(len(sieve) ** 0.5) + 1):
    if sieve[x]: mark(sieve, x)
print(sum(i for i in range(2, len(sieve)) if sieve[i]))


sieve 전역 변수는 True로 2,000,000개를 초기화해놓은 배열입니다.

mark함수는 초기화된 sieve와 배수로 지정할 소수를 넘겨줍니다.

그리고 모두 종료되면 모든 합을 구합니다.


일단 대단하다고 느껴집니당. 대단하지만, 이제는 조금 경쟁심이 생기네요 *^^*


[그림 01] Problem 009 문제


문제 이름 : 특별한 피타고라스 정리

문제 내용 : 피타고라스 정리의 삼각형에서 세 개의 자연수에 대한 설정은 a < b < c이며, a^2 + b^2 = c^2입니다. 예를들면 3^2 + 4^2 = 9 + 16 = 25 = 5^2라고 할 수 있습니다. 그렇다면, a + b + c = 1000일 때 위의 정의에 맞는 a, b, c를 구하고 그의 곱인 a*b*c를 구하시오.


우리는 다음과 같은 조건이 있다는 것을 알 수 있습니다.

1. a < b < c가 성립되어야 함

2. a^2 + b^2 = c^2이 되어야 함

3. a + b + c = 1000이 되어야 함


무작정 소스코드를 작성함에 있어서 많은 어려움이 있습니다...

먼저 손으로 구해본 결과 어떻게 해야할지 몰라서... BruteForcing을 해야 하나 하며 고민한 결과 정말로 BruteForcing을 하기로 했습니다. ㅎㅎ

첫번째 버전의 소스는 다음과 같습니다.

#Problem 009
#Python 3 version source

#a + b + c = 1000

def findPythagorean(Limit):
	a = 3
	b = 4
	c = 5
	
	for i in range(a, Limit):
		for j in range(b, Limit):
			for k in range(c, Limit):
				if k**2 == (i**2 + j**2):
					print("A : ", i, " B : ", j, " C : ", k, "A+B+C == ", i+j+k)
					if i+j+k == 1000:
						print(i, " ", j, " ", k) 
						break



첫번째 버전의 소스로 하면 겁나 오래걸립니다. 나오기는 합니다만, 중복되는 계산이 많아서 꽤 걸립니다.


두번째 버전의 소스로 넘어가 보면 조건을 하나 더 걸어보았습니다.

#Problem 009
#Python 3 version source

#a + b + c = 1000

def findPythagorean(Limit):
	a = 3
	b = 4
	c = 5
	
	for i in range(a, Limit):
		for j in range(b, Limit):
			if j > i:
				for k in range(c, Limit):
					if k > j:
						if k**2 == (i**2 + j**2):
							print("A : ", i, " B : ", j, " C : ", k, "A+B+C == ", i+j+k)
							if i+j+k == 1000:
								print(i, " ", j, " ", k)
								break


두번째 버전의 소스는 a < b < c 의 조건을 성립시켜주었습니다. 사실 첫번째 소스를 작성할 때 이 조건을 깜빡하고 했습니다. ㅎㅎ

그러나 이렇게 해도 시간은 어어어엄청 걸립니다. a + b + c 의 값이 1000이 되지 않으면 무한 반복이지요...(정확히는 a가 1000이 될 때까지 계속 반복됩니다.)

그렇다면 우리는 시간을 대폭 줄일 필요가 있습니다.


세번째 소스로 보겠습니다.

#Problem 009
#Python 3 version source

#a + b + c = 1000

def findPythagorean(Limit):
	half = Limit / 2
	a = 0
	b = 0
	c = 0
	
	while(a < half):
		a += 1
		b = half - a
		
		while(b < half):
			c = Limit - (a + b)
			if c < (a+b) and a**2 + b**2 == c**2:
				print("A : ", a, " B : ", b, " C : ", c, " A*B*C : ", a*b*c)
				b = Limit + 1
				a = b
				
			b += 1

if __name__ == "__main__":
	Main_LimitNumber = 1000
	
	findPythagorean(Main_LimitNumber)
		


반복구간을 결정할 필요가 있습니다.

먼저 a, b, c를 선언하고 초기화 해주고 다음으로 넘어갑시다.

a의 검색 구간을 전체의 반으로 줄여서 1부터 500으로 만들어서 그 반복을 설정합니다.

또한 b는 설정구간을 (절반 - a)로 지정해줍니다.

그 다음 while문으로 넘어가 b가 절반보다 작을 때 조건문을 실행합니다.


<조건문>

1. 삼각형의 기본적인 조건인 가장 큰 변이 두 변의 합보다 작아야 함.

2. 주어진 조건인 a^2 + b^2 = c^2


위의 두 조건이 만족할 때 a, b, c를 각각 출력합니다. 그리고 적절하게 빠져나갈 수 있도록 길을 만들어놓습니다.

만약 위의 조건문이 실행되지 않을 시 b += 1을 실행해주고, while문을 반복합니다.


이렇게 되면 a의 범위 b의 범위 c의 범위 1000 * 1000 * 1000이 아닌 (500 * 501) / 2 * 500으로 빠르게 해결할 수 있습니다.

물론 그 사이사이의 조건 계산과 값 계산을 뺀다면 더욱 빠르게 할 수 있다는 것을 의미합니다.

1초에 할 것을 0.062초만에 하는 것입니다.


저보다 겁나 뛰어난 분의 소스를 가져와보도록 합시다.

for a in range(1, 1000):
     for b in range(a, 1000):
         c = 1000 - a - b
         if c > 0:
             if c*c == a*a + b*b:
                 print(a*b*c)
                 break


겁나게 굉장합니다.

범위를 절반이 아닌 전체로한 것 이외에 조건은 똑같습니다.... 우왕...



Visual Basic Script의 문법을 설명하기 앞서, vbs에서는 마땅한 print 함수, 출력 함수가 없습니다.

그래서 선언으로 대신 설명하고, 만약 print를 하고 싶다면 msgbox(Message Box)로 대신 출력해도 됩니다.

msgbox("내용", 버튼종류, "제목") 과 같은 방법으로 작성하면 됩니다.


01. 변수


변수 선언하는 법

 - 대소문자 구분 안 함(변수 외에도 여러가지 선언 시 SELECT CASE 나 select case나 같이 적용됩니다.)

 - 문자로 시작해야 문자, 숫자, 언더바( _ )만 가능

 - 255자 이내로 선언

 - dim 사용 혹은 사용하지 않고 사용 가능


파이썬과 VBS의 차이점


Python :

Value01 = "String01"
Value02 = 100
Value03 = list()

Visual Basic Script :

Value01 = "String"
Value02 = 100
Dim Value03(2) 'REM dim Value03(2)는 Value03(0) ~ Value03(2)까지 총 세 변수입니다.


원래는 dim을 이용하여 변수를 설정하는 것이 정석이지만, 사용하지 않고 일반적으로 사용할 수 있다고 합니다.

또한, dim은 보통 배열을 선언할 때 사용합니다.

배열 선언을 다시 알아보도록 해봅시다.


일반적인 배열은 다음과 같습니다.

'REM dim array(2) 는 정적으로 배열을 선언한 것입니다.
Dim array(2)

array(0) = 0
array(1) = 1
array(2) = 2

'REM dim arr()는 동적으로 배열을 선언한 것입니다.
Dim arr()

'REM 이렇게 배열을 다시 선언하면 배열에 있던 값이 모두 삭제된 상태로 다시 선언 됩니다.
ReDim arr(2) 

'REM 이렇게 배열을 다시 선언하면 배열에 있던 값은 모두 유지된 채로 다시 선언 됩니다.
ReDim Preserve arr(2)


참고로 REM은 Remark라고 하여 주석처리와 같습니다.


02. 연산자


비교 연산자

 연산자

 설명

=

같다 

<>

같지 않다

<

작다

>

크다

<=

작거나 같다

>=

크거나 같다

is

두 변수의 객체가 같다


논리 연산자

연산자

설명 

not

참일 경우 거짓, 거짓일 경우 참 

and

둘 다 참일 경우만 참 

or

둘 중의 하나라도 참이면 참 

xor

서로 다를 경우만 참 

eqv

서로 같을 경우만 참 


산술 연산자

연산자

설명

^

자수

+

덧셈

-

뺄셈

*

곱셈

/

나눗셈

mod

나머지

\

정수 나눗셈

&

문자열 연결


03. 조건문


select case 문

이 select case문은 C언어나 JAVA와 같은 고급언어의 switch case문과 동일합니다.

사용방법도 거의 동일합니다. Python도 같이 비교해보고 싶어서 같이 비교해보도록 하겠습니다.


파이썬과 C와 VBS의 차이점


Python :

switch_map{
"apple":1,
"banana":2,
"tomato":3
}

print switch_map['apple']
print switch_map['banana']

C :

switch(num)
{
   case 1:
       printf("num01");
       break;
   case 2:
       printf("num02");
       break;
   default:
       printf("not num01 ~ num02");
       break;
}

Visual Basic Script :

Select Case number
   Case 1
       name = "First"
   Case 2
       name = "Second"
   Case 3
       name = "Last"
End Select


if 문

if문은 아주 간단한 형태로 되어 있습니다.

하지만 시작 부분과 끝부분은 지정해줘야 함이 Python과는 다르고, 형태도 마치 C의 전처리기와 비슷한 형태로 되어 있습니다.


파이썬과 C와 VBS의 차이점


Python :

if number == 0:
   print "a"
elif number == 1:
   print "b"
else:
   print "else"

C :

if(number == 0)
{
   printf("a");
}
else if(number == 1)
{
   printf("b");
}
else
{
   printf("else");
}

Visual Basic Script :

If number = 1 Then
   name = "a"
ElseIf number = 2 Then
   name = "b"
Else
   name = "c"
End If


04. 반복문


반복문은 두 가지로 나뉩니다.

do loop문과 for문으로 구분되는데, do loop는 do while과 동일합니다.

그리고 for문은 굉장히 차이가 있습니다.


do loop문


Python :

# Python에서는 do while은 없습니다.
# while은 있습니다.
while(1):
   print "a"
   if count == 100:
       break
   count += 1

while True:
   print "b"
   if count == 100:
       break
   count += 1

C :

do
{
   printf("a");
   count ++;
}while(count < 100)

while(count < 100)
{
   printf("b");
   count++;
}

Visual Basic Script :

Do While count < 100   'REM 100보다 작으면 계속해서 실행합니다.
   count = count + 1
Loop

Do Until count > 100   'REM 100보다 커질 때까지 계속해서 실행합니다.
   count = count + 1
Loop

Do
   'REM 실행 코드
Loop

do loop는 이렇게 세 가지로 구분되어 있습니다.

이제 for문을 살펴보도록 합시다.


for 문


Python :

for i in range(10):
   print i #0 1 2 3 4 5 6 7 8 9

for i in range(3, 10):
   print i   #3 4 5 6 7 8 9

a = ['a','b','c','d','e']
for i in a:
   print i   #a b c d e

C :

int i = 0;
for(i=0;i<10;i++)
{
   printf(" %d",i);
}

Visual Basic Script :

For i = 0 To 10 (step num)   'REM step num은 증가할 값이며 음수로도 지정 가능합니다. 만약 생략할 시 1씩 증가하도록 되어 있습니다.
   num = num + 1
Next

For number As double = 2 To 0 step -0.25
   [실행 코드]
Next

For index As integer = 1 To 5
   [실행 코드]
Next


05. 함수 호출


함수 호출은 Visual Basic Script에서는 두 가지로 구분되어 있습니다. 

서브함수와 함수가 있습니다. 서브루틴, 함수 두 가지로 구분하기도 합니다.

그렇다면 이 두 가지를 구분해보도록 하겠습니다.

여기서는 Visual Basic Script 위주로 설명해보겠습니다.


서브루틴

Sub subname(num)
   num = num + 1
End Sub

Call subname(3)

'REM 다른 방법의 sub루틴 선언=================
Sub ConvertTemp()
   temp = InputBox("화씨 온도를 입력하십시오.", 1)
   MsgBox "섭씨 " & Celsius(temp) & "도 입니다."
End Sub


함수

Function Celsius(fDegrees)
   Celsius = (fDegrees - 32) * 5 / 9
End Function



Python으로 Windows OS의 프로세스를 체크하는 소스입니다.

먼저 소스를 봅시다.


import os, sys, time
from win32com.client import GetObject

WMI = GetObject('winmgmts:')

ProcessList = []

processes = WMI.instancesOf('Win32_Process')

for process in processes:
	 ProcessList.append(process.Properties_('Name').Value)

print ProcessList


소스에서 os, sys, time 이외의 win32com.client라는 패키지와 GetObject라는 모듈이 있습니다.

win32com.client는 Python 모듈을 따로 받아줘야 합니다.


win32com.client 다운로드 링크(Source Forge) : https://sourceforge.net/projects/pywin32/files/?source=navbar


각설하고, 소스를 설명하겠습니다.


WMI에는 GetObject('winmgmts:')를 선언합니다.

processes라는 변수에는 WMI.instancesOf('Win32_Process')를 통해 프로세스에 대한 데이터를 모두 긁어옵니다.

그리고 for 문에서는 processes에서 있는 정보를 하나씩 가져와서 Properties_('Name').Value를 통해 하나씩 ProcessList에 append 하도록 만들었습니다.

이후 ProcessList를 통해 확인합니다.


<<참고>>


GetObject()함수는 VB(Visual Basic)의 GetObject() 함수와 동일한 기능을 한다고 합니다.

GetObject(Class = "ProgramID") 혹은 GetObject(Class = clsid)로 쓰면, 이미 실행하고 있는 COM object에 연결합니다.

COM(Component Object Model) object란 마이크로소프트가 개발한 소프트웨어 구성요소들의 응용프로그램 이진 인터페이스를 말합니다.






Python 2.7.x로 작성한 shutdown.py입니다.


다음 소스는 랜덤한 시간으로 shutdown을 할 수 있도록 하는데, shutdown은 os.system 함수를 사용합니다.

아주 아주 간단한 소스입니다.

-r 옵션은 restart옵션이고, 만약 컴퓨터를 끄고 싶다면 -s를 사용하면 되겠습니다.


#python 2 version source
import os
import random

time = random.randrange(600,900)

os.system("shutdown -r -t %s" % str(time))


os.system을 통해 cmd 창에서 실행할 명령어를 작성하였습니다.

str(time)은 time을 랜덤값으로 600 ~ 900sec를 주어지도록 하였습니다.


'PYTHON > Python 2.x' 카테고리의 다른 글

Python3 Transposition Cipher Bruteforcing(전치 암호)  (0) 2017.04.04
Python [Windows] Process List Check  (2) 2017.01.15

[그림 01] Problem 008 문제


문제 이름 : 가장 큰 곱(product)

문제 내용 : 네 개의 가장 인접한 숫자들로 이루어진 천의 자리 가장 큰 곱은 9 * 9 * 8 * 9 = 5832입니다. 즉, 위의 숫자 나열 중에 9989라고 연속되어 있는 숫자가 있고, 이 숫자들의 곱이 4개의 숫자 중 가장 큰 것이라는 의미입니다. 그렇다면, 열 세 개의 숫자가 나열되어 있는 것들 중 가장 큰 곱을 가지고 있는 것은 무엇입니까?

위의 문제는 4개의 연속된 숫자 중 가장 큰 곱이 나올 숫자를 찾아보니 9989라는 숫자로 9 * 9 * 8 * 9가 되어 5832라고 하였습니다.

그렇다면 우리는 13개의 연속된 숫자 중 가장 큰 곱이 나올 숫자를 찾아 그 값을 출력해야 합니다.


우리는 다음과 같은 조건이 부여된 것입니다.

1. 13자리 자리씩 끊어서 가장 큰 곱이 나오는 것을 찾도록 해야 함

1. 13자리를 각각 곱해야 함



#Problem 008
#Python 3 version source
 
import operator
 
Number = """7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450"""
 
NumberList  = list(Number) #Number List
ProductDict = {}           #New dictionary type
position    = 0
num         = 1
 
while(position+13 < len(NumberList)):
  for i in range(position, position+13):
    num *= int(NumberList[i])
  ProductDict[num] = Number[position:position+13]
  num = 1
  position += 1
   
sorted_ProductDict = sorted(ProductDict.items(), key=operator.itemgetter(1)) #key 값으로 정렬
#sorted_ProductDict = sorted(ProductDict.items(), key=operator.itemgetter(0)) #value 값으로 정렬
print(sorted_ProductDict)


이제 소스코드를 작성해보도록 하겠습니다.

위와 같이 Number에 숫자를 모두 넣어주고 13칸 씩 이동하면서 각 숫자를 모두 더하여, 가장 큰 숫자를 프린트하도록 합니다.

ProductList는 13개의 숫자를 곱하여 나온 값을 하나하나 List로 저장하는 것이고, 이를 sort()함수로 정렬한 뒤, 가장 큰 숫자를 출력하도록 했습니다.

메모리적 이득을 보기 위해서는 set()을 쓰는 것도 괜찮은 방법이라고 생각합니다.


또한 소스에서 21번째 라인을 보시게 되면 key 값으로 정렬하지 않고 value 값으로도 정렬할 수 있습니다.


이제 저보다 겁나 잘하는 사람의 소스를 가져와서 비교해보도록 할 것입니다.

마음의 준비를 해야겠네요..


s = '7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450'
n = 0
for i in range(0, len(s)-4):
    p = 1
    for j in range(i,i+5):
        p = p * int(s[j])
    if p > n: n = p
 
print(n)



이 소스는 13개의 숫자가 아니라 5개의 숫자를 곱한 값을 구하는 공식입니다.

확실히 더 효율적으로 보입니다.

굳이 List에 저장할 필요 없이 더 크면 저장하고 크지 않으면 저장하지 않는 알고리즘이 더 메모리를 아낄 수 있는 것 같습니다.



[그림 01] Problem 007 문제


문제 이름 : 10001번째 소수

문제 내용 : 처음 6개의 소수가 2, 3, 5, 7, 11, 13이라고 할 때, 6번째 소수는 13이라는 걸 알 수 있습니다. 그렇다면 10001번째의 소수는 무엇입니까?


우리는 간단하게 다음과 같은 조건을 만족하는 소스를 작성해봅시다.

1. 소수를 구하는 함수

2. 소수 리스트의 길이가 10001이 될 때까지 반복

3. 마지막 소수를 출력


이제 소스를 작성해봅시다.





#Problem 007
#Python 3 version source

#By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13.
#What is the 10 001st prime number?

def getPrime():    #소수 구하는 함수
	primeList = [2]  #소수 첫번째 리스트
	num = 3          #두 번째 소수 비교하는 수
	
	while(len(primeList) < 10001):    #primeList가 10001번째보다 작으면 계속 반복
		for x in primeList:        
			if(num % x == 0):
				break
			elif(x == primeList[-1]):
				primeList.append(num)
				break
		num += 1
		
	return primeList

if __main__ = "__name__":
	Main_primeList = getPrime()
	
	print(Main_primeList[-1]) #마지막 소수 리스트의 숫자를 출력

getPrime 함수는 소수를 구하는 함수이고, 이 함수는 primeList의 길이가 10001번째가 아니면 계속 반복하여 구하는 구조입니다.

그리고 모두 구한 후, main에 있는 변수로 리턴해주고, 이 중 마지막 리스트를 출력합니다.


이제 저보다 잘하는 사람의 소스를 참고해봅시다.

두근두근...!!

#python 2 version source
import prime
print prime.prime(10000)

오... 신이시여... 이럴수가...

위의 소스는 prime이라는 모듈을 다운받아야 실행이 되는 것 같습니다.

저는 다운받는데 실패했습니다.

흑흑.... 그냥 저 위의 소스를 활용하렵니다.

+ Recent posts