Random / Seed / Reproduction

5주차 1교시: 난수와 시드

사각형의 색과 막대의 높이를 무작위로 선택하고, 시드로 같은 이미지를 재현합니다.

세 줄의 종이 막대 배열에서 첫째와 셋째 줄의 높이 순서가 반복되는 시드 재현 개념 이미지
시드 재현의 개념 · 위에서 아래로 A → B → 다시 A

선택과 변화

핵심 개념
무작위 선택, 실행 순서, 후보와 범위, 시드와 재현
이전 학습
RGB, 사각형의 좌표, 리스트, for 반복문
학습 구성
8단계 · 선택과 비교, 원인 분석, 종합 설계
다음 학습
2교시의 조건문, 3교시의 포스터 제작

학습 목표

  1. 이미지 생성에 난수를 사용하는 이유를 설명한다.
  2. 색상 후보 목록과 choice()가 선택한 색을 구분한다.
  3. 색 선택 코드의 위치에 따라 선택 횟수가 어떻게 달라지는지 비교한다.
  4. randint()의 정수 범위가 막대 높이에 미치는 영향을 설명한다.
  5. 같은 코드와 시드로 막대 배열을 재현하고 결과를 비교한다.
  6. 같은 시드에서도 결과가 달라지는 원인을 분석한다.
  7. 원의 크기와 여백을 고려해 x좌표의 범위를 계산한다.
  8. 색과 높이를 함께 선택하는 막대 배열을 설계하고 생성 규칙을 설명한다.

학습 내용

시작 안내

각 단계는 개념 이해 → 전체 코드 실행 → 결과 확인 → 원리 설명으로 이어집니다. 색상 배열에서 선택을 익힌 뒤 막대의 높이와 원의 위치에 적용합니다. 각 실습에는 준비부터 출력까지 포함한 코드가 있어 해당 코드 하나를 새 Colab 셀에서 바로 실행할 수 있습니다. A·B 비교도 코드를 각각 제공합니다. 6단계에서 시드와 코드 변경의 영향을 분석하고, 8단계에서 색과 높이를 함께 설계합니다. 실습 번호는 주요 학습 단계의 번호와 같습니다.

1. 무작위 선택 이해하기

핵심 질문: 무엇을 사람이 정하고, 무엇을 프로그램이 선택할까요?

4주차에는 리스트에 저장한 색을 순서대로 사용하거나 인덱스로 색을 지정했습니다. 같은 코드를 다시 실행하면 같은 자리에 같은 색이 나타났습니다. 다른 색 조합을 만들려면 색을 지정한 값을 직접 수정해야 했습니다.

이번에는 우리가 색상 목록을 정하고, 프로그램은 그중에서 각 칸의 색을 무작위로 선택합니다. 아래는 같은 코드와 세 가지 색으로 만든 실행 예시입니다. 코드를 수정하지 않아도 실행할 때마다 색의 배열이 달라질 수 있습니다.

두 이미지에서 칸의 개수·위치·크기는 같고 색의 순서는 다릅니다. 두 번째 이미지에는 세 가지 색 중 두 가지만 나타났습니다. 다음 절에서는 같은 색이 반복되거나 일부 색이 선택되지 않는 이유를 설명합니다.

무작위 선택

난수는 무작위성을 나타내도록 생성한 수입니다. 이 수업에서 사용하는 Python의 random 모듈은 정해진 계산 절차로 난수를 생성합니다. 프로그램은 난수를 이용해 목록에서 색을 선택하거나, 정해진 범위에서 막대 높이에 사용할 정수를 선택합니다. 이렇게 일부 규칙을 유지하면서 색이나 높이를 바꾸는 것을 변주라고 합니다.

상자에 서로 다른 색의 카드 세 장이 있다고 가정합니다. 한 장을 뽑은 뒤 다시 넣으면, 다음에도 같은 색을 뽑을 수 있습니다. 프로그램도 매번 같은 세 가지 색 중에서 하나를 선택합니다. 따라서 색마다 선택되는 횟수가 다르거나, 한 번도 선택되지 않는 색이 있을 수 있습니다.

선택과 중복

첫 번째와 두 번째 선택 모두 세 색의 후보를 유지하며 청회색을 선택한 개념도
같은 후보에서 다시 선택위쪽은 선택 가능한 세 색, 아래쪽은 선택 결과입니다. 첫 번째에 청회색을 골라도 두 번째에 청회색을 다시 고를 수 있습니다.

색을 선택해도 원래 목록에서 그 색을 지우지 않습니다. 세 색이 각각 한 번씩 들어 있는 목록이라면, 매 선택에서 각 색이 선택될 확률은 같습니다. 그렇다고 여섯 칸에 각 색이 정확히 두 번씩 나타나는 것은 아닙니다.

‘무작위’는 매번 다른 값이 나온다는 뜻이 아닙니다. 같은 색이 이어지거나 어떤 색이 빠져도 정상입니다. 모든 색을 한 번씩 사용하는 배열과, 매번 세 후보 중 하나를 선택하는 배열은 서로 다른 규칙입니다.

고정과 변화

고정 요소는 실습에서 바꾸지 않는 값이나 설정입니다. 변화 요소는 프로그램이 무작위로 선택하는 값입니다. 색상 배열에서는 색만, 막대 배열에서는 높이만, 원의 위치 실습에서는 x좌표만 선택합니다.

예제별 고정 요소와 변화 요소
예제고정 요소무작위 선택 요소
색상 배열여섯 칸의 위치·크기, 배경, 색상 후보각 칸의 색
막대 배열막대 열두 개의 가로 위치·너비·아래쪽 좌표·색, 높이 범위각 막대의 높이
원의 위치원의 크기·색·y좌표, 여백과 x좌표 범위원의 x좌표

우리는 후보와 범위를 정하고, 프로그램은 그 안에서 값을 선택합니다. 선택할 수 있는 대상을 제한하면서 결과에 변화를 주는 방식을 통제된 우연이라고 합니다.

활동 1 · 확인 질문

첫 실행과 다음 실행에서 달라진 것 두 가지와 그대로인 것 두 가지를 적습니다. 두 번째 이미지에 세 후보 중 두 색만 나왔을 때 오류라고 할 수 있을까요?

예상과 해설 비교

색 순서와 색별 개수는 달라질 수 있습니다. 사각형의 위치·크기와 후보 목록은 같습니다. 후보에 있는 색이 반드시 한 번씩 나오는 규칙은 아니므로 두 색만 나와도 정상입니다.

실제 작가의 데이터 아트 사례

데이터 아트는 데이터를 재료로 삼아 이미지·소리·공간을 구성하는 예술적 접근입니다. 실제 작가들은 데이터를 어떤 시각적 형태로 변환할까요? 다음 두 작품에서 작가가 선택한 데이터와 화면을 생성하는 방식을 살펴봅니다.

사례 1. 료지 이케다, 〈data.tron〉, 2007

료지 이케다의 data.tron 작품 사진. 흑백 숫자가 화면을 촘촘하게 채워 깊이감 있는 패턴을 형성한다.
Ryoji Ikeda, 〈data.tron〉, 2007사진: Ryuichi Maruo · 제공: Yamaguchi Centre for Art and Media [YCAM] · 작가 공식 자료

료지 이케다는 〈data.tron〉에서 수학적 원리와 데이터를 결합해 영상의 각 픽셀을 계산합니다. 대형 화면에 투사된 숫자와 패턴은 관람자가 데이터의 규모와 밀도를 시각적으로 경험하게 합니다. 작가의 공식 설명은 이 작품을 수학적 계산에 기반한 시청각 설치로 소개합니다.

관찰할 점 숫자는 정보를 읽는 기호이면서, 반복과 명암을 구성하는 시각 요소이기도 합니다. 사진에서 개별 숫자와 전체 화면의 패턴을 번갈아 관찰해 봅니다.

