제어문 2

제어문중 하나인 반복 제어문(반복문)을 배우는 글입니다.

반복문

우리가 컴퓨터로 곰 인형 눈알을 붙이는 코딩을 해본다고 가정해봅시다.

곰 인형 눈알을 붙이는 과정은 아래와 같은 과정이 될 것 입니다.

눈알을 2개 가져온다.
곰 인형 오른쪽 눈에 끼운다.
곰 인형 왼쪽 눈에 끼운다.

이게 곰 인형 1개에 눈알을 붙이는 과정입니다.

그러면 곰 인형 5개에 눈알을 붙이려면 어떻게 될까요?

눈알을 2개 가져온다.
곰 인형 오른쪽 눈에 끼운다.
곰 인형 왼쪽 눈에 끼운다.

눈알을 2개 가져온다.
곰 인형 오른쪽 눈에 끼운다.
곰 인형 왼쪽 눈에 끼운다.

눈알을 2개 가져온다.
곰 인형 오른쪽 눈에 끼운다.
곰 인형 왼쪽 눈에 끼운다.

눈알을 2개 가져온다.
곰 인형 오른쪽 눈에 끼운다.
곰 인형 왼쪽 눈에 끼운다.

눈알을 2개 가져온다.
곰 인형 오른쪽 눈에 끼운다.
곰 인형 왼쪽 눈에 끼운다.

이렇게 같은 내용을 5번이나 반복해서 적어야 합니다.

만약 곰 인형이 100개라면? 100개를 넘어서 100,000개가 된다면? 코드를 엄청 길게 작성해야 될 것입니다. 굉장히 비효율적이죠. 그래서 나온 것이 코드를 한 번만 짜도 100,000번을 돌릴 수 있는 반복문입니다.

for문

반복문 함수는 여러가지가 있지만 그중에서도 제일 많이 쓰이는 반복문은 for문 입니다.

for문은 아래와 같은 내용으로 쓰입니다.

for ... in ...:
    blah blah

상황에 따라 ... 부분이 달라질 수도 있습니다.

이번 글에서는 range를 기본적으로 사용하는 방법에 대해서만 배울 것입니다.

for 변수 이름 in range(숫자):
    blah blah

우선 (숫자)는 반복할 횟수를 정하는 값입니다.

변수 이름은 그동안 몇 번 반복했는지 값을 저장해주는 변수입니다. 변수의 저장 값은 0부터 시작하여 (숫자) - 1까지 저장됩니다.

변수의 저장 값이 0부터 (숫자) - 1까지 저장되는게 무슨 소리냐면 아래의 코드를 한 번 직접 작성하여 실행해봅시다.

for i in range(3):
    print(i)

이렇게 작성하고 실행해보면 출력 화면에서는 아래와 같이 나올 것 입니다.

0
1
2

이제 곰 인형 5개의 눈알을 끼워보는 과정을 for문을 사용하여 만들어보면 아래 코드와 같습니다.

for i in range(5):
    눈알을 2개 가져온다.
    곰 인형 오른쪽 눈에 끼운다.
    곰 인형 왼쪽 눈에 끼운다.

더 나아가서 n을 입력 받아 곰 인형 n개의 눈알을 끼운다면 코드가 어떻게 될까요?

n = int(input())
for i in range(n):
    눈알을 2개 가져온다.
    곰 인형 오른쪽 눈에 끼운다.
    곰 인형 왼쪽 눈에 끼운다.

위와 같이 n이 정수 값이기 때문에 오류가 나오지 않고 코드가 돌아가게 됩니다.

Q) 정수 값이라면 0이나 음수여도 돌아가는 것인가요?

A) 네. 하지만 0이하의 정수가 들어갈 경우 for문 안의 코드가 돌아가지 않고 다음 줄로 통과하게 됩니다.

이번엔 곰 머리가 5개가 있는데 각각 몸통, 팔 2개, 다리 2개를 끼우는 프로그램을 for문을 이용해서 만들어봅시다.

for i in range(5):
    곰 인형에 몸통을 끼움
    for j in range(2):
        곰 인형에 팔을 끼움
    for j in range(2):
        곰 인형에 다리를 끼움

