1 of 55

C++�Programming

한밭대학교 • INFO2118 • Aaron Snowberger

6주차: 라이브러리 알고리즘

2 of 55

라이브러리 알고리즘

06

3 of 55

라이브러리 알고리즘

5장에서 배운 바와 같이 다양한 타입의 컨테이너에 적용할 수 있는 함수들이 있습니다. 벡터, 문자열, 리스트는 요소를 삽입하는 insert 함수와 요소를 제거하는 erase 함수를 지원합니다. 이러한 함수들의 인터페이스는 모든 컨테이너 타입에서 동일하므로, 같은 코드를 문자열 클래스에도 적용할 수 있습니다.

4 of 55

라이브러리 알고리즘

또한 모든 컨테이너는 요소를 탐색하고 확인할 수 있도록 반복자(iterator)를 제공합니다. 반복자의 연산은 모든 컨테이너 타입에 대해 동일하게 작동합니다. 라이브러리는 표준 알고리즘을 통해 이 공통의 인터페이스를 활용하여 코드를 반복해서 작성하지 않고도 놀라울 정도로 작고 간단하게 프로그램을 만들 수 있습니다.

이 장에서는 라이브러리 알고리즘의 핵심 개념을 살펴보고, 다양한 문제에 알고리즘을 적용하여 해결하는 방법을 배웁니다. 모든 알고리즘은 <algorithm> 헤더에 정의되어 있습니다.

5 of 55

라이브러리 알고리즘

5.8.2/158p에서는 2개의문자 그림을 결합하는 데 반복문을 사용했습니다.

여러분은 이반복문이 ret 벡터의 끝에 bottom벡터에 있는 요소들의 복사본을 삽입하는 것을 의미함을 살펴보았습니다.

6.1 문자열 분석

6 of 55

라이브러리 알고리즘

제네릭 알고리즘 genetic algorithm이란 특정 종류의 컨테이너에 속하지 않으며, 인수의 타입으로 데이터에 접근하는 방법을 결정하는 알고리즘을 말합니다. 표준 라이브러리의 제네릭 알고리즘은 일반적으로 컨테이너 요소를 조작하는데 사용해 반복자가 인수로 있습니다. 예를 들어 3개의 반복자 begin, end, out이 인수로 있는 copy 알고리즘[begin, end) 범위의 모든 요소를 out에서 시작하는 요소들의 순차열에 필요한 만큼 공간을 확장하여 복사합니다.

6.1 문자열 분석

7 of 55

라이브러리 알고리즘

반복자 어댑터를 살펴보기 전에 앞 반복문에서 사용한 증가 연산자의 후위 postfix 표기를 주목 합시다.

여러분이 지금까지 사용한 전위 표기와 다르게 begin++begin 값을 반환한 후에 begin 값을 증가시킵니다.

바꿔 말하면 앞 코드는 다음 코드와 같습니다.

6.1 문자열 분석

8 of 55

라이브러리 알고리즘

증가 연산자는 * 연산자와 우선 순위가 같고 둘 다 오른쪽 우선 결합성이 있으므로 *out++는 *(out++)와 같습니다.

따라서 앞코드의 자세한 의미는 다음 코드와 같습니다.

6.1 문자열 분석

9 of 55

라이브러리 알고리즘

반복자 어댑터(iterator adaptor)는 인수와 관련된 속성을 가진 반복자를 반환하는 함수입니다. back_inserter 함수가장 일반적으로 사용되는 반복자 어댑터로, 컨테이너를 인수로 사용하며 컨테이너에 값을 추가할 목적지인 반복자를 반환합니다.

예를 들어 back_inserter(ret)는 ret 컨테이너에 요소들을 추가할 목적으로 사용되는 반복자를 반환합니다. 따라서 bottom 컨테이너의 모든 요소를 ret의 끝에 복사하여 추가하는 코드를 실행하면 ret의 크기가 bottom.size()만큼 증가합니다.

6.1 문자열 분석

10 of 55

라이브러리 알고리즘

표준 알고리즘을 사용하여 다시 작성할 수 있는 또 다른 함수는 5.6/150p에서 살펴본 split 함수 입니다. 이 함수를 작성할 때 까다롭다고 느낄 수 있습니다. 입력된 문장에서 각 단어를 구분할 때 인덱스를 사용하기 때문입니다. 인덱스는 반복자로 대체할 수 있고 다양한 동작을 실행할 때 표준 라이브러리 알고리즘을 사용할 수 있습니다.