사례 2. 레픽 아나돌, 〈Unsupervised〉, 2022, MoMA 전시

MoMA 로비에 설치된 레픽 아나돌의 Unsupervised. 대형 화면의 녹색과 청색 형태가 전시장 내부를 밝힌다.
Refik Anadol, 〈Unsupervised〉, MoMA 전시 전경전시 기간: 2022~2023 · 사진: Robert Gerhardt · MoMA 공식 사진 기록

레픽 아나돌은 MoMA 소장품 데이터를 학습한 인공지능 모델로 새로운 형태와 색채를 실시간으로 생성합니다. 전시장 안의 빛·움직임·소리와 외부 날씨도 변화하는 영상과 음향에 영향을 줍니다. 소장품 데이터는 학습의 재료가 되고, 전시장 환경은 작품의 변화에 관여하는 입력이 됩니다. MoMA 전시 설명에서 제작 방식과 기록 영상을 확인할 수 있습니다.

관찰할 점 어떤 데이터를 사용할지, 그 데이터가 화면에 어떻게 영향을 줄지를 작가가 설계합니다. 전시 기록 영상에서 시간에 따라 달라지는 색채와 형태를 관찰해 봅니다.

작품 감상에서 색상 선택 실습으로

두 작품에서는 데이터를 선택하고 처리하는 방식이 시각적 결과를 결정합니다. 다음 실습에서는 우리가 색상 후보를 정하고, 프로그램이 그중 하나를 무작위로 선택하도록 작성합니다. 사각형의 개수·위치·크기를 유지한 채, 후보 목록에 따라 어떤 색상 조합이 나타나는지 확인합니다.

2. 색상 후보에서 선택하기

핵심 질문: 선택 방법이 같아도 후보를 바꾸면 어떤 색이 나올까요? · 실습 2-A·2-B

3주차에는 이미지를 그리기 위해 Pillow를 불러왔습니다. 이번에는 무작위 선택에 필요한 random 모듈을 불러옵니다. 모듈은 관련 변수·함수 등을 정의한 코드의 단위입니다. import random을 실행하면 이 모듈의 기능을 사용할 수 있습니다. random은 Python에 포함되어 있어 별도로 설치할 필요가 없습니다.

함수는 특정 작업을 수행하는 코드 묶음입니다. 지금까지 사용한 print()는 값을 출력하는 함수입니다. 이번에는 random 모듈의 함수를 사용합니다. 색을 선택할 때는 함수의 괄호 안에 색상 목록을 지정하고, 정수를 선택할 때는 최솟값과 최댓값을 지정합니다.

실습 준비

Colab에서 새 노트북을 만들고 이름을 week05_period1_이름으로 바꿉니다. 각 실습의 전체 코드를 새 코드 셀에 입력하고 셀 전체를 실행합니다. 이전 셀을 실행하지 않아도 필요한 모듈·팔레트·캔버스가 모두 준비됩니다. ‘시연’은 개념을 숫자로 확인하는 짧은 예제입니다.

색상 배열

같은 크기의 사각형 여섯 개를 나란히 배치해 색을 비교합니다. 3주차에 배운 draw.rectangle()로 사각형을 그리고, 4주차에 배운 반복문으로 여섯 번 반복합니다. 사각형의 크기와 간격은 유지하고 색만 바꿉니다.

여백과 팔레트

캔버스는 660 × 220이고 배경색은 (245, 240, 228)입니다. 아래 세 가지 색을 후보로 사용합니다. 사용할 색을 모아 놓은 목록을 팔레트라고 합니다.

  • 청회색(37, 62, 78)낮은 명도의 청회색
  • 녹색(111, 143, 126)중간 명도의 녹색
  • 황토색(211, 155, 42)청회색과의 색상 대비

좌표와 반복

경계 상자는 도형이 들어갈 사각형 영역입니다. (x, 60, x + 80, 160)은 왼쪽·위·오른쪽·아래 좌표를 차례로 지정합니다. 가로 좌표의 차이는 80, 세로 좌표의 차이는 100입니다. x만 100씩 늘리면 같은 크기의 사각형을 오른쪽에 차례로 그릴 수 있습니다.

색상 배열 코드의 역할
코드역할
Image.new()빈 캔버스 생성
ImageDraw.Draw(image)캔버스에 연결한 그리기 도구 준비
range(6)i가 0부터 5까지 바뀌며 여섯 번 반복
x = 40 + i * 100왼쪽 x좌표를 40, 140, 240, 340, 440, 540으로 계산
fill=color변수 color에 저장한 색으로 사각형 채우기
display(image)완성된 이미지 출력

Image.new()는 반복문 앞에, display(image)는 반복문 뒤에 둡니다. 캔버스를 한 번 만들고, 사각형 여섯 개를 그린 뒤 이미지를 출력합니다.

색상 선택

palette[0]은 팔레트의 첫 번째 색을 지정합니다. random.choice(palette)는 팔레트에서 항목 하나를 무작위로 선택합니다. choice는 ‘선택’이라는 뜻입니다.

팔레트의 대괄호 [ ]는 여러 색을 리스트로 묶습니다. 각 색의 소괄호 (37, 62, 78)는 R·G·B 값을 하나의 튜플로 묶습니다. choice()는 리스트에서 색상 튜플 하나를 선택합니다.

색상 선택과 사용
코드역할
random.choice(palette)random 모듈의 choice 함수로 색상 후보 하나 선택
color =선택한 RGB 튜플을 변수에 저장
print(color)저장된 RGB 값을 텍스트로 출력
fill=color저장된 값을 도형의 채우기 색으로 사용

선택값의 사용

color = random.choice(palette)는 오른쪽의 함수를 먼저 실행하고, 선택한 값을 왼쪽 변수에 저장합니다. 아래는 녹색이 선택된 경우입니다. choice()가 직접 사각형을 그리는 것은 아닙니다.

  1. 01선택random.choice(palette)후보 중 RGB 튜플 하나
  2. 02저장color = (111, 143, 126)변수에 선택값 저장
  3. 03사용fill=color녹색으로 사각형 채우기

선택한 값은 다시 저장하기 전까지 유지됩니다. 같은 color를 두 번 출력하거나 여러 도형의 색으로 사용해도 그 과정에서 색을 새로 선택하지 않습니다. 색을 다시 선택하려면 choice()를 다시 실행해야 합니다.

시연: 선택과 출력

choice()를 실행할 때 색을 선택합니다. print(color)는 저장된 값을 읽어 출력하므로 두 번 출력해도 같은 값입니다. 교수자 시연에서 선택 한 번과 출력 두 번을 구분합니다.

실습 2-A · 새 셀에서 실행 · 전체 코드
import random

palette = [(37, 62, 78), (111, 143, 126), (211, 155, 42)]
color = random.choice(palette)
print(color)
print(color)

반복문 안의 선택

각 칸마다 색을 고르려면 color = random.choice(palette)를 반복문 안에 작성합니다. 매 반복에서 색 선택 → 변수에 저장 → 사각형 그리기 순서로 실행합니다. 반복문 밖에서 한 번만 선택하면 모든 칸에 그 색을 사용합니다. 반복문 안의 줄은 스페이스 네 칸을 들여씁니다.

후보와 선택 결과

팔레트에 색 세 가지를 넣어도 이미지에는 한두 색만 나올 수 있습니다. 각 칸의 색을 고를 때 같은 색을 다시 선택할 수 있기 때문입니다. 두 번의 실행에서 모두 세 색이 나왔더라도, 다음 실행에서도 반드시 세 색이 나오는 것은 아닙니다.

실습 2-A. 색상 선택

실습 2-A · 새 셀에서 실행 · 전체 코드