이렇게 for문 안에 for문을 추가할 수도 있습니다. 이런 코드는 이중 for문이라고 부릅니다.

for i in range(100):
    for j in range(100):
        for k inr range(100):
            print(i, j, k)

위와 같이 총 3개의 for문이 중첩되어 있으면 삼중 for문이라고 부를 수 있습니다.

보통 for문에 사용하는 변수는 i, j, k를 사용하고 있으며 for문 한 개만 쓸 때는 i를, 이중 for문을 쓸 때는 i와 j를, 삼중 for문을 쓸 때는 i, j, k를 사용합니다.

range()에서 괄호 안의 값은 최대 3개의 값을 넣을 수 있습니다.

for i in range(start, end, step):

i = start에서 시작하여 i = end - 1에 끝나고, for문이 한 번 돌아갈 때마다 i += step을 해주는 방식입니다.

range안에 값을 1개만 넣을 경우

for i in range(end)

for i in range(0, end):

for i in range(0, end, 1):

이 3개의 코드는 전부 똑같은 행동을 합니다.

맨 아래 줄 range에 값을 3개 넣는 것이 원래 코드인데, 편의를 위해 값을 1개만 넣어도 동작할 수 있도록 설정했다고 생각하는 것이 편합니다.

range에 값을 1개 넣는 경우에는 만약 for문이 500번 반복된다면 for i in range(500):만 입력하면 되므로 이런 반복하는 기능만 사용할 때 제일 많이 쓰입니다.

range안에 값을 2개만 넣을 경우 아래 2개의 코드는 똑같은 행동을 합니다.

for i in range(start, end):

for i in range(start, end, 1):
for i in range(10, 100):

위 코드는 i = 10부터 시작하여 i = 100 - 1 = 99까지 총 90번 실행합니다.

마찬가지로 range에 값을 3개 넣는 것이 원래 코드인데 편의를 위해 값을 2개만 넣어도 동작할 수 있도록 설정했다고 생각하는 것이 편합니다.

아직까지는 값이 for문 루프를 한 번 돌아갈 때마다 1 증가하는 것이 기본 설정이라서 i = 2, 4, 8, 10, .. 과 같이 2씩 올라가거나 i = 10, 9, 8, 7, ...과 같이 값이 줄어들게 만들 수 없습니다.

여기서 드디어 세 번째 값인 step이 쓰입니다.

for i in range(start, end, step):
for i in range(10, 100, 10):

위와 같은 코드가 있으면 i = 10, 20, 30, ... , 80, 90 까지 총 9번 반복하게 됩니다.

이제 문제를 한 번 풀어보겠습니다.

자연수 N을 입력 받고 1부터 N까지 한 줄에 하나씩 출력하라고 합니다.

N은 int(input())으로 값을 받을 수 있고, 1부터 N을 출력하는 것은 for문을 이용하여 출력할 수 있습니다.

n = int(input())
for i in range(n):
    print(i+1)
n = int(input())
for i in range(1, n+1):
    print(i)

둘 다 전부 가능합니다. 이외에도 여러가지 방법으로 풀 수 있겠습니다.

멀티탭의 개수 N이 주어지고, 다음 N개의 줄에서 각각의 멀티탭이 몇 개의 플러그로 이루어져 있는지 입력 값을 받는다고 합니다. 이때 최대 몇 대의 컴퓨터를 연결할 수 있는지 구하는 문제입니다.

만약 플러그가 3개인 멀티탭이 1개만 주어진다면 컴퓨터는 3대를 연결할 수 있을 것이고, 플러그가 3개인 멀티탭 1개, 플러그가 2개인 멀티탭이 1개 주어진다면 컴퓨터는 (3-1)+2 = 4대를 연결할 수 있을 것입니다.

(3-1) + 2는 플러그가 3개인 멀티탭에서 1개의 플러그를 플러그가 2개 있는 멀티탭에 연결한 것입니다. 그 반대의 경우도 마찬가지로 3 + (2-1) = 4로 똑같겠지요.

