Skip to content

13주차 · 실용 알고리즘과 데이터 활용

  1. 카운팅·정렬·탐색 3가지 패턴을 EE 맥락(센서 로그, 측정값, 임계값)에서 사용한다
  2. dict로 빈도를 세는 카운팅 패턴을 작성한다
  3. sorted(..., key=함수)로 측정 레코드를 원하는 기준으로 정렬한다
  4. for 루프와 break로 조건을 만족하는 첫 번째 항목을 효율적으로 탐색한다
  5. 알고리즘 결과를 의사결정 문장으로 요약한다
  6. O(n)O(n²) 차이를 루프 횟수로 직관적으로 설명한다

알고리즘 quickstart: 3가지 패턴 뼈대

Section titled “알고리즘 quickstart: 3가지 패턴 뼈대”

초급 단계에서 마주치는 대부분의 문제는 아래 3가지 패턴으로 해결할 수 있습니다. 먼저 문제 문장을 질문으로 바꿔 보세요.

문제 질문우선 떠올릴 패턴결과 형태
”각 상태가 몇 번 나왔나?”카운팅dict
”큰 순서대로 보고 싶다”정렬정렬된 리스트
”처음 기준을 넘는 값은?”탐색값 1개 또는 None
counts = {}
for x in items:
counts[x] = counts.get(x, 0) + 1
def sort_key(item):
return item["value"] # 정렬 기준을 함수로 명시한다
ordered = sorted(items, key=sort_key)
found = None
for x in items:
if x > threshold:
found = x
break # 첫 번째 매치에서 즉시 종료
  1. 문제를 먼저 카운팅 / 정렬 / 탐색 중 하나로 분류한다
  2. 카운팅·조회가 많으면 dict를 쓴다
  3. 첫 번째 항목만 필요하면 break로 조기 종료한다
  4. 결과를 의사결정 문장 한 줄로 요약한다

자료구조언제 쓰나EE 예시
list순서가 중요하고 인덱스로 접근할 때시간별 측정값 시퀀스
dict”키 → 값” 조회나 카운팅이 많을 때상태 코드별 발생 횟수
set중복 제거나 포함 여부 검사가 필요할 때고유 채널 번호 집합

  • O(n): 루프 한 겹 → 데이터 2배면 반복 횟수도 약 2배
  • O(n²): 루프 두 겹 → 데이터 2배면 반복 횟수는 약 4배

실행 시간이 아니라 반복 횟수를 세어 보면 O(n)O(n²) 차이를 더 쉽게 볼 수 있습니다. 아래 예제에서 n을 10에서 100으로 바꾸면, 한 겹 루프는 10배 늘지만 두 겹 루프는 100배 늘어납니다.

예제 · 루프 횟수 비교 코드를 실행하고 출력 결과를 확인하세요.
Ready

1) 카운팅 패턴 — 센서 상태 로그 집계

Section titled “1) 카운팅 패턴 — 센서 상태 로그 집계”

센서 로그에서 각 상태(OK / WARN / ERROR)가 몇 번 나왔는지 세면 시스템 상태를 빠르게 파악할 수 있습니다. counts.get(x, 0) + 1은 처음 보는 키면 0으로 시작하고, 이미 있으면 1 증가시킵니다.

예제 1 · 센서 상태 카운팅 코드를 실행하고 출력 결과를 확인하세요.
Ready
  • counts.get(status, 0): 딕셔너리에 키가 없으면 0을 기본값으로 반환합니다
  • max(counts, key=get_count): counts 딕셔너리의 키 중에서 get_count를 적용했을 때 가장 큰 값을 찾습니다. 여기서 key는 “무엇을 기준으로 비교할 것인가”를 알려주는 함수입니다
  • counts.values(): 딕셔너리에서 값만 꺼냅니다. 예를 들어 sum(counts.values())는 세어 놓은 전체 횟수가 원본 로그 길이와 같은지 검증할 때 유용합니다
  • 결과는 단순한 숫자가 아니라 의사결정 문장으로 마무리합니다

한 단계 더: 빈도표를 많이 나온 순서로 정렬하기

Section titled “한 단계 더: 빈도표를 많이 나온 순서로 정렬하기”

카운팅 결과를 보고할 때는 가장 많이 나온 상태부터 보여 주는 경우가 많습니다. 딕셔너리의 items()("OK", 4)처럼 키와 값이 한 쌍인 tuple들을 만들어 줍니다. 이때 item[0]은 상태 이름, item[1]은 횟수입니다.

예제 1-2 · counts.items() 정렬 코드를 실행하고 출력 결과를 확인하세요.
Ready

reverse=True-item[1] 중 무엇을 쓰나?

Section titled “reverse=True와 -item[1] 중 무엇을 쓰나?”

둘 다 내림차순 정렬에 사용할 수 있습니다. reverse=True는 “정렬 결과 전체를 뒤집는다”는 뜻이고, -item[1]은 “횟수에 마이너스를 붙여 큰 값이 앞에 오도록 기준을 바꾼다”는 뜻입니다. 초급 단계에서는 둘 중 하나만 일관되게 쓰면 충분하지만, 여러 기준을 섞어 정렬할 때는 return (-count, name)처럼 기준 함수를 쓰는 방식이 읽기 쉽습니다.


2) 정렬 패턴 — 측정값 우선순위 정렬

Section titled “2) 정렬 패턴 — 측정값 우선순위 정렬”

