이진 검색
이진 검색은 정렬된 시퀀스에서만 사용할 수 있지만, 시간 복잡도는 선형 검색보다 효율적입니다.
이진 검색의 알고리즘은 다음과 같습니다.
1. 시퀀스의 범위를 측정합니다.
2. 시퀀스의 가운데 원소와 찾고자 하는 값을 비교합니다. (만일 두 원소가 같다면 true를 반환)
3. 가운데 원소와 찾고자 하는 값을 비교하여
* 가운데 원소보다 값이 작다면, 가운데 원소부터 그 이상의 원소를 제거합니다.
* 반대로 작다면, 가운데 원소부터 그 이하의 원소를 제거합니다.
4. 범위를 다시 측정하여 측정한 범위가 1 초과이면 2 번쨰 과정을 반복합니다.(만일 이 과정을 수행할 수 없다면, 시퀀스에는 찾고자 한 원소가 존재하지 않는 것이기에 false를 반환합니다.)
다음은 이진 검색의 C++ 코드입니다.
bool binary_search(int N, vector<int>& S)
{
auto first = S.begin();
auto last = S.end();
while(true)
{
auto lenth = distance(first, last);
auto middle_index = first+floor(lenth/2);
auto middle = *(middle_index);
if(middle==N)
return true;
if(middle>N)
advance(last, middle_index);
else
advance(first, middle_index);
if(lenth==1)
return false;
}
}
이제 두 가지 경우의 소모 시간을 비교해보겠습니다.
#include <iostream>
#include <vector>
#include <chrono>
#include <random>
#include <algorithm>
using namespace std;
bool linear_search(int N, vector<int>& S)
{
for(auto i : S)
{
if(i==N)
return true;
}
return false;
}
bool binary_search(int N, vector<int>& S)
{
auto first = S.begin();
auto last = S.end();
while(true)
{
auto range_length = distance(first, last);
auto middle_index = range_length / 2;
auto middle = *(first + middle_index);
if(middle == N)
return true;
else if(middle > N)
advance(last, -middle_index);
if(middle < N)
advance(first, middle_index);
if(range_length == 1)
return false;
}
}
void search_test(int size, int N)
{
vector<int> S;
random_device rd;
mt19937 rand(rd());
uniform_int_distribution<mt19937::result_type> uniform_dist(1,size);
for(auto i =0; i<size;i++)
S.push_back(uniform_dist(rand));
sort(S.begin(),S.end());
chrono::steady_clock::time_point begin = chrono::steady_clock::now();
bool search_result=binary_search(N,S);
chrono::steady_clock::time_point end = chrono::steady_clock::now();
auto diff = std::chrono::duration_cast<chrono::microseconds>(end-begin);
cout<<"이진 검색 수행 시간:"<<diff.count()<<endl;
if(search_result)
cout<<"이진 검색으로 원소를 찾았습니다."<<endl;
else
cout<<"이진 검색으로 원소를 찾지 못했습니다."<<endl;
chrono::steady_clock::time_point begin1 = chrono::steady_clock::now();
bool search_result1=linear_search(N,S);
chrono::steady_clock::time_point end1 = chrono::steady_clock::now();
auto diff1 = chrono::duration_cast<chrono::microseconds>(end1-begin1);
cout<<"선형 검색 수행 시간:"<<diff1.count()<<endl;
if(search_result1)
cout<<"선형 검색으로 원소를 찾았습니다."<<endl;
else
cout<<"선형 검색으로 원소를 찾지 못했습니다."<<endl;
cout<<"-----------------------------------------"<<endl;
}
int main()
{
search_test(100000, 36543);
search_test(1000000, 36543);
search_test(10000000, 36543);
return 0;
}
이진 검색의 수행시간은 0에 수렴하지만, 선형 검색의 경우 범위가 늘어남에 따라서 시간도 비례해서 늘어나는 모습을 볼 수 있다.
sort
C++ 표준 라이브러리에서 제공하는 정렬 알고리즘이며, 임의 접근 반복자의 범위내의 요소들을 정렬합니다. 기본적으로는 올림차순이나 원한다면 내림차순도 가능합니다.
함수의 기본형은 다음과 같습니다.
template<class RandomIt>
void sort(RandomIt first, RandomIt last);
template<class RandomIt, class Compare>
void sort(RandomIt first, RandomIt last, Compare comp);
#include <chrono>
이 헤더파일은 시간을 다루기 위해 사용하는 헤더파일입니다. 포함되어 있는 클래스는 다음과 같습니다.
chrono::duration
chrono::time_point
chrono::system_clock
chrono::high_resolution_clock
chrono::steady_clock
Rep은 자료형, Period는 시간의 간격의 단위를 나타낸다.
- chrono::time_point는 특정 시간을 나타냅니다. 주로 chrono::time_point
형태로 사용하며
Clock은 측정하는 시계의 자료형을, Duration는 시각을 나타내는 시계의 자료형을 나타냅니다. - chrono::system_clock는 현제 시간을 내타내며, 다음처럼 사용합니다. chrono::time_point\
- chrono::high_resolution_clock는 고해상도의 시간을 측정하는데 사용되며, 다음과 같이 사용합니다.
chrono::time_point\
- std::chrono::steady_clock는 위의 클래스가 고해상도라면, 이 친구는 안정적인 속성을 지닌 시계이며 다음과 같이 사용합니다.
chrono::time_point\
#include <random>
이 헤더파일은 난수를 생성하기 위해 사용되는 헤더파일이며, 포함된 클래스는 다음과 같습니다.
random_device
default_random_engine
uniform_int_distribution
uniform_real_distribution
normal_distribution
bernoulli_distribution
mt19937 rand(rd())
- random_device는 하드웨어 기반의 난수 생성 장치에 접근할 수 있는 인터페이스를 제공하는 클래스입니다.
반환형은 random_device::result_type이고, result_type은 일반적으로 부호가 없는 정수 타입입니다. - default_random_engine는 난수 생성을 담당하는 클래스입니다
반환형은 일반적으로 부호가 없는 정수 입니다. - uniform_int_distribution는 균일 분포에서 정수를 생성하는데 사용됩니다.
반환형은 템플릿에 따라 다르지만 일반적으로 정수입니다. - uniform_real_distribution는 균일 분포에서 실수를 생성하는데 사용됩니다.
반환형은 템플릿에 따라 다르지만 일반적으로 실수입니다. - normal_distribution는 정규 분포에서 난수를 생성하는데 사용됩니다.
반환형은 템플릿에 따라 다르지만 일반적으로 생성된 난수입니다. - bernoulli_distribution는 베르누이 분포에서 난수를 생성하는데 사용됩니다.
반환형은 템플릿에 따라 다르지만 일반적으로 bool입니다. - mt19937 rand(rd())은 Mersenne Twister 알고리즘을 구현한 난수 생성 엔진 중 하나입니다. C에서의 srand과 유사하다고 생각하면 되겠습니다.
#include <algorithm>
이 헤더파일은 4장에서 다시 볼 것이기에 간단히만 알아보도록 하겠습니다.