이번엔 플러그가 4개인 멀티탭 2개, 플러그가 2개인 멀티탭 1개, 플러그가 1개인 멀티탭 2개가 있다면 (4-1) + (4-1) + (2-1) + (1-1) + 1 = 8대를 연결할 수 있을 것입니다.

(4-1) + (4-1) + (2-1) + (1-1) + 1은 위에 그림처럼 멀티탭에 연결하는 또 다른 멀티탭을 1개씩 연결한 것이라고 생각하면 됩니다.

다른 방식으로 멀티탭을 연결하면 값이 달라질까요?

이번엔 플러그가 4개인 멀티탭 1개에 모든 멀티탭을 연결해봅시다.

이렇게 되어도 답은 8인 것으로 보아 어떤 식으로 멀티탭을 연결해도 답은 변하지 않는 것 같습니다.

또 다른 입력 값들도 생각해보시면 규칙성이 하나 보이는데 답을 구하는 공식은 (플러그의 총 개수) - (멀티탭의 개수 - 1)이라는 것입니다.

위에 언급한 내용인 플러그가 3개인 멀티탭 1개, 플러그가 2개인 멀티탭이 1개가 입력 값으로 주어진다면 (4) - (2-1) = 3인 것으로 똑같다는 것을 알 수 있습니다. 그리고 마찬가지로 플러그가 4개인 멀티탭 2개, 플러그가 2개인 멀티탭 1개, 플러그가 1개인 멀티탭 2개가 입력 값으로 주어진다면 (12) - (5-1) = 8로 답이 똑같다는 것을 알 수 있습니다.

그러므로 이것은 for문을 통해서 플러그의 총 개수와 멀티탭의 개수를 구하여 답을 구할 수 있습니다.

n = int(input())
plug = 0
multitab = n
for i in range(n):
    x = int(input())
    plug += x
print(plug - (multitab - 1))
n = int(input())
plug = 0
for i in range(n):
    plug += int(input())
print(plug - (n - 1))

둘 다 완전히 같은 출력을 보여주는 코드입니다.

그런데 이렇게 제출하면 시간 초과가 나올 것입니다. 이것은 파이썬이 느린 함수이기 때문에 빠른 입력을 받아야하며 [입출력과 변수, 주석]에서 나왔던 빠른 입력을 사용하면 됩니다.

input = __import__('sys').stdin.readline
n = int(input())
plug = 0
multitab = n
for i in range(n):
    x = int(input())
    plug += x
print(plug - (multitab - 1))
input = __import__('sys').stdin.readline
n = int(input())
plug = 0
for i in range(n):
    plug += int(input())
print(plug - (n - 1))

좀 더 코드를 줄여보자면 아래와 같이 만들 수도 있습니다.

input = __import__('sys').stdin.readline
plug = 0
for i in range(int(input())):
    plug += int(input())
print(plug - i)

왜 이렇게 해도 답이 똑같은지 한 번 생각해보면 좋을 것 같네요.

풀어볼 문제

while문

while문은 if문과 for문을 합친 것이라고 생각하면 이해하기가 편합니다.

while ...:
    blah blah

while문의 특징은 ... 부분이 True일 때까지 코드가 돌아갑니다.

if ... : 일 때 ... 부분이 True일 때 if문 안의 코드가 돌아간다는 점이 공통점이며, ...이 거짓이 될 때까지 반복이 된다는 점이 for문과의 공통점이 있습니다.

그래서 for문과 똑같이 곰 인형 5개에 눈알을 끼우는 코드를 만들어보자면 아래와 같습니다.

x = 1
while x != 6:
    눈알을 2개 가져온다.
    곰 인형 오른쪽 눈에 끼운다.
    곰 인형 왼쪽 눈에 끼운다.
    x += 1

x = 1, 2, 3, 4, 5에서는 x != 6이 True이므로 while문 안의 코드가 돌아가다가 x = 6이 될 때 x != 6이라는 부분이 False가 되면서 while문을 빠져나오게 됩니다.