6.1.1 새로운 split 함수

11 of 55

라이브러리 알고리즘

여기에서 반복자를 사용하여 문자열의 단어를 구분하고 추출하는 방법을 설명하고 있습니다. 반복자 i와 j문자열에서 단어의 범위를 찾는 데 사용됩니다.

  • i는 공백이 아닌 첫 번째 문자를 찾고,
  • j는 i부터 시작하여 첫 번째 공백을 찾습니다.
  • 이 범위를 사용하여 str의 데이터를 ret에 복사합니다.

find_if 함수는 반복자 범위 내에서 조건을 만족하는 요소를 찾는 데 사용됩니다. isspace 함수를 사용하여 공백 문자를 검사하고, spacenot_space 같은 서술 함수를 직접 작성하여 isspace 함수를 명확하게 전달합니다.

6.1.1 새로운 split 함수

12 of 55

라이브러리 알고리즘

이 알고리즘을 통해 문자열에서 단어를 구분하고, 단어의 범위를 반복자를 사용하여 직접 복사하는 과정을 살펴봤습니다.

string(i, j) 표현식을 사용하여 [i, j) 범위의 문자들을 복사하여 새로운 문자열을 만들고, ret 끝에 추가합니다. 이 방법은 이전의 인덱스를 사용한 프로그램과 다르게, 반복자와 str.end()를 활용하여 라이브러리 알고리즘을 적절히 사용합니다.

6.1.1 새로운 split 함수

13 of 55

라이브러리 알고리즘

여기에서 라이브러리를 사용하여 단어가 회문인지 판단하는 간단한 해결책을 소개합니다. 회문은 거꾸로 읽어도 철자가 같은 단어입니다. equal 함수rbegin 멤버 함수를 사용하여 이를 쉽게 판별할 수 있습니다.

level, eye, 오레오, 기러기, ..

6.1.2 회문 (palindrome)

14 of 55

라이브러리 알고리즘

equal 함수순서가 반대인 두 문자열이 같은지 비교합니다. 첫 번째세 번째 인수로 주어진 반복자들을 통해 두 순차열의 문자열을 비교합니다. rbegin 함수는 컨테이너의 마지막 요소에서 시작하여 거꾸로 통과하는 반복자를 반환합니다. 이러한 방식으로 equal 함수를 호출하면 문자열의 앞과 뒤를 비교하여 회문인지 판단할 수 있습니다.

  • s의 첫 문자와 마지막 문자를 비교하고,
  • 두 번째 문자와 마지막에서 두 번째 문자를 비교하는 방식으로 진행됩니다.

6.1.2 회문 (palindrome)

15 of 55

라이브러리 알고리즘

이 예제에서는 문자열에 포함된 웹 주소(URL)를 찾는 함수를 작성합니다. 이 함수는 전체 문자열을 훑으며 모든 URL을 찾고, 각각의 URL을 :// 구분자를 기준으로 앞쪽의 '프로토콜 이름'과 뒤쪽의 '리소스 이름'으로 구분합니다.

함수는 인수로 전달된 문자열에서 모든 URL을 찾아 vector<string> 형식으로 반환합니다. 반복자 b를 사용하여 문자열 내에서 ://를 찾고, 이를 기준으로 앞쪽의 '프로토콜 이름'과 뒤쪽의 '리소스 이름'을 찾아냅니다.

6.1.3 URL 찾기

16 of 55

라이브러리 알고리즘

앞 코드에서는 문자열에서 찾은 URL을 저장할 ret 벡터URL의 범위를 나타내는 반복자를 선언합니다. url_beg 함수url_end 함수를 사용하여 각 URL의 시작과 끝을 찾습니다.

  • url_beg 함수는 유효한 URL을 식별하고, 프로토콜 이름첫 번째 문자를 참조하는 반복자를 반환합니다. URL을 식별하지 못하면 두 번째 인수인 e를 반환합니다.
  • url_end 함수주어진 지점부터 문자열의 끝이나 URL이 아닌 문자에 도달할 때까지 탐색하여 URL의 마지막 문자 이후를 참조하는 반복자를 반환합니다.

6.1.3 URL 찾기

17 of 55

라이브러리 알고리즘

