헤더파일

노말분포 & 시퀀스 컨테이너 본문

C++

노말분포 & 시퀀스 컨테이너

헤더파일 2018. 4. 16. 14:40

default_random_engine 의 객체는 함수처럼 호출할 수 있다.->()연산자가 오버로딩되어있다.

dre() 이렇게만 써도 랜덤 값을 뽑아낼 수 있다. 즉 호출가능객체의 일종인ㄴ 함수객체이다.

함수객체는 상태(퍼블릭맴버)를 갖는다.


uniform_int_distribution<int> uid(0, 1000) 

uid(dre) 값은 랜덤엔진에서 나온 값을 균일하게 범위안에 위치하게 하는 것이다.


함수를 부를때마다 리턴값을 다르기 때문에 값은 함수 밖에 저장되있다.

이런 함수는 매번 부를 때마다 값이 달라지기 때문에 순수한 함수가 아니다.



분포는 normal_distribute와 uniform_distribute가 있다.



int main()

{

default_random_engine dre;

random_device rd;

dre.seed(rd());

normal_distribution<double> distribute(0,0.1);

//uniform_int_distribution<int> uid(0, 20);

//distribute(dre) -> 대부분의 값은 -1~1사이에 있을거다

//0~10사이의 정수값으로 만들어보자.




array<int, 21> a = {0};


for (int i = 0; i < 1000000; ++i)

{

// int val = uid(dre);


double val = distribute(dre);

if (-1.0 <= val && val <= 1.0)

{

val += 1.0;

val *= 10.0;

a[(int)val]++;

}

}


for (int i = 0; i <a.size(); ++i)

cout<<i<<" : "<<a[i]<<endl;


}


uniform_distribute는 균일한 값이 있다.



normal_distribute는 가우시안 분포를 따라서 값이 나온다.



노멀분포(평균, 표준편차)

평균은 가장 값이 많이 몰리는 값이고 -1~1범위에서 값이 존재합니다. -1~1을 벗어날 수 있지만 표준편차가 아주 클 경우입니다. 이렇게 얻은 -1~1값을 0~100 사이의 값으로 바꾸려면 이런 과정을 거쳐야 합니다.


노멀분포값 N


N이 -1~1사인지 조건문으로 확인합니다.

N +=1 ->-1~1범위를 0~2범위로 바꿉니다.

N *= 50 최대값의 절반을 곱해 0~100값으로 만들어줍니다.




덱은 윈도우 메시지를 처리할 때 쓰이는 자료구조입니다.

앞뒤 모두 꺼내고 넣을 수 있다는 장점이 있습니다.

array와 같이 at(번호)로 접근한다면 예외처리를 할 수 있습니다.


deque<int> d;

try

{

d.at(0) = 100;

cout << d.at(0) << endl;

}

catch (exception& e)

{

cout << e.what() << endl;

}


무효화된 반복자


스택에 1이라는 정수를 담는 벡터가 생겼다고 생각해봅시다. begin() 함수에 호출해서 반복자를 저장하고 안에 값을 불렀다면 1이 불립니다. 하지만 이제 새로 2를 넣는다면 벡터는 스택에 다른 공간에 메모리를 잡고 거기에 1과 2가 씌이게 됩니다.  따라서 반복자를 새로 begin()으로 받아오지 않는다면 반복자를 증가시킨다고 2를 찾아갈 순 없습니다.


vector<int> v;


v.push_back(1);

vector<int>::iterator iter = v.begin();

cout << *iter << endl;


v.push_back(2);

cout << *iter << endl;

iter++;

cout << *iter << endl;



리스트


리스트는 구조상 노드가 앞뒤의 위치를 기억하고 있기 때문에 무효화된 반복자 문제가 없습니다.


list<int> v;


v.push_back(1);

list<int>::iterator iter = v.begin();

cout << *iter << endl;


v.push_back(2);

cout << *iter << endl;

iter++;

cout << *iter << endl;


list는 이중연결리스트입니다 그래서 리스트의 크기는 노드를 상호 연결하기 위한 포인터가 있어야합니다. 임의의 위치에서 하는 원소의 삽입, 삭제가 상수시간의 복잡도를 가집니다.


list<int> v{ 1,2,4};

list<int> l{3,6,7,8 };


v.merge(l);


이렇게 정렬된 리스트를 merge 하면 정렬된 순서를 유지한다는 장점도 있습니다.

추가로 리스트 만의 sort함수와 remove_if함수가 있고 이걸 사용하는 게 성능살에 이점이 있습니다.



STL에서 값을 검색


vector<int>::const_iterator f(const vector<int>& data,const int find)

{

for (auto iter = data.cbegin(); iter != data.cend(); ++iter)

{

if (*iter == find)

{

return iter;

}

}


return data.cend();

}


int main()

{

vector<int> v{ 1,2,3,4,5,6,7,8,9,10 };

vector<int>::const_iterator p = f(v, 6);

if (p != v.end())

{

cout<< distance(v.cbegin(), p)+1 << endl;

}

}


template 함수를 쓰면 이렇게 쓰면 됩니다.


vector<int>::const_iterator p = find(v.begin(),v.end() ,6);


iterator의 범위를 받아서 for문을 돌면서 같으면 해당 위치 iterator를 반환하고 없으면 마지막 iterator를 반환합니다. 그런데 만약 int형이 아니라 내가 만든 클래스를 넣고 싶으면 어떻게 해야 할까요? 오류문을 보면 이렇습니다.