여기서는 for문이랑 다르게 저 x라고 칭한 변수를 i, j, k가 아닌 자유롭게 변수 이름을 설정하여 사용할 수 있습니다.

for문에서 step부분을 담당하는 부분도 while문에서는 자유롭게 x += 1, x -= 5 이렇게 설정해줄 수 있습니다.

x = 1
while x != 6:
    x += 1
    눈알을 2개 가져온다.
    곰 인형 오른쪽 눈에 끼운다.
    곰 인형 왼쪽 눈에 끼운다.
x = 1
while x != 6:
    눈알을 2개 가져온다.
    x += 1
    곰 인형 오른쪽 눈에 끼운다.
    곰 인형 왼쪽 눈에 끼운다.
x = 1
while x != 6:
    눈알을 2개 가져온다.
    곰 인형 오른쪽 눈에 끼운다.
    x += 1
    곰 인형 왼쪽 눈에 끼운다.
x = 1
while x != 6:
    눈알을 2개 가져온다.
    곰 인형 오른쪽 눈에 끼운다.
    곰 인형 왼쪽 눈에 끼운다.
    x += 1

이 코드는 전부 5번 돌아갑니다.

왜냐하면 while문안의 코드를 전부 끝마치고 나서야 다시 위로 올라가서 x != 6을 비교하기 때문입니다.

작동 과정을 화살표로 표시해보면

이렇게 while 옆에 있는 불리언 식을 포함한 채로 while문 코드 전체가 한 번씩 돌아가는 것 입니다.

while문의 특징이 while 옆에 있는 불리언 식이 True일 때만 while문이 돌아간다고 하였는데요. 이 특징을 활용하여 True만 계속 나오게해서 while문을 영원히 돌릴 수도 있습니다.

while True:
    blah blah

위 코드처럼 대놓고 True를 써서 무한으로 돌릴 수도 있고

x = 1
while x == 1:
    눈알을 2개 가져온다.
    곰 인형 오른쪽 눈에 끼운다.
    곰 인형 왼쪽 눈에 끼운다.

아니면 이렇게 while문을 만들어서 컴퓨터가 평생 곰 인형 눈알을 끼우게 할 수도 있습니다.

이 과정을 무한 루프라고 하는데요. 무한 루프는 제어문 3에서 배우는 기타 제어문인 break문과 같이 사용하는 편입니다.

이제 while문을 사용하여 문제를 풀어봅시다.

첫째 줄에 테스트케이스 수가 주어지고 T+1번째 줄까지 매 줄마다 모든 닭의 다리 수 합과 닭의 수가 주어진다고 할 때, 다리가 잘린 닭의 수와 멀쩡한 닭의 수를 구하는 문제입니다.

문제에서 다리가 모두 잘린 닭은 없으므로 (닭의 수)*2 - (닭의 다리 수의 합) = (다리가 잘린 닭의 수)가 될 수 있습니다. 그리고 (닭의 수) - (다리가 잘린 닭의 수) = (멀쩡한 닭의 수)가 될 수 있겠네요.

이 문제에서 while문을 사용하는 곳은 테스트케이스 수만큼 반복문을 돌아가게 만드는 것입니다. 우리는 while문 옆에 True가 되는 불리언 식이 나와야 while문이 돌아간다는 것을 배웠고, 불리언 식에서 0이 아닌 모든 숫자가 나오면 True가 될 수 있다는 내용을 제어문1에서 배운 적이 있습니다.

이것을 활용하여 t를 입력 받아서 while문이 돌아갈 때마다 t의 값을 1씩 빼면서 결국 t = 0이 될 때까지 while문을 t번 돌아가게 만들 수 있습니다.

t = int(input())
while t:
    t -= 1

이렇게 말이죠

아니면 t를 입력 받고 값이 0인 변수를 하나 설정해둔 다음, 그 변수를 1씩 더해서 (변수) == t가 될 때까지 while문을 돌리면 됩니다.

t = int(input())
x = 0
while x != t:
    x += 1

이제 위의 풀이과정을 적용해봅시다. 문제에서 모든 닭의 다리수를 N이라고 하고, 모든 닭의 수를 M이라고 했습니다.