url_begurl_end를 사용하여 반복자

  • b는 URL의 시작점을,
  • 반복자 after는 URL의 마지막 문자 이후를 가리킵니다.

이 범위의 문자들로 새로운 문자열을 만들어 ret 벡터에 추가합니다.

그 후, b를 증가시켜 다음 URL을 찾는 작업을 진행합니다. 모든 URL을 찾을 때까지 while 문을 실행하고, 완료되면 모든 URL을 저장한 벡터를 반환합니다.

6.1.3 URL 찾기

18 of 55

라이브러리 알고리즘

이제 url_beg와 url_end 함수를 구현할 것입니다. url_end 함수를 먼저 살펴보겠습니다.

이 함수는 find_if 라이브러리 함수를 사용하여 not_url_char 함수를 전달합니다.

not_url_char 함수는 전달된 문자가 URL에 포함될 수 없는 문자인 경우 을 반환합니다.

6.1.3 URL 찾기

19 of 55

라이브러리 알고리즘

not_url_char 함수는 여러 개념을 포함하고 있습니다:

  • static으로 선언된 지역 변수는 함수를 반복해서 호출하더라도 값이 보존됩니다. 따라서 not_url_char 함수의 첫 번째 호출에서만 문자열 url_ch를 만들고 초기화합니다.
  • isalnum 함수<cctype> 헤더에서 정의하며, 전달된 문자가 알파벳이나 숫자인지 판별합니다.
  • find 알고리즘은 find_if와 유사하며, 주어진 값이 있는지 순차열에서 찾고 해당 값의 첫 번째 출현 지점을 가리키는 반복자를 반환합니다.

6.1.3 URL 찾기

이 지식을 바탕으로 not_url_char 함수를 이해할 수 있습니다. 함수는 전달된 문자가 알파벳이나 숫자, 또는 url_ch 문자열에 속한 문자일 때 거짓을 반환합니다. 그 외의 경우 참을 반환합니다.

20 of 55

라이브러리 알고리즘

url_beg 함수는 문자열에서 URL을 찾기 위해 ://를 포함하지만 URL이 될 수 없는 경우까지 고려해야 하는 복잡한 함수입니다. 이 함수는 유효한 프로토콜 이름의 목록을 제한하지 않고 구분 기호 :// 앞뒤로 유효한 문자가 하나라도 있으면 URL로 인식하도록 구현합니다.

6.1.3 URL 찾기

21 of 55

라이브러리 알고리즘

url_beg 함수탐색할 범위를 나타내는 두 개의 반복자를 인수로 받아, 해당 범위 내에서 찾은 첫 번째 URL의 시작점을 나타내는 반복자를 반환합니다. URL을 식별하기 위해 구분 기호를 사용하며, 이를 위한 문자열 sep를 선언하고 초기화합니다.

sep는 not_url_char 함수에서 사용된 url_ch처럼 static이면서 const이므로, 값을 수정할 수 없고 함수의 첫 번째 호출에서만 생성됩니다. url_beg 함수는 주어진 b와 e 사이의 범위를 나타내는 문자열에서 두 반복자를 사용하여 동작합니다.

6.1.3 URL 찾기

22 of 55

라이브러리 알고리즘

url_beg 함수는 문자열에서 URL 구분 기호의 시작점을 찾습니다. 반복자 i는 URL 구분 기호의 시작점을 가리키고, 반복자 beg프로토콜 이름의 시작점을 가리킵니다.

url_beg 함수는 라이브러리 함수인 search 함수를 사용하여 구분 기호를 찾습니다. search 함수두 쌍의 반복자를 인수로 받아 현재 탐색할 순차열과 찾고자 하는 순차열을 나타냅니다. search 함수는 찾고자 하는 순차열을 발견하지 못하면 두 번째 인수에 해당하는 반복자를 반환합니다.

6.1.3 URL 찾기

23 of 55

라이브러리 알고리즘

search 함수 호출 후, i는 문자열의 마지막 지점 다음이나 발견한 구분 기호의 : 지점을 가리킵니다.

구분 기호를 발견하면 다음으로 프로토콜 이름을 구성하는 문자들을 찾습니다. 먼저 구분 기호가 현재 탐색 범위의 처음이나 마지막에 있는지 확인합니다. URL은 구분 기호 양쪽에 최소한 문자가 하나씩 있어야 합니다. 구분 기호의 위치가 현재 탐색 범위의 처음 또는 마지막이 아니라면 프로토콜 이름시작점인 beg의 위치를 구해야 합니다.