정렬은 “어떤 기준으로 우선순위를 매길 것인가”를 코드로 표현하는 과정입니다. sorted(..., key=함수)에서 key에는 함수 객체를 넘깁니다. lambda도 사용할 수 있지만, 이 수업에서는 가독성을 위해 **이름 있는 함수(def)**를 먼저 권장합니다.

sorted(items, key=f)는 각 원소에 함수 f를 적용한 결과를 기준으로 정렬합니다. 함수 이름만 넘기고 괄호는 붙이지 않습니다.

def sort_key(record):
return record["snr"] # 함수를 정의하고
ordered = sorted(records, key=sort_key) # 이름만 넘긴다 (sort_key() 가 아님)
예제 2 · SNR 기준 채널 정렬 코드를 실행하고 출력 결과를 확인하세요.
Ready
  • -ch["snr"]를 쓰는 이유: sorted는 기본 오름차순이므로 내림차순으로 정렬하려면 부호를 반전합니다
  • CH3CH4는 SNR이 같으므로 두 번째 기준인 채널 ID 오름차순으로 결정됩니다
  • 정렬 결과의 첫 번째 원소가 최우선 채널입니다

3) 탐색 패턴 — 임계값 초과 항목 찾기

Section titled “3) 탐색 패턴 — 임계값 초과 항목 찾기”

탐색은 “조건을 만족하는 항목을 찾는” 패턴입니다. 첫 번째 매치만 필요하면 break로 루프를 즉시 종료해 불필요한 반복을 줄입니다.

예제 3 · 임계값 초과 첫 번째 측정값 탐색 코드를 실행하고 출력 결과를 확인하세요.
Ready
  • break는 첫 번째 매치를 찾은 즉시 루프를 종료합니다 — 이후 데이터를 불필요하게 확인하지 않습니다
  • loop_count를 출력하면 조기 종료 효과를 직접 확인할 수 있습니다
  • first_over is not None 조건으로 탐색 실패 케이스를 안전하게 처리합니다

탐색 실패 케이스도 반드시 처리하기

Section titled “탐색 실패 케이스도 반드시 처리하기”

탐색에서는 “찾았다”만큼 “못 찾았다”도 중요한 결과입니다. 처음에 found = None으로 시작하면, 루프가 끝난 뒤에도 None인지 확인해서 실패 케이스를 안전하게 처리할 수 있습니다.

예제 3-2 · 임계값을 넘지 않는 경우 코드를 실행하고 출력 결과를 확인하세요.
Ready

4) 작은 통합 예시 — 카운팅 → 정렬 → 탐색

Section titled “4) 작은 통합 예시 — 카운팅 → 정렬 → 탐색”

실제 문제는 한 패턴만 쓰고 끝나지 않는 경우가 많습니다. 아래 예시는 같은 채널 로그에서 상태를 세고, 전력 기준으로 정렬하고, 기준보다 강한 첫 채널을 찾습니다.

예제 4 · 세 패턴 연결하기 코드를 실행하고 출력 결과를 확인하세요.
Ready
  1. status_countsdict 카운팅 패턴입니다.
  2. ordered는 정렬 결과이므로 원본 channel_log와 순서가 다릅니다.
  3. 첫 번째 후보만 필요하므로 탐색 루프에서 break를 사용합니다.
  4. 마지막 assert는 상태 카운트가 원본 데이터 개수와 맞는지 확인합니다.

실수증상수정 방법
list로 모든 것을 처리같은 키를 반복 탐색해 코드가 길어짐카운팅·조회는 dict로 분리
복잡한 lambda 사용정렬 기준이 한 줄에 숨어 읽기 어려움def sort_key(x): return ... 형태로 분리
break 없는 탐색첫 매치 후에도 루프가 계속 돔첫 매치 직후 break 추가
해석 문장 없음숫자만 출력하고 끝남의사결정 문장 한 줄 작성
이중 루프 남용데이터가 커지면 급격히 느려짐dict/set 기반 단일 순회로 재설계

교정 워크플로 (이론 → 코드 → 해석)

Section titled “교정 워크플로 (이론 → 코드 → 해석)”
  1. 이론: 문제를 “카운팅 / 정렬 / 탐색” 중 하나로 분류한다.
  2. 이론: 자료구조를 선택하고 루프 겹 수를 예상한다.
  3. 코드: 가장 짧은 정답 루프부터 작성한다.
  4. 코드: 중간 결과(counts, ordered)를 출력해 검증한다.
  5. 해석: 결과를 근거로 다음 행동 문장을 한 줄 작성한다.

목표: 리스트에 들어 있는 상태값의 발생 횟수를 딕셔너리로 셉니다.

해야 할 일: status_counts를 완성하고 전체 개수가 원본 로그 길이와 같은지 확인하세요.

완료 조건: 상태별 개수와 합계 검증 결과가 출력되어야 합니다.

실습 문제 1 · 상태 카운팅 코드를 실행하고 출력 결과를 확인하세요.
Ready

  • 문제를 카운팅/정렬/탐색 중 하나로 먼저 분류했는가?
  • 딕셔너리 카운팅에서 없는 key를 안전하게 처리했는가?
  • 정렬 방향이 문제 의도와 맞는가?
  • 첫 번째 결과만 필요할 때 break를 사용했는가?
  • 숫자 결과 뒤에 해석 문장을 붙였는가?