우리는 변수 U에 다리가 잘린 닭의 수와 변수 T에 멀쩡한 닭의 다리 수를 만들면 됩니다. 그러면 공식은 아래처럼 나옵니다.

U = 2 * M - N
T = M - U

N과 M을 입력 받고 위에 있는 식을 그대로 while문에 넣으면 됩니다.

t = int(input())
while t:
    N, M = map(int,input().split())
    U = 2 * M - N
    T = M - U
    print(U, T)
    t -= 1
t = int(input())
x = 0
while x != t:
    x += 1
    N, M = map(int,input().split())
    U = 2 * M - N
    T = M - U
    print(U, T)

또 다른 문제를 풀어봅시다.

도미노 세트의 크기 N이 주어진다고 합니다. 이때 N은 도미노 한 칸에서 표현할 수 있는 점의 개수인데 도미노에는 총 두 칸이 있다고 합니다. 그래서 크기가 N인 도미노 세트에는 점이 총 몇 개가 있는지 구하는 문제입니다.

일단 while문 루프를 바로 끝내는 경우는 바로 찾을 수 있습니다. 윗 칸과 아래 칸에 들어있는 점의 개수가 n개이면 종료 시키는 것이죠.

a, b = 0, 0 #윗 칸, 아래 칸
while not(a == n and b == n):
    blah blah

그리고 이 문제의 그림을 보시면 절대 위 칸에 있는 점의 개수가 아래 칸에 있는 점의 개수보다 많은 적은 없습니다.

아래 칸의 점의 개수가 1개이면 위 칸에 올 수 있는 점의 개수는 0개, 1개이고, 아래 칸의 점의 개수가 2개이면 위 칸에 올 수 있는 점의 개수는 0개, 1개, 2개이고, .... , 아래 칸의 점의 개수가 k개이면 위 칸에 올 수 있는 점의 개수는 0개, 1개, ... , k개가 될 수 있습니다.

그러면 우리는 조건을 2개 세울 수 있습니다.

  1. 아래 칸의 점의 개수가 위 칸의 점의 개수와 같은 경우

  2. 아래 칸의 점의 개수가 위 칸의 점의 개수보다 많은 경우

1번의 경우에는 아래 칸의 점의 개수를 1개 추가하고, 위 칸의 점의 개수를 0개로 만들어줍니다.

2번의 경우에는 위 칸의 점의 개수를 1개 추가합니다.

이렇게해서 n = 2일 때로 살펴보자면(이때 편의상 아래 칸의 점의 개수를 a, 위 칸의 점의 개수를 b에 저장해두었다고 합시다.)

a, b = 0, 0이므로 조건 1을 적용해야 합니다. 그러므로 a, b = 1, 0이 됩니다.

a, b = 1, 0이므로 조건 2를 적용해야 합니다. 그러므로 a, b = 1, 1이 됩니다.

a, b = 1, 1이므로 조건 1을 적용해야 합니다. 그러므로 a, b = 2, 0이 됩니다.

a, b = 2, 0이므로 조건 2를 적용해야 합니다. 그러므로 a, b = 2, 1이 됩니다.

a, b = 2, 1이므로 조건 2를 적용해야 합니다. 그러므로 a, b = 2, 2가 됩니다.

a, b = 2, 2이므로 조건 2를 적용해야 하지만 not(a == 2 and b == 2)가 False이므로 while문을 탈출합니다.

조건은 if / else문으로 만들어주면 아래 코드와 같이 나오게 됩니다.

n = int(input())
a, b = 0, 0 #아래 칸, 윗 칸
while not(a == n and b == n):
    if a > b:
        b += 1
    else:
        a += 1
        b = 0

마지막으로 점의 개수를 세는 변수를 하나 추가하여 만들어주면 됩니다.

n = int(input())
a, b = 0, 0 #아래 칸, 윗 칸
ans = 0
while not(a == n and b == n):
    if a > b:
        b += 1
    else:
        a += 1
        b = 0
    ans += a + b
print(ans)

풀어볼 문제

Last updated