6.1.3 URL 찾기

24 of 55

라이브러리 알고리즘

url_beg 함수는 문자열에서 프로토콜 이름을 찾기 위해 beg 반복자를 거꾸로 이동시킵니다.

  • beg 반복자를 거꾸로 이동시켜 현재 탐색 범위의 처음이나 알파벳이 아닌 문자를 만나면 반복자를 반환합니다.
  • beg 반복자는 인덱스를 지원하므로 beg[-1]은 beg가 가리키는 문자 바로 전의 문자를 의미합니다.
  • beg 반복자가 문자 하나만큼 이동하여 프로토콜 이름을 찾으면, 구분 기호 뒤에 유효한 문자가 있는지 확인해야 합니다.
  • 구분 기호 뒤에 문자(i[sep.size()])가 존재하고 not_url_char 함수를 통해 해당 문자가 URL의 일부로 유효한지 확인합니다.
  • 발견한 구분 기호가 URL의 일부가 아니면 i 반복자를 구분 기호의 크기만큼 증가시켜 다음 구분 기호를 찾습니다.
  • 전위 감소 연산자를 사용하여 반복자를 감소시키고 새로운 값을 반환합니다.

6.1.3 URL 찾기

25 of 55

라이브러리 알고리즘

과제 점수의 중앙값에 기반하여 학생의 최종 점수를 계산하는 성적 산출 방식이 허점을 악용할 수 있는 가능성이 있습니다. 일부 학생들은 과제를 제출하지 않고도 최종 점수를 높게 유지할 수 있기 때문입니다.

이를 해결하기 위해 두 가지 새로운 성적 산출 방식을 제안합니다:

  1. 평균값을 사용하며, 학생이 제출하지 않은 과제는 0점으로 처리합니다.
  2. 학생이 실제로 제출한 과제 점수들만으로 중앙값을 계산합니다.

6.2 성적 산출 방식 비교

26 of 55

라이브러리 알고리즘

이 방법을 사용하여 모든 과제를 제출한 학생들의 최종 점수 중앙값과 하나 이상의 과제를 빼먹은 학생들의 최종 점수 중앙값을 비교할 수 있습니다.

이 문제를 해결하기 위해 두 가지 단계를 수행합니다:

  1. 모든 학생의 정보를 읽고, 과제를 모두 제출한 학생과 제출하지 않은 학생을 분류합니다.
  2. 각 집단에 성적 산출 방식을 적용하고, 각각의 중앙값을 추출합니다.

6.2 성적 산출 방식 비교

27 of 55

라이브러리 알고리즘

학생 정보를 읽고 분류하는 첫 번째 부분은 이전에 작성한 코드를 활용할 수 있습니다. 4.2.1/112p에서 살펴본 Student_info 타입과 4.2.2/112p에서 살펴본 read 함수를 사용하여 학생 정보를 읽을 수 있습니다. 이번에 새로 작성해야 할 것은 학생이 모든 과제를 제출했는지 확인하는 함수입니다.

6.2.1 학생 분류

28 of 55

라이브러리 알고리즘

did_all_hw 함수는 s.homework 벡터에 저장된 값 중 0이 있는지 확인합니다. 제출한 과제는 최소한의 부분 점수를 부여하므로 0점은 과제를 제출하지 않은 것을 의미합니다. find 함수의 반환 값과 homework.end() 함수의 반환 값을 비교하여 해당 학생이 모든 과제를 제출했는지 확인합니다. find 함수는 찾으려는 값을 발견하지 못했을 때 두 번째 인수를 반환합니다.

6.2.1 학생 분류

29 of 55

라이브러리 알고리즘

이전에 살펴본 read 함수와 did_all_hw 함수를 사용하여 학생 정보를 읽고 분류하는 코드를 간단히 작성할 수 있습니다. 학생 정보를 읽은 후, 해당 학생이 모든 과제를 제출했는지 확인하고 did와 didn't로 이름 붙인 두 벡터 중 하나로 학생 정보를 분류합니다.

새롭게 살펴볼 것은 컨테이너의 멤버 함수 empty입니다. empty 함수는 해당 컨테이너가 비어 있으면 참을 반환하고 그렇지 않으면 거짓을 반환합니다. 컨테이너가 비어 있는지 확인하려면 size 함수의 반환 값이 0인지 확인하는 것보다 empty 함수를 사용하는 것이 더 효율적입니다.