아래 코드를 그대로 실행합니다. 각 사각형을 그리기 전에 세 후보 중 색 하나를 선택합니다.

실습 2-A · 새 셀에서 실행 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

palette = [
    (37, 62, 78),
    (111, 143, 126),
    (211, 155, 42),
]

image = Image.new("RGB", (660, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)

for i in range(6):
    x = 40 + i * 100
    color = random.choice(palette)
    draw.rectangle((x, 60, x + 80, 160), fill=color)

display(image)

결과 확인 여섯 칸의 위치와 크기는 같습니다. 다시 실행하면 색의 배열은 달라질 수 있습니다. 같은 색이 반복되거나 일부 후보가 나오지 않아도 정상입니다.

팔레트 수정

choice()는 색을 선택하는 함수이고, palette는 선택할 색을 저장한 리스트입니다. 팔레트에서 황토색을 산호색으로 바꾸면 선택 가능한 색이 달라집니다. 이번 실습에서는 색상 후보 하나만 바꾸고, 선택 함수와 사각형의 위치·크기는 유지합니다.

바뀐 후보

새 팔레트에는 황토색이 없으므로 새 이미지에는 황토색이 나오지 않습니다. 산호색은 선택 가능한 색이지만 이번 실행에서 선택되지 않을 수도 있습니다. choice 함수는 그대로 두고 팔레트의 색만 바꾼 결과입니다.

실습 2-B. 색상 후보

실습 2-B · 새 셀에서 실행 · 전체 코드

황토색을 산호색으로 교체한 코드입니다. 선택 함수와 사각형의 위치·크기는 그대로입니다.

실습 2-B · 새 셀에서 실행 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

palette = [
    (37, 62, 78),
    (111, 143, 126),
    (201, 111, 93),
]

image = Image.new("RGB", (660, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)

for i in range(6):
    x = 40 + i * 100
    color = random.choice(palette)
    draw.rectangle((x, 60, x + 80, 160), fill=color)

display(image)

결과 확인 새 팔레트에 없는 황토색은 나오지 않습니다. 산호색은 후보에 있어도 이번 실행에서 선택되지 않을 수 있습니다.

팔레트 수정 전후의 실행 예시입니다. 실제 실행에서는 각 칸의 색을 다시 선택하므로 색 순서도 달라질 수 있습니다. 기존 이미지에서 황토색 부분만 교체하는 것이 아니라, 모든 칸의 색을 다시 선택해 새 이미지를 그립니다.

3. 선택 횟수 비교하기

핵심 질문: 여섯 번 그리는 것과 여섯 번 선택하는 것은 같을까요? · 실습 3

지금까지는 반복문 안에서 각 칸의 색을 선택했습니다. 이번에는 color = random.choice(palette)를 for문 앞으로 옮깁니다. 이 줄을 반복문 안에 쓰는지, 앞에 쓰는지에 따라 색을 선택하는 횟수가 달라집니다.

for문 앞의 줄은 반복을 시작하기 전에 한 번 실행됩니다. for문 아래에서 네 칸 들여쓴 줄은 반복할 때마다 실행됩니다. 따라서 사각형을 여섯 번 그려도 색은 한 번만 선택할 수 있습니다.

코드 위치와 실행 횟수
색을 선택하는 줄choice 실행사각형 그리기색의 사용
반복문 앞 · 들여쓰기 없음1번6번한 번 저장한 색을 모든 칸에 사용
반복문 안 · 네 칸 들여쓰기6번6번각 칸을 그리기 전에 색을 새로 선택

실행 순서

먼저 예상 · 선택 횟수와 사용 횟수

아래 A와 B의 실행 흐름을 보고 빈칸을 채웁니다. “A는 색을 ___번 선택하고 사각형을 ___번 그린다. B는 색을 ___번 선택하고 사각형을 ___번 그린다.” 이어서 B의 여섯 칸이 우연히 모두 같은 색이라면 선택 횟수도 한 번이라고 말할 수 있는지 생각합니다.

A · 반복문 앞

한 번 선택 → 여섯 번 사용

색 선택 1번 / 그리기 6번

123456

반복이 시작되기 전에 색을 저장합니다. 반복문은 저장된 색을 그대로 사용합니다.

B · 반복문 안

선택 → 그리기를 여섯 번 반복

색 선택 6번 / 그리기 6번

123456

매 반복에서 색을 새로 저장한 뒤 그립니다. 새로 선택해도 이전과 같은 색일 수 있습니다. 여섯 칸이 모두 같은 색이어도 선택 횟수는 여섯 번입니다.

그림의 숫자는 반복 순서입니다. B의 첫 반복에서는 i = 0으로 x를 40으로 계산한 뒤 색을 선택하고 첫 사각형을 그립니다. 다음 반복에서는 i = 1로 x를 140으로 계산하고 색을 다시 선택합니다. 이 과정을 i가 5일 때까지 반복합니다.

들여쓰기는 반복할 코드의 범위를 표시합니다. 색 선택 줄을 for문 앞으로 옮기면 반복 대상에서 빠집니다. 사각형을 그리는 줄은 그대로 반복문 안에 있으므로 그리는 횟수는 여섯 번으로 유지됩니다.

예상 확인: 이미지로 실행 횟수를 알 수 있을까?

A는 선택 1번·그리기 6번, B는 선택 6번·그리기 6번입니다. B에서 한 색만 보이더라도 choice는 여섯 번 실행됩니다. 결과 이미지가 같아 보여도 그 결과를 만드는 과정은 다를 수 있습니다. 실행 횟수는 색의 종류 수가 아니라 코드의 위치와 반복 횟수로 판단합니다.

실습 3. 선택 횟수

A와 B는 각각 새 셀에서 실행합니다. 사각형을 그리는 횟수는 같고, 색을 선택하는 줄의 위치만 다릅니다.

실습 3-A · 반복문 앞에서 선택 · 전체 코드

실행 전에 색 선택 횟수와 그리기 횟수를 예상합니다.

실습 3-A · 반복문 앞에서 선택 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

palette = [
    (37, 62, 78),
    (111, 143, 126),
    (201, 111, 93),
]

image = Image.new("RGB", (660, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)

color = random.choice(palette)
for i in range(6):
    x = 40 + i * 100
    draw.rectangle((x, 60, x + 80, 160), fill=color)

display(image)

결과 확인 색은 한 번 선택하고, 저장한 색으로 사각형 여섯 개를 그립니다.

실습 3-B · 반복문 안에서 선택 · 전체 코드

매 반복에서 색을 선택하는 전체 코드입니다.

실습 3-B · 반복문 안에서 선택 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

palette = [
    (37, 62, 78),
    (111, 143, 126),
    (201, 111, 93),
]

image = Image.new("RGB", (660, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)

for i in range(6):
    x = 40 + i * 100
    color = random.choice(palette)
    draw.rectangle((x, 60, x + 80, 160), fill=color)

display(image)

결과 확인 색 선택과 그리기가 각각 여섯 번입니다. 우연히 한 색만 보여도 선택 횟수는 여섯 번입니다.

A의 색과 B의 색 순서는 실행할 때마다 달라질 수 있습니다. 예시와 같은 색이 나올 필요는 없습니다.

4. 정수 범위로 높이 정하기

핵심 질문: 선택 가능한 정수 범위가 막대의 높이에 어떻게 나타날까요? · 실습 4

색을 선택할 때는 리스트를 사용했습니다. 막대의 높이를 정할 때는 최솟값과 최댓값 사이에서 정수 하나를 선택합니다. 정수는 20, 21처럼 소수 부분이 없는 수입니다.

random.randint(20, 140)은 20과 140을 포함한 정수 하나를 선택합니다. height = random.randint(20, 140)은 그 값을 변수 height에 저장합니다. height는 ‘높이’를 뜻합니다. 선택한 정수를 height에 저장한 뒤, 막대를 그릴 때 사용합니다.

범위와 선택값

작은 범위로 먼저 확인합니다. random.randint(3, 6)에서 3과 6은 선택 가능한 양 끝값입니다. 그 사이의 3·4·5·6 중 하나가 실행 결과로 나옵니다. 아래는 그중 5가 선택된 한 번의 실행을 나타냅니다.

01 입력선택 가능한 정수
random.randint(3, 6)최솟값 3부터 최댓값 6까지
  1. 3최솟값 포함
  2. 4선택 가능
  3. 5선택 가능
  4. 6최댓값 포함
02 실행한 번 실행 → 정수 하나 반환
03 결과이번에 선택된 값
5
number = 5변수에 저장해서 사용
다음 선택에서도 후보는 그대로
5가 선택된 경우를 보여 주는 개념도입니다. 후보에서 5를 지우지 않으므로 다음에도 5가 나올 수 있습니다. 3.5 같은 소수는 선택하지 않습니다.

괄호 안의 두 수는 선택 가능한 범위이고, 함수가 반환하는 수는 이번에 선택된 값입니다. 3~6에는 3, 4, 5, 6의 네 정수가 들어 있습니다. 20~140에도 양 끝을 포함한 정수만 들어 있으며, 범위 안의 정수를 모두 출력하는 것은 아닙니다.

코드 전체를 number = random.randint(3, 6)으로 적으면 오른쪽에서 정수 하나를 반환받아 왼쪽 변수 number에 저장합니다. 위 결과 카드의 number = 5는 5가 선택된 순간의 저장 상태를 보여 줍니다. 다음 실행의 결과까지 5로 고정한다는 뜻은 아닙니다.

먼저 예상 · 범위와 결과 구분

random.randint(3, 6)의 한 번 실행 결과로 3, 6, 3.5, 네 숫자 전체 중 무엇이 가능할까요? 직전에 5가 나왔다면 다음에 다시 5가 나올 수도 있을까요?

해설: 범위는 여러 후보, 반환값은 하나

3과 6 모두 가능합니다. 3.5는 정수가 아니므로 나오지 않고, 한 번의 randint 호출이 네 숫자를 모두 반환하지도 않습니다. 다음 호출에서도 후보는 3·4·5·6이므로 다시 5를 선택할 수 있습니다. 그림의 카드는 정수 후보를 설명하는 비유이며, 코드에서 이 네 값을 리스트로 직접 만들 필요는 없습니다.

randint()는 선택한 숫자의 용도를 정하지 않습니다. 같은 수를 height에 저장하면 막대 높이 계산에, x에 저장하면 가로 위치 계산에 사용할 수 있습니다. 선택한 숫자가 높이와 위치 중 무엇을 결정하는지는 해당 값을 사용하는 그리기 코드에 따라 달라집니다.

range와 randint

끝값의 포함 여부
코드제공하는 값역할
range(3, 6)3, 4, 5를 순서대로 제공반복 순서, 끝값 6 제외
random.randint(3, 6)3, 4, 5, 6 중 하나 선택정수 하나의 선택, 끝값 6 포함

시연: 정수 출력

교수자 시연에서 정수 선택 한 번과 출력 두 번을 구분합니다. 두 줄에는 같은 수가 출력됩니다. 셀 전체를 다시 실행하면 20 이상 140 이하의 정수를 새로 선택합니다.

먼저 예상 · 범위와 결과 구분
import random

height = random.randint(20, 140)
print(height)
print(height)

막대의 좌표

막대의 높이를 비교할 수 있도록 아래쪽 끝을 같은 높이에 맞춥니다. 색상 배열과 같은 draw.rectangle()을 사용하되, 아래쪽 y좌표를 180으로 고정하고 위쪽 y좌표를 180 - height로 계산합니다.

3주차에 배운 것처럼 이미지의 y좌표는 아래로 갈수록 커집니다. 높이가 커질수록 위쪽 y좌표는 작아져 막대가 위로 길어집니다. height는 막대의 위쪽과 아래쪽 y좌표 차이를 뜻합니다.

height = 40위쪽 y = 140
180 − 40 = 140 아래쪽 180
height = 120위쪽 y = 60
180 − 120 = 60 아래쪽 180
막대 배열 코드의 역할
코드역할
range(12)막대 열두 개 그리기
x = 40 + i * 50왼쪽 x좌표를 40부터 50 간격으로 계산
(x, 180 - height, x + 30, 180)가로 좌표 차이는 30, 세로 좌표 차이는 height
fill=(37, 62, 78)높이의 차이를 비교하도록 모든 막대의 색 고정

다음 실습의 두 코드는 모두 청회색 막대를 그립니다. 개수·가로 위치·너비·아래쪽 좌표를 고정하고, height를 선택하는 범위의 차이를 비교합니다.

높이와 좌표 계산

  1. 01높이 선택height = 12020~140에서 선택된 예시
  2. 02위쪽 좌표 계산180 - 120 = 60아래쪽 좌표에서 높이 빼기
  3. 03막대 그리기(x, 60, x + 30, 180)위쪽 60 / 아래쪽 180

height를 위쪽 y좌표로 직접 지정하면 값이 커질수록 막대의 위쪽 끝이 아래로 이동합니다. 아래쪽을 180으로 유지한 채 막대를 더 길게 만들려면, 180에서 height를 빼서 위쪽 y좌표를 계산해야 합니다.

높이 범위를 100~140으로 좁히면 높이 100 미만의 막대는 생성되지 않습니다. 선택 가능한 최댓값은 140으로 유지되고 최솟값은 100으로 증가합니다. 높이 범위는 막대 사이에 나타날 수 있는 높이 차이를 제한합니다.

범위와 높이 차이

100~140에서는 높이가 100보다 작은 막대가 나오지 않습니다. 두 막대의 높이 차이는 20~140 범위에서 최대 120, 100~140 범위에서 최대 40입니다. 다만 최솟값이나 최댓값이 한 번도 선택되지 않을 수 있습니다. 넓은 범위에서도 비슷한 높이만 나올 수 있으므로, 실제 이미지와 코드에 지정한 범위를 함께 확인해야 합니다.

실습 4. 높이 범위

막대 열두 개의 색·너비·간격·아래쪽 좌표는 고정합니다. A와 B를 각각 실행해 최솟값의 차이가 화면에 어떻게 나타나는지 비교합니다.

실습 4-A · 넓은 범위 20~140 · 전체 코드

20 이상 140 이하의 정수를 막대 높이로 선택합니다.

실습 4-A · 넓은 범위 20~140 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

image = Image.new("RGB", (660, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)

for i in range(12):
    x = 40 + i * 50
    height = random.randint(20, 140)
    draw.rectangle((x, 180 - height, x + 30, 180), fill=(37, 62, 78))

display(image)

결과 확인 짧은 막대와 긴 막대가 함께 나올 수 있습니다. 아래쪽 끝은 모두 y = 180입니다.

실습 4-B · 좁은 범위 100~140 · 전체 코드

최솟값이 100인 전체 코드입니다. A의 코드를 수정할 필요 없이 바로 실행합니다.

실습 4-B · 좁은 범위 100~140 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

image = Image.new("RGB", (660, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)

for i in range(12):
    x = 40 + i * 50
    height = random.randint(100, 140)
    draw.rectangle((x, 180 - height, x + 30, 180), fill=(37, 62, 78))

display(image)

결과 확인 모든 막대의 높이가 100 이상입니다. 실제 최솟값·최댓값이 반드시 100·140인 것은 아닙니다.

범위별 실행 예시입니다. 두 코드 모두 색·너비·간격·아래쪽 좌표가 같습니다. 막대의 높이와 순서는 예시와 달라도 됩니다.

5. 시드로 배열 재현하기

핵심 질문: 다른 배열을 생성한 뒤 처음 배열을 재현할 수 있을까요? · 실습 5

막대 코드를 다시 실행하면 각 막대의 높이를 새로 선택합니다. 이 방법으로 여러 배열을 만들 수 있습니다. 이 중 하나를 나중에 다시 만들려면 각 막대의 높이를 처음과 같은 순서로 선택해야 합니다.

Python의 random 모듈은 계산으로 난수를 생성합니다. 이 계산을 시작할 때 사용하는 값이 시드(seed)입니다. random.seed(21)은 시작값을 21로 설정합니다. 같은 Python 환경에서 시드와 난수 함수의 종류·범위·실행 순서를 유지하면 같은 값이 같은 순서로 선택됩니다.

선택 순서의 재현

시드 21의 수열 22, 54, 89, 54, 82와 시드 7의 수열 42, 20, 51, 84, 7을 비교하고 시드 21로 첫 수열을 재현한 그림
같은 시작값, 같은 선택 순서같은 환경에서 randint(1, 100)을 다섯 번 실행한 예시입니다. 21로 다시 시작하면 첫 줄의 다섯 숫자가 같은 순서로 나옵니다.

seed(21)의 21은 다음에 나올 숫자를 뜻하지 않습니다. 위 예시에서 첫 숫자는 22이며, 다음에는 54가 나옵니다. 시드는 난수 계산의 초기 상태를 정하는 값입니다. 시드의 수치가 크다고 생성되는 난수도 커지거나 이미지의 완성도가 높아지는 것은 아닙니다.

시드를 다시 설정하지 않고 난수 함수를 계속 실행하면 다음 값을 생성합니다. 같은 시드를 다시 설정하면 난수 생성기가 같은 초기 상태로 설정됩니다. 따라서 이미지를 재현하려면 시드 설정부터 그리기까지 전체 코드를 다시 실행해야 합니다.

시연: 숫자 재현

교수자 시연에서 시드 설정을 포함한 셀 전체를 두 번 실행합니다. 두 실행 모두 22, 54, 89, 54, 82를 출력합니다. 같은 시드와 난수 호출 순서를 사용하면 생성되는 수열을 재현할 수 있습니다.

실습 5-A · 시드 21 · 전체 코드
import random

random.seed(21)
for i in range(5):
    number = random.randint(1, 100)
    print(number)

시드 설정 코드의 위치

이미지를 재현할 때도 random.seed(21)을 반복문 바로 앞에서 한 번 실행합니다. 시드 설정 줄 앞에는 공백을 넣지 않습니다. 이 줄을 반복문 안에 작성하면 반복할 때마다 난수 생성기가 같은 초기 상태로 설정되어 모든 막대의 높이가 같아집니다.

실습 5. 배열 재현

A → B → A 순서로 실행합니다. 마지막에는 A 셀 전체를 다시 실행해 처음의 배열이 재현되는지 확인합니다.

실습 5-A · 시드 21 · 전체 코드

시드 설정부터 이미지 출력까지 한 번에 실행하는 코드입니다.

실습 5-A · 시드 21 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

image = Image.new("RGB", (660, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)

random.seed(21)
for i in range(12):
    x = 40 + i * 50
    height = random.randint(20, 140)
    draw.rectangle((x, 180 - height, x + 30, 180), fill=(37, 62, 78))

display(image)

결과 확인 첫 막대부터 높이가 41, 73, 108, 73 순서입니다. 같은 셀을 다시 실행해도 배열이 같습니다.

실습 5-B · 시드 7 · 전체 코드

A와 같은 범위와 그리기 설정을 사용하고 시작값만 바꾼 코드입니다.

실습 5-B · 시드 7 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

image = Image.new("RGB", (660, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)

random.seed(7)
for i in range(12):
    x = 40 + i * 50
    height = random.randint(20, 140)
    draw.rectangle((x, 180 - height, x + 30, 180), fill=(37, 62, 78))

display(image)

결과 확인 A와 다른 배열이 나옵니다. 이제 A 셀을 다시 실행해 A의 처음 결과와 비교합니다.

시드별 결과

같은 Python 환경에서 코드의 시드만 21 → 7 → 21로 바꾼 결과입니다. A와 C는 같은 순서에 있는 막대의 높이가 모두 일치합니다. B는 다른 배열이지만 일부 막대의 높이는 우연히 같을 수 있습니다.

6. 같은 시드에서 결과가 달라지는 이유

핵심 질문: 높이가 같은 것과 완성 이미지가 같은 것은 어떻게 다를까요?

네 코드는 모두 시드 21을 사용합니다. A를 기준으로 B는 시드 위치, C는 난수 호출 횟수, D는 배경색 하나만 바꿉니다. 먼저 바뀔 결과를 예상하고, 출력된 높이와 이미지를 따로 비교합니다.

실습 6. 코드 변경과 결과의 관계 분석

실습 6-A · 기준 · 전체 코드

반복문 앞에서 시드를 한 번 설정합니다.

실습 6-A · 기준 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

image = Image.new("RGB", (660, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)

random.seed(21)
for i in range(12):
    x = 40 + i * 50
    height = random.randint(20, 140)
    print(i + 1, height)
    draw.rectangle((x, 180 - height, x + 30, 180), fill=(37, 62, 78))

display(image)

결과 확인 처음 네 높이는 41, 73, 108, 73입니다.

실습 6-B · 시드 위치 변경 · 전체 코드

시드를 매 반복마다 다시 설정합니다.

실습 6-B · 시드 위치 변경 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

image = Image.new("RGB", (660, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)

for i in range(12):
    random.seed(21)
    x = 40 + i * 50
    height = random.randint(20, 140)
    print(i + 1, height)
    draw.rectangle((x, 180 - height, x + 30, 180), fill=(37, 62, 78))

display(image)

결과 확인 열두 높이가 모두 41입니다. 배열은 A와 다르고, B 자체는 다시 실행해도 같습니다.

실습 6-C · 난수 호출 추가 · 전체 코드

반복 전에 높이 하나를 preview에 저장하고, 막대는 그다음 값부터 사용합니다.

실습 6-C · 난수 호출 추가 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

image = Image.new("RGB", (660, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)

random.seed(21)
preview = random.randint(20, 140)
for i in range(12):
    x = 40 + i * 50
    height = random.randint(20, 140)
    print(i + 1, height)
    draw.rectangle((x, 180 - height, x + 30, 180), fill=(37, 62, 78))

display(image)

결과 확인 처음 네 높이는 73, 108, 73, 101입니다. preview의 값을 그리기에 사용하지 않아도, 해당 대입문에서 난수 함수는 한 번 실행됩니다.

실습 6-D · 배경색 변경 · 전체 코드

A와 같은 선택 순서를 유지하고 캔버스의 배경색만 바꿉니다.

실습 6-D · 배경색 변경 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

image = Image.new("RGB", (660, 220), (225, 232, 229))
draw = ImageDraw.Draw(image)

random.seed(21)
for i in range(12):
    x = 40 + i * 50
    height = random.randint(20, 140)
    print(i + 1, height)
    draw.rectangle((x, 180 - height, x + 30, 180), fill=(37, 62, 78))

display(image)

결과 확인 높이 열두 개는 A와 같습니다. 배경색이 달라 완성 이미지는 다릅니다.

6-D · 배경색만 변경Python 실행 결과
옅은 녹회색 배경 위에 시드 21의 높이를 유지한 청회색 막대 열두 개
A와 막대 높이는 같고 배경색이 다릅니다. B·C의 출력 이미지는 위의 시드 위치·호출 순서 비교와 같습니다.
실행 후 비교 기록
경우변경한 항목A와 높이가 같은가?A와 이미지가 같은가?
B시드 위치기록기록
C난수 호출 한 번기록기록
D배경색기록기록
활동 6 · 확인 질문

B·C·D 중 높이는 A와 같지만 이미지가 다른 것은 무엇인가요? B도 같은 결과를 다시 만들 수 있다면 “재현된다”와 “높이가 다양하다”는 같은 뜻일까요?

예상과 해설 비교

D는 높이가 같고 배경색이 다릅니다. B와 C는 높이 배열도 다릅니다. B는 모두 41인 배열을 재현하므로 재현 여부와 값의 다양성은 별개입니다.

보충 시연: 수열과 난수 호출 순서

반복할 때마다 시드를 다시 설정하면

시드를 설정하면 난수 생성기의 상태가 초기화됩니다. 상태는 다음 값을 계산하는 데 사용하는 내부 정보입니다. randint를 실행하면 난수를 생성하는 과정에서 내부 상태도 갱신됩니다. 반복문 안에서 같은 시드로 seed를 실행하면 반복할 때마다 내부 상태가 동일하게 초기화됩니다.

A의 처음 네 높이는 41, 73, 108, 73입니다. B에서는 첫 선택값 41만 열두 번 사용합니다. A와 B 모두 다시 실행하면 각자의 결과를 재현합니다. 재현 가능한 이미지에도 다양한 값이 들어갈 수 있습니다.

시연 A · 시드를 한 번 설정 · 전체 코드

두 코드의 네 출력값을 먼저 예상합니다. 각 코드는 독립적으로 실행할 수 있습니다.

시연 A · 시드를 한 번 설정 · 전체 코드
import random

random.seed(21)
for i in range(4):
    height = random.randint(20, 140)
    print(height)

결과 확인 41, 73, 108, 73이 순서대로 나옵니다.

시연 B · 반복마다 시드 재설정 · 전체 코드

seed를 반복문 안에서 실행하는 비교 코드입니다.

시연 B · 반복마다 시드 재설정 · 전체 코드
import random

for i in range(4):
    random.seed(21)
    height = random.randint(20, 140)
    print(height)

결과 확인 41이 네 번 나옵니다. 매번 첫 선택값부터 시작하기 때문입니다.

재현의 기준

시드 없이 만든 배열에 나중에 21을 지정해도 이전 배열이 복원되지는 않습니다. 시드 21을 먼저 설정한 뒤 배열 A를 만들고, 같은 설정으로 A를 다시 만들 수 있는지 확인합니다. 재현에 필요한 코드 전체와 시드를 함께 보관합니다.

재현 조건

시드가 같을 때 유지할 설정
변경 사항달라지는 과정결과
같은 코드를 같은 시드부터 실행선택 순서와 그리기 설정 유지같은 이미지 재현
높이 범위를 100~140으로 변경선택 가능한 정수 변경막대 높이 변경
seed 다음에 randint(20, 140) 한 번 추가첫 난수를 생성하고 그리기에서 제외막대에 사용하는 값의 순서 변경
배경색만 변경숫자 선택 순서는 유지높이는 같아도 이미지는 다름

시드는 완성 이미지를 저장하는 기능이 아닙니다. 같은 Python 환경에서 코드와 시드를 함께 유지해야 같은 이미지를 다시 만들 수 있습니다. 숫자의 재현과 이미지 전체의 재현을 구분합니다.

난수를 한 번 더 생성하면

같은 시드라도 난수 함수를 한 번 더 실행하면 뒤에서 사용하는 값이 달라질 수 있습니다. 다음 비교에서는 seed(21) 직후에 같은 범위의 randint(20, 140)를 한 번 추가합니다. 먼저 생성한 값 41은 도형에 사용하지 않으므로 첫 막대의 높이에는 다음 값인 73을 사용합니다.

A · 바로 그리기
  1. 41
  2. 73
  3. 108
  4. 73
B · 한 번 먼저 선택
  1. 41그리기 제외
  2. 73
  3. 108
  4. 73
  5. 101
시연 · 출력과 새 선택 구분
시연 · 출력과 새 선택 구분
import random

random.seed(21)
preview = random.randint(20, 140)
print("먼저 선택:", preview)
for i in range(4):
    height = random.randint(20, 140)
    print("막대 높이:", height)

print("먼저 선택:", preview)만 지우면 막대 높이도 바뀔까요? 예상한 뒤 실행합니다. 이어서 preview = random.randint(20, 140)도 지우고 비교합니다.

해설: 출력은 새 난수를 생성하지 않는다

print만 삭제하면 난수 생성 코드는 계속 실행되므로 막대 높이는 73, 108, 73, 101로 유지됩니다. preview 대입문까지 삭제하면 첫 값 41부터 막대에 사용합니다. print는 저장된 값을 읽고, randint는 새 값을 생성합니다. 이 예시는 같은 randint 범위의 호출을 하나 추가한 경우입니다. 난수 함수나 범위를 다르게 지정하면 값의 순서가 이 예시처럼 한 항목씩 이동한다고 단정할 수 없습니다.

7. 크기와 여백을 고려해 위치 정하기

핵심 질문: 원 전체가 보이려면 x의 최댓값을 어떻게 정할까요? · 실습 7

지금까지는 선택한 정수를 막대의 높이로 사용했습니다. 이번에는 randint()로 선택한 정수를 x에 저장해 원의 가로 위치를 정합니다. 원 하나의 색·크기·y좌표를 고정하고 x좌표만 선택합니다.

원의 좌표

3주차의 draw.ellipse()는 경계 상자 안에 타원을 그립니다. (x, y, x + size, y + size)처럼 가로와 세로의 좌표 차이를 같게 지정하면 원이 됩니다. 이 실습에서는 size를 80, y를 70으로 정하므로 x를 바꾸어도 원의 크기와 세로 위치는 유지됩니다.

너비 660인 캔버스에서 x를 620으로 정하면 원의 오른쪽 끝은 620 + 80 = 700에 놓입니다. 캔버스 너비를 넘기 때문에 원의 오른쪽이 잘립니다. 왼쪽 x좌표뿐 아니라 오른쪽 끝의 좌표도 확인해야 합니다.

범위를 벗어난 원왼쪽 x = 620 · 오른쪽 x = 700
너비 660인 캔버스의 오른쪽 끝에서 일부가 잘린 산호색 원
원은 너비 80의 경계 상자에 그려졌지만, 캔버스 밖에 있는 부분은 표시되지 않습니다.

좌표 범위 계산

원의 크기를 정한 뒤, 원 전체가 들어갈 수 있는 x좌표의 범위를 계산합니다. margin은 가장자리에서 안쪽 기준선까지의 간격이며 여기서는 40입니다. 왼쪽 x좌표는 margin 이상이어야 하고, 오른쪽 좌표 x + size는 canvas_width - margin 이하여야 합니다.

x좌표의 선택 범위
항목식의미
최솟값margin원의 왼쪽 끝을 놓을 수 있는 최소 x좌표
오른쪽 기준선canvas_width - margin원의 오른쪽 끝을 놓을 수 있는 최대 x좌표
최댓값canvas_width - margin - size오른쪽 기준선에서 원 크기만큼 뺀 좌표

random.randint(margin, canvas_width - margin - size)로 이 범위 안의 x를 선택합니다. 셀을 한 번 실행하면 x를 한 번 선택하고, 새 캔버스에 원 하나를 그립니다.

오른쪽 한계

원의 크기를 고려한 최대 x좌표 너비 660인 캔버스에 좌우 여백 40을 두면 오른쪽 기준선은 620입니다. 크기 80인 원의 왼쪽 x좌표는 최대 540이고, 오른쪽 끝은 620입니다. 크기 80 040540620660 왼쪽 x의 선택 범위 40 ≤ x ≤ 540
오른쪽 기준선 620에서 원의 크기 80을 빼면, 왼쪽 x좌표의 최댓값은 540입니다.
  1. 01기준선 계산660 - 40 = 620캔버스 너비에서 여백 빼기
  2. 02크기 반영620 - 80 = 540기준선에서 원 크기 빼기
  3. 03위치 선택random.randint(40, 540)원 전체가 보이는 범위

620은 원의 오른쪽 끝이 위치할 수 있는 최대 x좌표입니다. x는 경계 상자의 왼쪽 끝이므로 620에서 원의 크기를 뺀 값 이하여야 합니다. 원 크기가 120으로 커지면 최댓값은 620 - 120 = 500으로 줄어듭니다.

실습 7. 위치 범위

A에서 잘리는 이유를 확인한 뒤 B를 실행합니다. 두 코드 모두 원의 크기는 80, y좌표는 70입니다.

실습 7-A · 범위를 벗어난 위치 · 전체 코드

오른쪽 끝이 캔버스를 넘는 예시입니다.

실습 7-A · 범위를 벗어난 위치 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

canvas_width = 660
margin = 40
size = 80
x = 620
y = 70

image = Image.new("RGB", (canvas_width, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)
draw.ellipse((x, y, x + size, y + size), fill=(201, 111, 93))

display(image)
print("왼쪽 x:", x)
print("오른쪽 x:", x + size)

결과 확인 왼쪽 x는 620, 오른쪽 x는 700입니다. 캔버스 너비 660을 넘어 원이 잘립니다.

실습 7-B · 크기와 여백을 반영한 위치 · 전체 코드

원의 크기를 뺀 범위 안에서 x를 고르는 전체 코드입니다.

실습 7-B · 크기와 여백을 반영한 위치 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

canvas_width = 660
margin = 40
size = 80
x = random.randint(margin, canvas_width - margin - size)
y = 70

image = Image.new("RGB", (canvas_width, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)
draw.ellipse((x, y, x + size, y + size), fill=(201, 111, 93))

display(image)
print("왼쪽 x:", x)
print("오른쪽 x:", x + size)

결과 확인 왼쪽 x는 40 이상, 오른쪽 x는 620 이하입니다. 다시 실행하면 이 범위 안에서 위치를 새로 선택합니다.

수정 후 실행 예시x 선택 · 크기 80 유지
계산한 좌표 범위 안에서 위치를 선택해 전체가 보이는 산호색 원
원의 색과 크기는 고정하고 가로 위치만 무작위로 선택합니다. 원의 위치는 예시와 달라도 됩니다.

3교시 포스터에서는 같은 원리로 크기를 먼저 정하고 x와 y의 범위를 계산합니다. 가로와 세로 모두 원의 크기를 고려합니다.

실습 7-C. 크기가 다른 원의 최대 위치

크기 80일 때와 120일 때의 x 최댓값을 먼저 계산합니다. 아래 코드는 무작위 위치 대신 각 범위의 최댓값에 원을 놓아, 두 원의 오른쪽 끝이 같은 기준선에 닿는지 확인합니다.

실습 7-C · 크기 80과 120 · 전체 코드

한 셀에서 두 이미지를 차례로 출력합니다. 각 반복에서 새 캔버스를 만들므로 원 두 개가 한 이미지에 겹치지 않습니다.

실습 7-C · 크기 80과 120 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display

canvas_width = 660
margin = 40

for size in [80, 120]:
    x_max = canvas_width - margin - size
    x = x_max
    y = 50
    image = Image.new("RGB", (canvas_width, 220), (245, 240, 228))
    draw = ImageDraw.Draw(image)
    draw.ellipse((x, y, x + size, y + size), fill=(201, 111, 93))
    print("크기:", size, "x 범위:", margin, "~", x_max)
    print("오른쪽 끝:", x + size)
    display(image)

결과 확인 크기 80의 범위는 40~540, 크기 120의 범위는 40~500입니다. 두 원의 오른쪽 끝은 모두 620입니다.

활동 7 · 확인 질문

크기를 120으로 늘리고 x를 540으로 유지하면 오른쪽 여백 40을 유지할 수 있나요? 도형이 캔버스 안에 표시되는 조건과 지정한 여백을 확보하는 조건을 구분해 설명합니다.

예상과 해설 비교

오른쪽 좌표는 660이므로 여백 기준선 620을 40만큼 넘습니다. 크기 120에서는 x가 500 이하여야 오른쪽 끝이 여백 기준선을 넘지 않습니다.

8. 색과 높이를 함께 설계하기

핵심 질문: 내가 정한 규칙과 프로그램이 선택한 값을 어떻게 설명할까요?

지금까지는 색 또는 높이 하나를 선택했습니다. 이번에는 막대 하나를 그릴 때 높이 선택 → 색 선택 → 두 값을 사용해 그리기를 수행합니다. 막대 여섯 개의 위치·너비·아래쪽 좌표는 고정합니다.

실습 8. 여섯 막대의 색과 높이 설계

실습 8 · 색과 높이 종합 설계 · 전체 코드

설정 네 줄에 시드·높이 범위·팔레트가 모여 있습니다. 먼저 그대로 실행하고, 출력된 높이·RGB 값이 각 막대의 높이·색에 대응하는지 확인합니다.

실습 8 · 색과 높이 종합 설계 · 전체 코드
from PIL import Image, ImageDraw
from IPython.display import display
import random

# 내가 정하는 설정
seed = 21
min_height = 40
max_height = 140
palette = [(37, 62, 78), (111, 143, 126), (201, 111, 93)]

image = Image.new("RGB", (660, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)

random.seed(seed)
for i in range(6):
    x = 50 + i * 100
    height = random.randint(min_height, max_height)
    color = random.choice(palette)
    draw.rectangle((x, 180 - height, x + 60, 180), fill=color)
    print("막대", i + 1, "높이", height, "색", color)

display(image)

결과 확인 같은 설정으로 셀 전체를 다시 실행하면 높이와 색 순서가 모두 같습니다. 색 종류가 세 가지여도 세 색이 반드시 모두 나오는 것은 아닙니다.

8 · 시드 21의 색과 높이 배열Python 실행 결과
여섯 막대의 높이와 색을 각각 선택한 종합 실습 결과
이 코드는 막대마다 randint 다음에 choice를 실행합니다. 높이만 선택했던 실습 5와 난수 호출 순서가 달라 같은 시드 21이어도 다른 높이 배열이 나옵니다.
설정 변경과 결과 비교
  1. 팔레트·높이 범위·시드 중 바꿀 설정 하나와 원하는 효과를 적습니다. 높이는 20 ≤ min_height ≤ max_height ≤ 140 안에서 정합니다.
  2. 바로 위 전체 코드의 설정값을 바꾸고 실행합니다. 새 실행 결과와 사용한 설정값을 함께 기록합니다.
  3. 설정을 그대로 두고 셀 전체를 다시 실행해 재현되는지 확인합니다.
  4. “내가 정한 것은 ___, 프로그램이 선택한 것은 ___, 재현하려면 보관할 것은 ___”를 완성합니다.
활동 8 · 확인 질문

막대 여섯 개를 그릴 때 randint와 choice는 각각 몇 번 실행되나요? 시드만 메모하면 나중에 이 이미지를 그대로 만들 수 있을까요?

예상과 해설 비교

각 함수는 여섯 번 실행됩니다. 시드뿐 아니라 팔레트·높이 범위·그리기 설정·난수 호출 순서를 포함한 코드 전체를 같은 Python 환경에서 사용해야 합니다.

핵심 정리

오늘 사용한 함수는 세 가지입니다. choice()는 목록에서 항목 하나를, randint()는 범위에서 정수 하나를 선택합니다. seed()는 난수 계산의 시작값을 설정합니다. 같은 함수라도 코드의 위치와 입력값에 따라 이미지가 달라집니다.

단계별 학습 결과물
단계기록설명할 핵심 개념
1. 무작위 선택고정·변화 요소 두 문장후보 목록과 실제 선택 결과의 차이
2. 색상 후보두 팔레트의 이미지선택 방법과 후보 목록의 역할
3. 선택 횟수A·B 이미지와 실행 횟수색 선택과 그리기의 횟수
4. 높이 범위넓은·좁은 범위의 이미지가능한 범위와 실제 높이
5. 시드와 재현21 → 7 → 21 비교같은 배열이 재현되는 조건
6. 원인 분석변경 사항과 비교 기록높이의 재현과 이미지의 재현
7. 크기와 위치크기 80·120의 좌표 범위오른쪽 기준선에서 size를 빼는 이유
8. 종합 설계설계한 배열·설정값·생성 규칙선택·저장·사용·재현의 연결

개념 복습

실행 결과 예상과 근거 설명
  1. 시드 21을 유지하고 배경색만 바꾸면 막대 높이와 완성 이미지 중 무엇이 같을까요?
  2. 같은 변수 color를 두 번 출력하면 선택 횟수도 두 번일까요?
  3. 너비 660, 여백 40에서 원 크기를 80에서 120으로 바꾸면 x의 최댓값은 어떻게 달라질까요?
  4. 시드 없이 생성한 이미지를 재현하려고 합니다. 나중에 seed(21)을 추가하면 그 이미지를 다시 만들 수 있을까요?

각 답변에 변경되는 값·유지되는 값·실행되는 코드 중 관련 근거를 한 문장씩 작성합니다.

설명 예시 확인

① 난수 호출을 유지했으므로 높이는 같지만 배경색이 달라 완성 이미지는 다릅니다. ② print는 저장된 값을 읽으므로 선택 횟수는 늘지 않습니다. ③ 최댓값은 540에서 500으로 줄어듭니다. ④ 새로 지정한 시드는 이전 상태를 복구하지 않습니다. 처음부터 시드와 코드를 함께 보관해야 합니다.

1. 후보와 선택

choice는 후보에서 하나를 선택합니다. 모든 후보를 한 번씩 사용한다는 뜻은 아닙니다.

2. 선택과 출력

print를 반복해도 새 값을 선택하지 않습니다. 새로 선택하려면 choice나 randint가 있는 줄을 다시 실행합니다.

3. 반복과 선택

반복문 앞에서 선택하면 한 번 저장한 값을 사용합니다. 반복문 안에서 선택하면 반복할 때마다 새로 고릅니다. 새로 골라도 같은 값이 나올 수 있습니다.

4. 범위와 결과

randint는 양 끝값을 포함한 정수 하나를 선택합니다. 반복 횟수가 적으면 양 끝값이 실제 결과에 없을 수 있습니다.

5. 시드와 코드

시드는 난수 생성기의 초기 상태를 정합니다. 같은 이미지를 재현하려면 같은 환경에서 시드와 코드 전체를 유지해야 합니다.

6. 좌표와 크기

원의 오른쪽 끝이 기준선을 넘지 않으려면, 오른쪽 기준선에서 원 크기를 빼서 x의 최댓값을 계산해야 합니다. 따라서 크기를 먼저 정한 뒤 위치를 선택합니다.

오류 점검

현상별 점검 항목
현상점검 항목
NameError: randomimport random을 포함한 전체 셀의 실행 여부
IndentationError반복문 안은 네 칸 들여쓰고, 시드 설정 줄은 for와 같은 들여쓰기 수준으로 작성
모든 칸이 같은 색실습 3-A는 정상. 칸마다 선택할 때는 choice가 반복문 안에 있는지 점검. 이 경우에도 우연히 한 색만 나올 수 있음
모든 막대의 높이가 80실습 4의 전체 코드에서 height에 randint의 결과를 저장하는지 점검
다시 실행한 막대 배열이 다름높이 범위가 20~140인지, 시드 21을 포함한 전체 코드를 실행했는지 점검
시드 적용 후 높이가 모두 같음seed가 반복문 안에 들어갔는지 점검
원의 오른쪽이 잘림x의 최댓값을 canvas_width - margin - size로 계산했는지, 수정한 셀 전체를 실행했는지 점검
사각형 하나만 남음Image.new가 반복문 앞에 있는지, x가 i에 따라 바뀌는지 점검

2교시 예고

1교시에서는 색상 후보에서 색을, 정수 범위에서 높이와 x좌표를 선택했습니다. 2교시에서는 선택한 수를 원의 크기로 사용하고, “크기가 60 이상이면 강조색을 사용한다”와 같은 규칙을 추가합니다. 이 규칙을 작성하는 비교식과 if / elif / else 조건문을 배웁니다. 1·2교시의 원리를 함께 활용한 작품 제작은 3교시 포스터 실습으로 이어집니다.

보충: 작품 사례

아래 이미지는 작품의 구조를 단순화한 수업용 개념도입니다. 실제 작품과 설명은 링크된 미술관 페이지에서 확인합니다. 오늘 배운 고정된 규칙과 무작위 선택이 작품에서 어떻게 사용되는지 보여 주는 보충 자료입니다.

같은 크기의 정사각형 36개로 된 두 격자. 왼쪽은 세 색을 순서대로 반복하고 오른쪽은 같은 세 후보에서 색을 무작위로 선택한 비교
수업용 구조도 · 왼쪽은 순서대로 배색, 오른쪽은 무작위 배색. 두 격자의 칸 크기·개수·위치는 같습니다.
CHANCE / COLOR / GRID

Ellsworth Kelly, Spectrum Colors Arranged by Chance VI ↗

MoMA의 작품 기록과 큐레이터 오디오 설명에 따르면, 이 1951년 작업은 색종이와 격자라는 정해진 재료·구조 안에서 색의 배치를 우연과 수학적 체계에 맡긴 사례입니다. 고정된 격자와 달라지는 색 배치를 구분해 관찰합니다.

위 비교 그림은 이 원리를 세 색과 6 × 6 격자로 설명하기 위해 만든 별도의 수업 자료입니다. 오른쪽에서도 칸을 옮기거나 크기를 바꾸지 않고 색만 선택합니다. 실제 작품의 색상 수나 배열을 재현한 그림은 아닙니다.

후보
정해진 색의 집합
규칙
격자 구조 유지
변화
색이 놓이는 순서
형태와 행동 규칙이 반복 실행되어 가변적인 구조를 만드는 과정을 나타낸 수업용 개념도
1주차 복습 구조도 · 자연어 규칙 → 소프트웨어 실행 → 계속 변하는 구조
RULE / SOFTWARE / VARIATION

Casey Reas, {Software} Structure #003 ↗

Whitney Museum의 설명에 따르면, Reas는 원의 수·크기·움직임과 요소 사이의 관계를 일상 언어로 작성한 뒤 프로그램으로 구현합니다. 실행 중 각 요소는 달라지지만 관계와 행동 규칙은 유지됩니다. 이 사례는 1주차의 ‘입력 → 규칙 → 출력’을 작품에 적용한 예입니다.

후보
원의 크기와 방향
규칙
요소의 관계와 행동
변화
실행 중 나타나는 구조
규칙 설명 예시

Kelly 사례는 “격자와 색 후보는 고정하고, 정해진 색 가운데 하나를 선택하여 각 칸의 색 배열을 변화시킨다”라고 정리할 수 있습니다. Reas 사례는 “요소의 수와 행동 관계는 고정하고, 각 원의 크기와 방향을 변화시켜 실행 중 나타나는 구조를 바꾼다”라고 정리할 수 있습니다.

참고 자료

이 페이지의 목차