외국의 블로그에 소프트엔지니어 채용 과정 중에 코딩 인터뷰에서 있을 법한 문제로 회자되는 "FizzBuzz" 문제가 있습니다. 


"FizzBuzz"문제는 


 - 1에서 100까지의 숫자를 출력하되, 

 - 만약 해당 숫자가 '3'으로 나누어지면 숫자 대신에 "Fizz"를 출력하고, 

 - 만약 해당 숫자가 '5'로 나누어지면 숫자 대신에 "Buzz"를 출력하며, 

 - 만약 해당 숫자가 '3'과 '5'로 모두 나누어지면 숫자 대신에 "FizzBuzz"를 출력


하는 코드는 짜라는 것입니다.  


1~100까지 하기에는 좀 길기 때문에 1~30까지의 숫자에 대해서만 FizzBuzz 문제를 풀어보면 아래의 Original -> FizzBuzz 변환 전/후 비교를 보시면 이해가 쉬울 것입니다. 




위의 FizzBuzz 문제를 푸는 방법이 여러가지가 있을 수 있을 텐데요, (1) if, elif, else 조건문 사용, (2) if, elif, else 조건문을 포함한 사용자 정의 함수, (3) 문자열 합치기 (concatenating strings) 의 세 가지 방법을 소개합니다. 



  (1) if, elif, else 조건문을 사용한 FizzBuzz 코딩



# (1) if, elif, and else

for num in range(1, 31):

    if num % 3 == 0 and num % 5 == 0:

        print('FizzBuzz')

    elif num % 3 == 0:

        print('Fizz')

    elif num % 5 == 0:

        print('Buzz')

    else:

        print(num)

 




  (2) if, elif, else 조건문과 사용자 정의 함수(UDF)을 사용한 FizzBuzz 코딩


사용자 정의 함수에 대해서는 조만간 포스팅 하겠습니다. ^^



# (2) User Defined Function

def fizzbuzz(num):

    if num % 3 == 0 and num % 5 == 0:

        return 'FizzBuzz'

    elif num % 3 == 0:

        return 'Fizz'

    elif num % 5 == 0:

        return 'Buzz'

    else:

        return str(num)

    

print("\n".join(fizzbuzz(num) for num in range(1, 31)))

 




  (3) 문자열 합치기(Concatenating Strings)을 이용한 FizzBuzz 코딩



# (3) Concatenating Strings

for num in range(1, 31):

    string = ""

    if num % 3 == 0:

        string = string + "Fizz"

    if num % 5 == 0:

        string = string + "Buzz"

    if num % 5 != 0 and num % 3 != 0:

        string = string + str(num)

    print(string)

 



아래 링크는 취업 인터뷰에서 FizzBuzz 코딩 문제를 듣고서 Tensorflow로 딥러닝 모형을 적용했다가 취업 실패했다는 (가상의?) 블로그 내용입니다.  