6.2.1 학생 분류

30 of 55

라이브러리 알고리즘

학생 정보를 did 벡터와 didn't 벡터로 분류했으므로, 다음 단계는 학생 데이터를 수학적으로 분석하는 것입니다. 이러한 분석은 세 가지 유형으로 구성되며, 과제를 모두 제출한 학생과 그렇지 않은 학생 두 그룹의 데이터를 각각 분석해야 합니다. 분석은 함수로 만들어야 하며, 분석 결과를 출력할 수 있어야 합니다.

각 분석 결과를 출력하는 함수는 세 가지 분석 함수를 did와 didn't 벡터에 각각 호출하는 작업을 반복해야 합니다. 따라서 출력함수를 작성하려면 분석 함수를 인수로 전달하는 것이 가장 편리한 방법입니다.

6.2.2 수학적 분석

출력함수는 다섯 가지 인수를 필요로 합니다:

  • 출력 스트림
  • 무엇을 출력할지 설명하는 문자열
  • 사용된 분석 함수
  • 분석 대상인 두 개의 벡터

31 of 55

라이브러리 알고리즘

median_analysis 함수는 학생 정보를 포함한 벡터를 인수로 받으며, 학생들의 최종 점수를 기존의 성적 산출 방식에 따라 계산한 후, 그 점수들의 중앙값을 반환하는 함수입니다.

6.2.2 수학적 분석

32 of 55

라이브러리 알고리즘

transform 함수는 3개의 반복자와 1개의 함수를 받으며, 학생 정보 범위의 요소들을 학생들의 최종 점수를 저장할 공간에 변환하는 역할을 합니다. 최종 점수를 저장할 공간을 확보하기 위해 back_inserter 함수를 사용합니다. 이 함수는 필요한 공간을 자동으로 확장하면서 학생들의 최종 점수를 grades 벡터에 추가합니다. transform 함수의 네 번째 인수는 범위 요소를 저장 전 변환할 함수를 지정합니다. 학생들의 최종 점수를 계산한 후, median 함수를 사용하여 점수들의 중앙값을 계산합니다.

6.2.2 수학적 분석

33 of 55

라이브러리 알고리즘

transform 함수가 grade 함수의 예외 처리를 제대로 하지 못하는 문제가 있으며, 이로 인해 median_analysis 함수도 예외를 처리하지 못합니다. transform 함수에서 grade 함수를 직접 전달하지 않고 보조 함수를 사용하면 grade 함수의 적절한 버전을 명시적으로 호출할 수 있어 컴파일러가 원하는 grade 함수를 정확하게 선택할 수 있습니다. 또한 보조 함수를 사용하여 grade 함수에서 발생하는 예외를 처리할 수 있습니다.

이제 grade_aux 함수를 사용하여 분석 함수를 다시작성해봅시다.

6.2.2 수학적 분석

34 of 55

라이브러리 알고리즘

write_analysis 함수는 두 학생 집단을 비교하는 기능을 수행합니다. 함수의 매개변수로 분석 함수를 전달할 수 있으며, 반환 타입은 void로, 반환값이 없다는 의미입니다. 함수에서는 반환문을 생략하거나 값을 반환하지 않고 함수를 종료할 수 있습니다.

6.2.2 수학적 분석

35 of 55

라이브러리 알고리즘

이제 average_analysis와 optimistic_median_analysis 함수를 작성하는 일만 남았습니다.

6.2.2 수학적 분석

36 of 55

라이브러리 알고리즘

average_analysis 함수는 학생들의 최종 점수를 계산할 때 중앙값 대신 평균값을 사용합니다. 이를 위해 average 함수를 작성하여 벡터의 평균값을 계산합니다. average 함수는 accumulate 함수를 사용하여 주어진 범위의 값들을 더하고 총합을 범위의 요소 개수로 나누어 평균값을 계산합니다. average_grade 함수는 average 함수를 사용하여 종합 과제 점수를 계산하고, 이를 grade 함수로 전달하여 최종 점수를 구합니다. average_analysis 함수는 median_analysis 함수와 매우 유사하지만, grade_aux 함수 대신 average_grade 함수를 사용합니다.

6.2.3 과제 점수의 평균값을 사용한 성적 산출