이항 '==': 왼쪽 피연산자로 'Model' 형식을 사용하는 연산자가 없거나 허용되는 변환이 없습니다.


 컴파일러가 find함수에서 같다는 조건을 판단할 ==연산자가 오버로딩되어있지 않기 때문에 문제가 발생하는 것입니다. 따라서 이런식으로 연산자를 오버로딩 해줘야 합니다.


int gid = 0;


class Model

{

public:

int size{ 0 };

char* data = nullptr;

int id;

public:

Model() :id{ ++gid } { cout << id << " - 디폴트 생성" << size << "바이트: " << static_cast<void*>(data) << endl; }

Model(const int& num) :size{ num }, id{ ++gid }

{

data = new char[num];

cout << id << " - (int)생성 : " << size << "바이트: " << static_cast<void*>(data) << endl;;

}

~Model()

{

cout << id << "- 소멸자:  " << size << "바이트: " << static_cast<void*>(data) << endl;

if (data != nullptr)

{

delete[] data;

data = nullptr;

}

}

Model(const Model& other) :size{ other.size }, id{ ++gid }

{

data = new char[size];

memcpy(data, other.data, sizeof(other.data));

cout << id << " - 복사 생성" << size << " 바이트: " << static_cast<void*>(data) << endl;;

}

//이동 생성자 - C++14에서 이함수는 예외를 던지면 안된다.

Model(Model&& other) noexcept:size{ other.size }, id{ ++gid }

{

data = other.data;

//이동의 대상이 되는 클래스는 well-known상태로 만들어 두자.

other.size = 0;

other.data = nullptr;

cout << id << " - 이동 생성" << size << " 바이트: " << static_cast<void*>(data) << endl;

}

Model& operator=(const Model& other)

{

if (this == &other)

return *this;

if (data != nullptr)

delete[] data;


size = other.size;

data = new char[size];

memcpy(data, other.data, sizeof(other.data));

cout << "= 복사" << size << " 바이트: " << static_cast<void*>(data) << endl;

return  *this;

}

Model& operator=(Model&& other) noexcept

{

if (this == &other)

return *this;

if (data != nullptr)

delete[] data;


size = other.size;

data = other.data;


other.size = 0;

other.data = nullptr;


cout << "= 이동" << size << " 바이트: " << static_cast<void*>(data) << endl;

return  *this;

}

bool operator==( const Model& rhs)

{

return size == rhs.size;

}

};


 위 같은 Model 클래스를 썼을 때 서로 같은 걸 size로 판단한다면 이런식으로 연산자를 오버로딩 하면 됩니다.


bool operator==( const Model& rhs)

{

return size == rhs.size;

}


STL에서는 조건에 맞는 걸 검색하는 템플릿 함수도 있습니다.


auto p = find_if(v.begin(), v.end(), [](const Model& a) 

{

return a.id == 20;

});


find_if의 3번째 인자는 호출가능객체를 넘겨줘서 조건을 판단합니다. 람다와 함수포인터 둘다 사용할 수 있지만 가독성면이나 속도면이나 람다 우월합니다. 람다는 컴파일러가 inline화를 할 수 있기 때문입니다.


STL에서 값을 삭제


remove_if함수를 써서 조건에 맞는 값을 삭제할 수 있습니다. 짝수만 지우게 됩니다.


vector<int> v{ 1,2,3,4,5,6,7,8};

remove_if(v.begin(), v.end(),[](int param)

{

return !(param&1);

}

);


for (int i=0;i<v.size();i++)

{

cout << v[i] << endl;

}


하지만 실제 값을 찍어보면 값이 삭제 되지 않고 뒤에 이상한 값이 찍힙니다.


알고리즘 함수는 벡터의 private 정보에 접근할 수 없어서 벡터의 size, capacity 등에 접근할 수 없습니다. 그래서 단순히 값을 지운자리에 값을 땡기는 식으로만 작동합니다. 그래서 remove_if함수가 반환하는 반복자부터 벡터의 끝까지 값을 지워야 합니다.

※insert 함수는 컨테이너에 맞게 변형됩니다.


v.erase(remove_if(v.begin(), v.end(), [](int param)

{

return !(param & 1);

}

) ,  v.end());


이렇게 erase함수와 remove_if를 합쳐서 작성해줘야합니다.

자료구조가 STL에 작성되어 있지만 굳이 자료구조를 배우는 이유는 STL을 더 잘 쓸 수 있기 때문입니다. 리스트를 지울때는 벡터처럼 짜지 않고 이렇게 작성합니다.


v.remove_if([](int param)

{

return !(param & 1);

});


리스트는 멤버함수로 지우는 함수를 갖고 있고 이 함수로 지우면 상수시간으로 지울 수 있습니다.

이렇듯 특정 컨테이너에서 전역알고리즘함수에 해당하는 멤버함수가 존재한다면 무조거 멤버함수를 사용해야합니다.

'C++' 카테고리의 다른 글

Map을 이용한 파일입출력  (0) 2018.05.14
연관 컨테이너  (0) 2018.04.30
컨테이너 - 3  (0) 2018.04.12
컨테이너 - 2  (0) 2018.04.09
컨테이너 -1  (0) 2018.04.05
Comments