Fizz Buzz in Tensorflow (http://joelgrus.com/2016/05/23/fizz-buzz-in-tensorflow/)


간단한 '주먹지르기'로 해결할 수 있는 문제를 단지 '최신 유행'하는 기술이라는 이유만으로  '공중 3회전 날아 뒤돌아 발 차고 손목날로 목 가로지르기' 기술을 적용할 필요가 있는가에 대해서 우스개 (가상?) 사례로 소개한 포스팅인 듯 합니다.  Interviewer 의 반응을 유심히 보면서... 인터뷰 과정을 한번 보시는 것도 재미있을 것 같습니다. ㅋㅋ


많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾸욱 눌러주세요. 



Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅은 페이스북의 R User Group 에서 아이디어 토론 주제로 올라왔길레 한번 짜봤던 코드입니다.  


'0'과 '1'로 가지는 긴 벡터를 처음부터 끝까지 쭉 훓어가면서 


(1) '0'이 연속으로 3번 나오면 그 구간을 기준으로 나누어서 

=> (2) 나누어진 구간을 새로운 벡터로 생성하기


입니다.  


'0'이 연속으로 나오는 회수는 분석가가 필요로 하는 회수로 지정할 수 있도록 매개변수(argument)로 지정해서 프로그래밍해보겠습니다. 


간단한 예제 벡터로서, '0'과 '1'을 원소로 해서 30개의 무작위수(이항분포 랜덤 샘플링, 0이 80%, 1이 20%)로 구성된 벡터를 생성해보겠습니다. 


 

> rm(list=ls()) # clean all


# Sample vector


> set.seed(123) # for reproducibility

> vec_raw <- sample(c(0, 1), size=30, replace=TRUE, prob=(c(0.8, 0.2)))

> vec_raw

 [1] 0 0 0 1 1 0 0 1 0 0 1 0 0 0 0 1 0 0 0 1 1 0 0 1 0 0 0 0 0 0

> vec <- vec_raw # copy for manipulation




R 프로그래밍을 하고자 하는 일의 아웃풋 이미지는 아래와 같습니다. 

'0'이 연속 세번 나오는 구간을 노란색으로 표시를 했습니다. 이 구간을 기준으로 원래의 벡터를 구분해서 나눈 후에(split), 각각을 새로운 벡터로 생성하여 분리해주는 프로그램을 짜보는 것이 이번 포스팅의 주제입니다. 




아래에 wihle() 반복문, if(), else if() 조건문, assign() 함수를 사용해서 위의 요건을 수행하는 코드를 짜보았습니다. 원래 벡터에서 제일 왼쪽에 있는 숫자가 '0'이 아니면 하나씩 빼서 빈 벡터에 차곡차곡 쌓아 놓구요, '0'이 3개 나란히 있는 '000'이 나타나면 그동안 쌓아두었던 벡터를 잘라내서 다른 이름의 벡터로 저장하고, '000'도 그 다음 이름의 벡터로 저장하면서 원래 벡터에서 '000'을 빼놓도록 하는 반복문입니다. assign() 함수는 저장하는 객체의 이름에 paste()를 사용해서 변화하는 숫자를 붙여주고자 할 때 사용하는 함수인데요, 자세한 사용법 예제는 여기(=> http://rfriend.tistory.com/108)를 참고하세요. 


아래 프로그램에서 'bin_range'에 할당하는 숫자를 변경하면 원하는 회수만큼 '0'이 반복되었을 때 구간을 분할하도록 조정할 수 있습니다.  가령, '0'이 연속 10번 나오면 분할하고 싶으면 bin_range <- 10  이라고 할당해주면 됩니다. 



##---------------------------------------------

# Vector Bin Split by successive '0's criteria

##---------------------------------------------


# Setting

vec_tmp <- c() # null vector

bin_range <- 3 # successive '0's criterion

vec_idx <- 1

vec_num <- 1


# Zero_Bin_Splitter

while (length(vec) > 0){

  if (sum(vec[1:bin_range], na.rm=T) != 0){

    vec_tmp[vec_idx] <- vec[1]

    vec <- vec[-1]

    vec_idx <- vec_idx + 1

  } else if (is.null(vec_tmp)){

    assign(paste0("vec_split_", vec_num), vec[1:bin_range])

    vec <- vec[-(1:bin_range)]

    vec_idx <- 1 # initialization

    vec_num <- vec_num + 1

  } else {

    assign(paste0("vec_split_", vec_num), vec_tmp)

    vec_tmp <- c() # initialization

    vec_num <- vec_num + 1

    assign(paste0("vec_split_", vec_num), vec[1:bin_range])

    vec <- vec[-(1:bin_range)]

    vec_idx <- 1 # initialization

    vec_num <- vec_num + 1

  }

}


# delete temp vector and arguments

rm(vec, vec_tmp, bin_range, vec_idx, vec_num)




아래는 위의 프로그램을 실행시켰을 때의 결과입니다.  원래 의도했던대로 잘 수행되었네요. 




=====================================================

아래의 코드는 페이스북 R User Group의 회원이신 June Young Lee 님께서 data.table 라이브러리를 사용해서 짜신 것입니다.  코드도 간결할 뿐만 아니라, 위에 제가 짠 코드보다 월등히 빠른 실행속도를 자랑합니다. 대용량 데이터를 빠른 속도로 조작, 처리하려면 data.table 만한게 없지요. 아래 코드는 저도 공부하려고 옮겨 놓았습니다. 


똑같은 일을 하는데 있어서도 누가 어떤 로직으로 무슨 패키지, 함수를 사용해서 짜느냐에 따라서 복잡도나 연산속도가 이렇게 크게 차이가 날 수 있구나 하는 좋은 예제가 될 것 같습니다. 이런게 프로그래밍의 묘미이겠지요? ^^  June Young Lee 님께 엄지척! ^^b



# by June Young Lee


library(data.table)


# 0이 세번 연속으로 들어 있는 아무 벡터 생성

vec <- c(0,0,0,3,10,2,3,0,4,0,0,0,1,2,50,4,0,0,32,1,0,0,0,1,1)


# data.table함수인 rleid이용하여 연속인 행들을 구분하는 idx1 컬럼 생성 

dt <- data.table(v1=vec, idx1=rleid(vec))


# idx1 기준으로 갯수를 세어 N 컬럼 생성 

dt[,N:=.N, by=idx1]


# 0이 세번 연속으로 나온 그룹(=N컬럼변수가 3인)을 idx2로 체크하기 

dt[v1==0&N==3,idx2:=1L]


# split 하기 위한 기준 벡터를 마찬가지로 rleid함수를 이용하여 idx3로 생성


dt[,idx3:=rleid(idx2)]


# data.table의 split 함수 적용하고, lapply 적용하여 v1 칼럼만 추출 

res <- lapply(split(dt, by="idx3"), function(x) x[,v1])

res

# $`1`

# [1] 0 0 0

# $`2`

# [1] 3 10 2 3 0 4

# $`3`

# [1] 0 0 0

# $`4`

# [1] 1 2 50 4 0 0 32 1

# $`5`

# [1] 0 0 0

# $`6`

# [1] 1 1


# 0이 3번 연속으로 나온 놈들을 남겨두도록 작성한 이유는, 

# 위치확인 & 확인용입니다. 

# 필요없으면, 아래 정도의 코드를 추가하면 되겠네요.


res2 <- res[!unlist(lapply(res, function(x) length(x)==3&sum(x)==0))]

res2

# $`2`

# [1] 3 10 2 3 0 4

# $`4`

# [1] 1 2 50 4 0 0 32 1

# $`6`

# [1] 1 1

 


많은 도움이 되었기를 바랍니다. 


이번 포스팅이 도움이 되셨다면 아래의 '공감~'를 눌러주세요. 



Posted by R Friend R_Friend

댓글을 달아 주세요

  1. karku 2018.02.13 13:43  댓글주소  수정/삭제  댓글쓰기

    늘 도움이 되는 글을 포스팅해주셔서 감사합니다.
    그런데 올려주신 코드를 입력해보면 'vec'객체가 발견되지 않는다는 오류문이 뜹니다.
    혹시 'vec_raw'를 'vec'로 수정하면 되는 건가요? 답글주시면 감사하겠습니다.

Python 으로 if 를 활용한 분기문이나 for, while 을 활용한 반복문 프로그래밍에 들어가기에 앞서서, if, for, while 프로그래밍의 기초 지식이 되는 블리언 형식에 대해서 알아보겠습니다. 


이번 포스팅은 어려운건 없구요, 그냥 편안하게 몸풀기 (머리 풀기?) 정도로 읽어보면 좋겠습니다. 

(not 논리 연산자가 살짝 헷갈리기는 합니다만, 전반적으로 매우 쉬움)


블리언 형식(Boolean type)참(True), 거짓(False) 의 두 개 값을 가지는 자료형을 말합니다. 



# (1) Boolean Type

In [1]: a = 2 > 1


In [2]: a

Out[2]: True


In [3]: b = 2 < 1


In [4]: b

Out[4]: False


In [5]: type(a)

Out[5]: bool

 


불리언 값이 (1) 조건문, (2) 논리 연산자, (3) 비교 연산자에서 어떤 경우에 참(True)이고, 어떤 경우에 거짓(False) 인지에 대해서 짚고 넘어가 보겠습니다. 



[ 파이썬 참/거짓 불리언 형식 (Python Boolean Type ]





 (1) 조건문에서의 참, 거짓 (True, False in Conditional statements)


프로그래밍의 조건문에서 참, 거짓을 판단할 때 블리언 False, None, 숫자 0, 비어있는 리스트, 비어 있는 튜플, 비어있는 사전 자료형의 경우에 False 로 판단을 합니다. 


반대로 블리언 True, not None, 0이 아닌 숫자, 값이 있는 리스트, 값이 있는 튜플, 값이 있는 사전 자료형의 경우 True 로 판단을 하구요. 


자료형마다 값이 없이 비어있으면 거짓, 값을 가지고 있으면 참이라고 파이썬은 판단한다는게 재미있습니다. ^^


각 경우 마다 bool() 함수를 사용해서 파이썬이 참(True)과 거짓(False) 중에 어떻게 판단을 하는지 예를 들어서 살펴보겠습니다. 



# (1-1) Boolean False, True

In [6]: bool(False) # False

Out[6]: False


In [7]: bool(True) # True

Out[7]: True

 



# (1-2) None is False

In [8]: bool(None) # False

Out[8]: False

 



# (1-3) Number 0, 0.00 is False

In [9]: bool(0) # False

Out[9]: False


In [10]: bool(0.00) # False

Out[10]: False


In [11]: bool(5) # True

Out[11]: True

 



# (1-4) blank List [] is False

In [12]: bool([]) # False

Out[12]: False


In [13]: bool(['a', 'b']) # True

Out[13]: True

 



# (1-5) blank Tuple () is False

In [14]: bool(()) # False

Out[14]: False

In [15]: bool(('a', 'b')) # True

Out[15]: True

 



# (1-6) blank Dictionary {} is False

In [16]: bool({}) # False

Out[16]: False

In [17]: bool({'a': 'b'}) # True

Out[17]: True

 




  (2) 논리 연산자에서 참, 거짓 (True, False in Logical operators) : not, and, or


파이썬의 논리 연산자(Logical operators)에는 피연산자를 부정하는 not, 두 피연산자 간의 논리곱을 수행하는 and, 두 연산자 간의 논리합을 수행하는 or 의 3가지가 있습니다. 



(2-1) 피연산자를 부정하는 'not' 논리 연산자 


위에서 소개한 조건문에서의 참, 거짓에 not 연산자를 붙이면 참이 거짓으로 바뀌고, 거짓은 참으로 바뀌게 됩니다. 



# (2-1) 피연산자 부정 'not' logical operator


In [18]: not True # False

Out[18]: False


In [19]: not False # True

Out[19]: True

 



숫자 '0'은 거짓(False)라고 했으므로 앞에 부정 연산자 not 이 붙으면 참(True)이 됩니다. 반면, '0'이 아닌 숫자는 참(True)이라고 했으므로 앞에 not 이 붙으면 거짓(False)으로 바뀝니다. 



In [20]: not 0 # True

Out[20]: True


In [21]: not 5 # False

Out[21]: False

 



None 은 거짓(False)라고 했으므로 not None은 참(True)로 평가합니다. 



In [22]: not None # True

Out[22]: True

 



비어있는 문자열, 리스트, 튜플, 사전은 거짓(False)으로 평가한다고 했으므로, 피연산자를 부정하는 not 이 붙으면 참(True)으로 바뀌게 됩니다. 



In [23]: not '' # denial of blank String -> True

Out[23]: True


In [24]: not [] # denial of blank List -> True

Out[24]: True


In [25]: not () # denial of blank Tuple -> True

Out[25]: True


In [26]: not {} # denial of blank Dictionary -> True

Out[26]: True

 



반대로, 문자열, 리스트, 튜플, 사전에 값이 들어있는 경우 참(True)으로 평가한다고 했으므로, 피연산자를 부정하는 not이 붙으면 거짓(False)이 됩니다.  위와 아래의 비어있는 자료형에 not 붙인 경우와 값이 있는 자료형에 not 붙인 경우의 참, 거짓이 처음엔 좀 직관적으로 와닿지가 안던데요, 자꾸 보니깐 그러려니 하게 되네요. ^^'



In [27]: not 'ab' # False

Out[27]: False


In [28]: not ['a', 'b'] # False

Out[28]: False


In [29]: not ('a', 'b') # False

Out[29]: False


In [30]: not {'a' : 'b'} # False

Out[30]: False

 




(2-2) 두 피연산자 간의 논리곱을 수행하는 and 논리 연산자


두 개의 피연산자가 모두 참(True and True)이면 True 이며, 두 피연산자 값 중에서 하나라도 거짓(False)이 있으면 거짓(False) 으로 판단합니다. 



# (2-2) 논리곱 'and' logical operator

In [31]: True and True # True

Out[31]: True


In [32]: True and False # False

Out[32]: False


In [33]: False and True # False

Out[33]: False

 

In [34]: False and False # False

Out[34]: False





(2-3) 두 피연산자 간의 논리합을 수행하는 or 논리 연산자


두 피연산자 중에서 한 개(True or False, False or True)나 혹은 두개 모두(True or True) 이라도 참(True)이면 참으로 평가합니다. 



# (2-3) 논리합 'or' logical operator

In [35]: True or True # True

Out[35]: True


In [36]: True or False # True

Out[36]: True


In [37]: False or False # False

Out[37]: False

 




  (3) 비교 연산자에서의 참, 거짓 (True, False in Comparison operators) : ==, !=, >, >=, <, <=


(3-1) == 비교 연산자


두 피연산자의 값이 같으면 참(True), 서로 다르면 거짓(False)으로 평가



# (3) Comparison(Relational) operators


In [39]: a = 1; b = 2; c = 2 # input data



# (3-1) == : If the values of two operands are equal, then the condition becomes true


In [40]: a == b # False

Out[40]: False


In [41]: b == c # True

Out[41]: True

 



(3-2) != 비교 연산자


두 피연산자의 값이 같지 않으면 참(True), 같으면 거짓(False)으로 평가 (음... 좀 헷갈리지요. ^^;)



In [39]: a = 1; b = 2; c = 2 # input data

# (3-2) != : If values of two operands are not equal, then condition becomes true

In [42]: a != b # True

Out[42]: True


In [43]: b != c # False

Out[43]: False

 



(3-3) > 비교 연산자


: 왼쪽의 피연산자 값이 오른쪽의 피연산자 값보다 크면 참(True), 아니면 거짓(False)



In [39]: a = 1; b = 2; c = 2 # input data


# (3-3) > : If the value of left operand is greater than the value of right operand, 

# then condition becomes true


In [44]: a > b # False

Out[44]: False


In [45]: b > a # True

Out[45]: True


In [46]: b > c # False

Out[46]: False

 



(3-4) >= 비교 연산자


왼쪽의 피연산자 값이 오른쪽의 피연산자 값보다 크거나 같으면 참(True), 아니면 거짓(False)



In [39]: a = 1; b = 2; c = 2 # input data


# (3-4) >= : If the value of left operand is greater than or equal to the value of right operand, 

# then condition becomes true


In [47]: a >= b # False

Out[47]: False


In [48]: b >= a # True

Out[48]: True


In [49]: b >= c # True

Out[49]: True

 



(3-5) < 비교 연산자


왼쪽 피연산자의 값이 오른쪽 피연산자의 값보다 작으면 참(True), 아니면 거짓(False)



In [39]: a = 1; b = 2; c = 2 # input data

# (3-5) < : If the value of left operand is less than the value of right operand, 

# then condition becomes true


In [50]: a < b # True

Out[50]: True


In [51]: b < a # False

Out[51]: False


In [52]: b < c # False

Out[52]: False

 



(3-6) <= 비교 연산자


왼쪽 피연산자의 값이 오른쪽 피연산자의 값보다 작거나 같으면 참(True), 아니면 거짓(False)



In [39]: a = 1; b = 2; c = 2 # input data


# (3-6) <= : If the value of left operand is less than or equal to the value of right operand,
# then condition becomes true


In [53]: a <= b # True

Out[53]: True


In [54]: b <= a # False

Out[54]: False


In [55]: b <= c # True

Out[55]: True

 



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾸욱 눌러주세요. ^^


다음번 포스팅에서는 if ~ else 분기문(Branch statement)에 대해서 알아보겠습니다. 



Posted by R Friend R_Friend

댓글을 달아 주세요