37 of 55

라이브러리 알고리즘

optimistic_median_analysis 함수는 학생들이 제출하지 않은 과제 점수를 제출한 과제 점수와 동일하게 간주하여 계산하는 낙관적 가정에서 이름을 딴 분석 방식입니다. 이러한 가정을 바탕으로 학생들의 제출한 과제 점수의 중앙값을 계산합니다.

optimistic_median 함수는 homework 벡터에서 0이 아닌 요소들을 추출하여 nonzero 벡터에 저장합니다. 0이 아닌 과제 점수가 존재하면 grade 함수를 호출하여 제출한 과제 점수의 중앙값으로 학생의 최종 점수를 계산합니다.

6.2.4 제출한 과제 점수의 중앙값

38 of 55

라이브러리 알고리즘

optimistic_median 함수는 remove_copy 알고리즘을 사용하여 homework 벡터에서 0이 아닌 모든 요소를 nonzero 벡터에 복사합니다. 그런 다음 nonzero 벡터가 비어 있으면 종합 과제 점수를 0점으로 처리하고, 값이 존재하면 nonzero 벡터의 중앙값을 계산하여 학생의 최종 점수로 사용합니다.

분석 결과를 얻기 위해 optimistic_median 함수를 호출합니다.

6.2.4 제출한 과제 점수의 중앙값

39 of 55

라이브러리 알고리즘

5.1/131p에서는 과락한 학생들의 정보를 기존 벡터에서 제거하고 별도의 벡터로 복사하는 문제를 다루었습니다. 다루어야 할 학생 수가 많아질수록 성능이 떨어지는 문제가 있었으며, 이를 해결하기 위해 벡터 대신 리스트를 사용했습니다. 또한 비슷한 성능을 가진 알고리즘을 사용하는 방법을 나중에 살펴보겠다고 언급했습니다.

알고리즘 라이브러리를 사용하여 두 가지 방법으로 문제를 해결할 수 있습니다. 첫 번째는 한 쌍의 라이브러리 알고리즘을 사용하여 모든 요소를 두 번씩 탐색하는 약간 느린 방법이고, 두 번째는 특성화된 라이브러리 알고리즘을 사용하여 모든 요소를 한 번씩 탐색하는 방법입니다.

6.3 학생 분류 다시 살펴보기

40 of 55

라이브러리 알고리즘

첫 번째 접근법은 remove_copy_if 알고리즘을 사용하여 과락한 학생들의 정보를 fail 벡터에 복사하는 방법입니다. 이때 remove_copy_if 알고리즘은 조건을 만족하는 학생들을 fail 벡터에 복사하고, 조건을 만족하지 않는 학생들을 원래 벡터에 남겨둡니다. 이때 사용되는 조건은 fgrade 함수를 호출한 결과를 부정한 값입니다. remove_copy_if 알고리즘은 조건을 만족하는 요소를 복사하지 않고, 만족하지 않는 요소만 복사합니다.

6.3.1 벡터를 두 번 탐색하는 방식

41 of 55

라이브러리 알고리즘

그 다음으로는 erase 함수를 사용하여 원래 벡터에서 과락한 학생들을 제거합니다. 이때 remove_if 알고리즘은 조건을 만족하지 않는 요소들을 원래 벡터에 남겨둡니다. 이렇게 하면 통과한 학생들의 정보만이 벡터에 남게 됩니다.

6.3.1 벡터를 두 번 탐색하는 방식

42 of 55

라이브러리 알고리즘

remove_if 함수는 주어진 서술 함수를 만족하지 않는 요소들을 students 벡터의 처음부터 차례로 복사하여 덮어씌웁니다. 이 예를 들어 7명의 학생이 있다고 가정했을 때, remove_if 함수를 호출하면 처음 두 학생의 정보는 그대로 유지됩니다. 나머지 학생들 중에서 과락한 학생들의 정보를 덮어쓰는 공간을 마련하기 위해 정보가 제거됩니다. 통과한 학생들의 정보는 여유 공간에 복사되어 벡터의 처음부터 차례로 배열됩니다.

6.3.1 벡터를 두 번 탐색하는 방식

43 of 55

라이브러리 알고리즘

remove_if 함수는 제거되지 않은 마지막 요소 이후의 반복자를 반환하여 벡터에서 의미 있는 부분을 알려줍니다. 그 후 erase 함수를 사용하여 students 벡터에서 remove_if가 반환한 반복자와 students.end() 사이의 요소들을 지워 통과한 학생들의 정보만 남깁니다.

6.3.1 벡터를 두 번 탐색하는 방식

44 of 55

라이브러리 알고리즘

partition과 stable_partition 함수는 하나의 순차열을 받아 서술 함수를 만족시키는 요소들을 그렇지 않은 요소들보다 앞쪽에 위치하도록 재배치합니다. 이 알고리즘들은 두 번째 카테고리의 첫 번째 요소를 나타내는 반복자를 반환합니다.

6.3.2 벡터를 한 번 탐색하는 방식

45 of 55

라이브러리 알고리즘

partition 함수는 요소들을 카테고리로 구분하여 재배치하며, stable_partition 함수는 partition 함수와는 달리 각 카테고리 안에서 요소 순서를 유지합니다. 예를 들어 학생들의 정보가 알파벳순으로 정렬되어 있고 각 카테고리 안에서 순서를 유지하려면 partition 함수가 아닌 stable_partition 함수를 사용해야 합니다.

6.3.2 벡터를 한 번 탐색하는 방식

이러한 알고리즘을 사용하면 서술 함수를 만족하는 요소들을 배열의 앞쪽에, 만족하지 않는 요소들을 배열의 뒤쪽에 위치시켜 과락 학생들의 정보를 추출할 수 있습니다.

46 of 55

라이브러리 알고리즘

이해를도우려고앞서 가정한7명의 학생을다시 한번살펴보겠습니다.

stable_partition 함수를 호출하면 과락한 학생들의 요소들이 재배치됩니다. 이때 재배치된 범위에서 과락한 학생들의 요소를 복사해 fail 벡터를 만들고, students 벡터에서 해당 요소들을 삭제합니다.

6.3.2 벡터를 한 번 탐색하는 방식

47 of 55

라이브러리 알고리즘

알고리즘 기반의 문제 해결 방식은 리스트 기반의 문제 해결 방식과 거의 같은 성능을 보여줍니다. 입력 데이터의 크기가 커질수록 알고리즘 및 리스트 기반 방식은 벡터 기반의 erase 함수로 문제를 해결하는 방식보다 훨씬 더 뛰어난 성능을 발휘합니다.

특히 입력 라이브러리가 소비하는 시간으로 최대 75,000개의 데이터가 담긴 입력 파일을 처리할 수 있습니다. extract_fails 함수를 대상으로 두 가지 방식의 부분 성능을 분석해보면, 벡터를 한 번 탐색하는 방식이 두 번 탐색하는 방식보다 약 2배 더 빠르다는 것을 확인할 수 있습니다.

6.3.2 벡터를 한 번 탐색하는 방식

48 of 55

라이브러리 알고리즘

알고리즘, 반복자, 컨테이너를 사용할 때 중요한 점은 알고리즘이 컨테이너의 요소들을 다루는 것이지 컨테이너 자체를 다루는 것은 아니라는 것입니다. 예를 들어 remove_if 함수는 컨테이너의 크기를 바꾸지 않고 요소들을 단순히 복사합니다.

6.4 알고리즘, 컨테이너, 반복자

49 of 55

라이브러리 알고리즘

erase 함수는 벡터의 멤버 함수로, 벡터 크기를 조정하여 원하는 요소만 남깁니다. erase 함수나 insert 함수 같은 컨테이너 연산은 반복자를 무효로 만들 수 있으므로, 반복자의 값을 저장할 때 주의해야 합니다.

partition 함수나 remove_if 함수는 특정 반복자가 나타내는 요소를 바꿉니다. 따라서 이러한 함수를 실행한 후 특정 요소를 나타내는 반복자를 계속 사용하기 어렵습니다.

6.4 알고리즘, 컨테이너, 반복자

50 of 55

라이브러리 알고리즘

6.5 핵심 정리

51 of 55

라이브러리 알고리즘

6.5 핵심 정리

52 of 55

라이브러리 알고리즘

6.5 핵심 정리

53 of 55

라이브러리 알고리즘

6.5 핵심 정리

54 of 55

라이브러리 알고리즘

연습문제

55 of 55

Thanks

Please keep this slide for attribution

CREDITS: This presentation template was created by Slidesgo, and includes icons by Flaticon and infographics & images by Freepik