코딩처음입니다만
군집 로봇, 모션 플래닝, 자율주행에 관심있는 SW 개발자입니다 !
코딩처음입니다만★/C++ (19)
[C++] 각도 제한 [ -pi ~ pi ]
728x90
반응형

Robotics 분야에서 각도 표현은 굉장히 중요하다 !!
로봇의 heading angle이 200도라도 표현하기 보단 -160도라고 표현하는 것이 일반적이다.

즉, -180 ~ 180도 사이에서 모든 각도를 표현할 수 있어야 한다. 따라서 그 사이값으로 각도를 제한해보자.

1. pi_to_pi 코드

double pi_to_pi(double angle)
{
  while(angle >= M_PI)
  {
    angle -= 2.*M_PI;

    while(angle < -M_PI)
    angle += 2.*M_PI;

    return angle;
  }
}

이렇게 하면 -180도 ~ 180도 사이로 각도가 들어온다.

반응형
  Comments,     Trackbacks
[C++] Quaternion to Euler 상호 변환
728x90
반응형

Robotics 분야에서 각도 표현에 자주 사용되는 것이 1) Quaternion과 2) Euler이다.

일반적으로 roll, pitch, yaw로 표현되는 Euler 각도에 다들 익숙할 것인데, 짐벌락이라는 한계때문에 각도는 Quaternion으로 계산하곤 한다. 거두절미하고 바로 코드를 보자

 

1. Quaternion to Euler 코드

#include <tf/tf.h>

tf::Quaternion q(
        msg->pose.pose.orientation.x,
        msg->pose.pose.orientation.y,
        msg->pose.pose.orientation.z,
        msg->pose.pose.orientation.w);
tf::Matrix3x3 m(q);
double roll, pitch, yaw;
m.getRPY(roll, pitch, yaw);

반대도 알아야겠죠~~

 

2. Euler to Quaternion 코드

#include <tf2/LinearMath/Quaternion.h>

// input : 오일러 각(roll, pitch, yaw)
tf2::Quaternion quat;
quat.setRPY(roll, pitch, yaw);
quat.normalize();
반응형
  Comments,     Trackbacks
[C++] 알고리즘 수행 시간 측정
728x90
반응형

1. 개요

알고리즘을 개발하다 보면 수행 시간이 얼마나 걸리느냐가 굉장히 중요한 요소 중 하나이다. 그렇다면 코드 단위에서 실행 시간을 어떻게 측정할까?

2. 간단한 예시

아주 간단하게는 <ctime>clock()을 사용하는 방법이 있다.

#include <ctime>

clock_t start, end;

start = clock();
// 이 공간에 측정하고자 하는 알고리즘을 넣는다.
end = clock();

std::cout << "실행시간: " << end - start  << " ms" << std::endl;

하지만 다음 설명할 방법이 내가 제일 좋아하는 방법이며, 헤더 파일로 하나 만들어두면 어느 프로그램을 개발할 때나 사용할 수 있다 !

3. 정확한 예시

클래스를 이용하여 아래와 같이 미리 선언해둔다.

class TicToc
{
  public:
    TicToc()
    {
        tic();
    }

    void tic()
    {
        start = std::chrono::system_clock::now();
    }

    double toc()
    {
        end = std::chrono::system_clock::now();
        std::chrono::duration<double> elapsed_seconds = end - start;
        return elapsed_seconds.count() * 1000;
    }

  private:
    std::chrono::time_point<std::chrono::system_clock> start, end;
};

이후 사용하는 곳에서는 아래와 같이 사용하면 끝 !!

TicToc t_algorithm;
// 측정하고자 하는 알고리즘
std::cout << "실행시간: " << t_algorithm.toc() << std::endl;

단위는 [ms]이다.

반응형
  Comments,     Trackbacks
[C++] 코딩테스트 때 자주 사용하는 STL 및 알고리즘 정리
728x90
반응형

1. 개요

코딩테스트 문제를 풀다보면 STL을 많이 사용하게 된다. 내가 자주 사용하는 것들을 까먹지 않게 좀 정리해본다.

원래 SW를 개발할 때는 namespace를 붙이지 않고 개발하는 게 좋지만, 코딩테스트의 편의성을 위해서 아래와 같이 선언해준다.

using namespace std;

대부분의 자료 구조는 4가지의 기본 연산을 갖고 있다. 따라서 지금부터 나열할 STL은 아래의 기준으로 나열한다.

  1. 읽기 : Read
  2. 탐색 : Search
  3. 삽입 : Insert
  4. 삭제 : Delete

여기서 어느 정도 눈치챈 사람들이 있겠지만, 앞으로 나올 STL은 위의 4가지 연산마다 저마다의 장점을 갖고 있다.

 

2. Vector

배열 관련하여 사용할 때는 vector를 줄구장창 사용할 것이다. 아래와 같이 코드가 선언되어 있다고 가정하자.

#include <vector>

using namespace std;

int main()
{
  vector<int> v;

  return 0;
}

2-A. 읽기

읽기는 대괄호를 이용해서 해당 값을 읽으면 된다.

cout << v[0] << endl;

2-B. 탐색

탐색을 위해서는 std::find를 이용하여 2가지 방법이 있다.

// 1. 반복자(iterator) 찾기
auto iter = std::find(v.begin(), v.end(), 178);

// 2. 인덱스(index) 찾기
auto idx = std::find(v.begin(), v.end(), 178) - v.begin();

// 만약에 없으면?
if ( std::find(v.begin(), v.end, 178()) == v.end() );

std::find() 함수를 이용하며, 해당 요소가 vector에 존재하지 않으면 v.end()를 반환한다. 이 함수의 반환값은 반복자이며, 해당 반복자에 해당하는 인덱스를 얻고 싶다면 첫번째 반복자 v.begin()을 빼주면 된다.

Iterator를 이용하여 인덱스를 얻는 방법이 하나 더 있다. 바로 distance() 함수를 이용하여 아래와 같이 사용가능하다.

#include <algorithm>

auto index = std::distance(v.begin(), iter);

2-C. 삽입

삽입은 insert()를 이용하며 아래와 같이 사용한다.

v.insert( v.begin(), 100 ); // 100을 제일 처음에 추가
v.insert( v.begin()+5, 5 ); // 5를 (처음+5)에 추가
v.insert( v.end(), 9 ); // 9를 제일 마지막에 추가

2-D. 삭제

삭제는 erase()를 이용하며 아래와 같이 사용한다.

v.erase( v.begin() ); // 제일 첫 번째 원소 제거
v.erase( v.end()-1 ); // 마지막에서 한 칸 앞에 원소 제거

3. Map

<map> 정말 많이 사용한다. 전화번호부, 메뉴판과 같이 이름-전화번호 혹은 메뉴-가격 이렇게 key-value의 쌍으로 표현할 수 있을 때라던지, 투표소에서 누가 투표를 안 했는가?에 대한 문제도 손쉽게 해결 가능 하다. 기본 자료 구조는 해시 테이블(hash table) 또는 트리(tree)이다. 기본 선언은 아래와 같다.

#include <map>

// 또는

#include <unordered_map> // 필자는 이걸 더 선호한다.

/*
  사용하는 곳 안에서 (대게 main문)
  key-value의 쌍(pair)로 선언해야 하므로 2가지를 <> 안에 넣어줘야 한다.
*/
unordered_map<string, int> um;
um["bigbigpark"] = 1234;   // 문자열 bigbigpark이라는 key에 저장된 value는 정수형 1234이다.

3-A. 탐색

탐색은 find 메소드를 이용해준다. 위에 vectorstd::find를 이용해주었다. 차이점을 주의하자.

if ( um.find("bigbigpark") != um.end() ) // 찾지 못한다면 end()를 반환

// 만약 찾았으면?
/*
  앞서 말했다시피 map 자료구조는 key-value 두 가지로 구성되어 있다.
  따라서 찾았을 때 두 가지 모두 접근이 가능하다.
*/
auto iter = um.find("bigbigpark");

auto key   = iter->first;  // key에 접근한다. -> 문자열 bigbigpark 반환.
auto value = iter->second; // value에 접근한다. -> 정수형 1234 반환.

3-B. 삽입

여러 방법이 있지만 나는 아래와 같이 사용한다.

um["Eric"] = 9999;

3-C. 삭제

key를 기준으로 삭제한다.

um.erase["Amanda"]; // 없으면 um.end()를 반환한다.

3-D. 반복문 순회

// vector의 경우 아래와 같이 반복문을 순회하며 값에 접근이 가능하다.
vector<int> v = {1, 2, 3, 4, 5};
for(auto n: v)
{
  std::cout << n << std::endl;
}

// map의 경우 어떨까?
unordered_map<string, int> um;
um["A"] = 1111;
um["B"] = 2222;

for(auto it: um)
{
  // key-value의 특성에 따라 접근하면 된다.
  std::cout << "key  : " << it->first << std::endl; // 문자열 A, B 출력
  std::cout << "value: " << it->second << std::endl; // 정수형 1111, 2222 출력
}

4. 문자열(String)

애증의 녀석. C++ 코테에 문자열은 빠지면 정말 섭섭하다. 큰 수 입력받을 때 9999999999999999도 string으로 받으면 된다. 아래와 같이 선언한다.

#include <string>

string s = "bigbigpark";

4-A. 찾기

find() 메소드를 사용하긴 하지만, 찾지 못하였을 때 반환값이 다름에 주의하자.

if ( s.find("big") != std::string::npos )

find() 메소드에는 다음과 같은 특성들이 있다.

  1. 못 찾았을 시 반환값은 std::string::npos이다.
  2. 찾았을 때 반환값은 해당 인덱스이다.
  3. 중복되는 문자의 경우 제일 먼저 등장한 인덱스를 반환해준다.

4-B. 교체

replace() 메소드를 사용한다.

int pos = 3;
int length = 5;
string 문자열 = "name";

/*
  pos위치에서 length길이만큼 문자열로 채우게 된다.
  pos위치와 length와 문자열 길이의 차이로 인한 에러는.. 각자 센스로 해결하자.
*/
s.replace(pos, length, 문자열);

4-C. 삭제

// 1.
s.erase(pos, len); // pos위치에서 len길이만큼 삭제한다.

// 2.
s.erase(s.begin()+2); // 처음 위치+2 에 있는 녀석을 삭제한다.

// 3.
s.erase(s.begin()+1, s.end()-3) // 첫 번째 위치 ~ 두 번째 위치까지의 요소들을 삭제한다.

자 ! 여기서 특정 문자만 제거하기를 해보자

/*
  A는 문자열이 아닌 char 형으로 들어감에 주의하자.
  문자열 s에서 'A'가 들어간 부분을 모조리 지워준다.
*/
s.erase( std::remove(s.begin(), s.end(), 'A'), s.end() )

4-D. 문자열 부분 추출

삽입, 삭제 말고 이미 존재하는 문자열에서 조금만 떼올 순 없나?
있다.

// 1. 전체 가져오기
string new_string = s.substr();

// 2. 5번 인덱스부터 끝까지 가져오기
string new_string = s.substr(5);

// 3. 5번 인덱스부터 1길이만큼 가져오기
string new_string = s.substr(5, 1);

4-E. 문자열 형변환

정말정말 많이 나온다. 자주 사용하면 거의 외울 수 있을 듯 ?

// char -> string, 문자열 객체 선언후 그냥 더하기.
char ch = 'A';
string temp = "";
temp += ch;

// string -> char, 그냥 인덱싱 하자.
string str = "bigbigpark";
char c = str[0];

// int -> string
int num = 5;
string str = to_string(num);

// string -> int 또는 double
string n_str = "1122";
int n1 = stoi(n_str);
double n2 = stod(n_str);

// char -> int
// 자릿수 연산에 많이 등장한다.
char ch = '9';
int n = ch - '0'; // '0'를 빼게 되면 보이는 그대로 숫자가 저장되게 된다.

추가적으로 ASCII 숫자 맵핑 관계를 알아보자

  1. AZ: 6590
  2. az: 97122

4-F. 문자열 공백으로 분리

공백 말고 다른 구분자(delimeter)으로도 분리 가능!

#include <sstream>
#include <string>

// 공백이 포함된 문자열이 있다고 가정하자.
string str = "hello world this is park";
istringstream ss(str);

string word;

// getline() 3번째 인자에 구분자를 설정해주자. 아래 예시는 공백.
while ( getline(ss, word, ' ') )
{
  std::cout << word << std::endl;
}

여기까지 내가 코테를 봐 오면서 시험 쳐온 현황이다. 다음은 함수를 몇 가지 알아보자.

5. 정렬

숫자를 오름차순/내림차순으로 정렬해보자.

#include <algorithm>
#include <vector>

using namespace std;

vector<int> v = {4, 2, 3, 5, 1};

// 오름차순
sort( v.begin(), v.end() );

// 내림차순
sort( v.begin(), v.end(), greater<>() );

6. 합

vector 요소의 합을 구해보자. 물론 반복문 선언해서 하나하나 다 더해도 됨.

#include <numeric>

// 합
auto sum = accumulate( v.begin(), v.end(), 0);

// 평균
auto avg = sum / n.size();

7. 이진수 연산

이진수를 위한 라이브러리가 있는지 최근에 알았다. 정말 편리하구나 ㅜㅜ

7-A. 선언 및 사용

#include <bitset>
/*
  1) 이진수 형태의 문자열을 바로 사용 가능.
  2) 십진수를 이진수로 바로 사용 가능.

  이진수 형태의 덧셈 뺄셈 가능
*/
int n1 = 544132;
string n2 = "01110111011";

bitset<16> bit; // 모든 16자리 비트가 0으로 초기화됨.
bitset<16> bit (n1); // 정수형을 바로 비트연산 가능
bitset<16> bit (n2); // 문자열을 바로 비트연산 가능

7-B. 이진수를 다시 문자열/정수형으로 변환

auto i_num = bit.u_long();    // 이진수를 정수형으로 변환
auto s_num = bit.to_string(); // 이진수를 문자열로 변환

7-C. 메소드들

  1. flip(n) : n번째 비트 반전 시키기
  2. test(n) : n번째 비트가 1인지 검사 (맞으면 true, 다르면 false) 반환
반응형
  Comments,     Trackbacks
[C++] 열거형 enum 손쉽게 쓰자
728x90
반응형

안녕하세요~ 뉴기니아 입니다!

오늘은 enum자료형에 대해서 알아보겠습니다.

 

 

 enum 이란?

 

- 열거형란 데이터를 일정한 관계에 따라서 쭈욱 나열한 형태를 말합니다.

이게 도대체 무슨 말이냐면 예를들어 메뉴 고르기 프로그램이 있다고 칩니다.

보통이면 int a = 0을 해서

조건문을 이용하여,

a = 0 → 삽입

a = 1 → 삭제

a = 2 → 출력

으로 구성할 것입니다.

하지만 0, 1, 2는 한눈에 뭐가 뭔지 알아보기 힘듭니다.

따라서 코드 가독성을 좋게하고 디버깅을 수월하게 하기 위해서 enum을 사용합니다.

 

 

제가 선언한 enum 구조체 안에는 다음과 같은 멤버가 있습니다.

여기서 선언할 때 콤마(,)로 선언해주시는 거 잊지마시고 마지막에는 콤마(,)가 없다는 것도 유의해주세요!

MM_NONE,

MM_INSERT,

MM_DELETE,

MM_PRINT,

MM_EXIT

 

각 변수에 마우스 커서를 대시면 각 변수가 갖는 값들이 출력됩니다

 

여기서 열거형의 특성이 나타납니다.

제일 첫 번째로 선언한 변수는 무조건 값 0을 가집니다.

그 뒤로 콤마를 쓰게 되면 +1을 하면서 번호가 할당되게 됩니다.

 

하지만 나는 제일 첫 번째 0 싫은데? 하시는 분들은

 

이렇게 MM_NONE = 2처럼 첫 숫자를 할당해줄 수 있습니다.

실제 코드를 통해서 이게 어떻게 동작하는지 봅시다

 

 

main문
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 
#include <iostream>
 
using namespace std;
 
int main()
{
    int choice = 0;
    
    do
    {
        cout << "1. 데이터 삽입" << endl;
        cout << "2. 데이터 삭제" << endl;
        cout << "3. 데이터 출력" << endl;
        cout << "4. 프로그램 종료" << endl;
        cout << " : ";
        cin >> choice;
 
        if (choice == 4return 0;
 
    } while (choice < 1 || choice > 3);
    
 
    return 0;
}
 
 
cs

 

어떤 코드인 지 감이 오시나요?

사용자로부터 1~4까지의 값을 입력으로 받고 choice에 저장합니다.

 

다음 코드를 보실까요?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
int main()
{
    int choice = 0;
    
    do
    {
        cout << "1. 데이터 삽입" << endl;
        cout << "2. 데이터 삭제" << endl;
        cout << "3. 데이터 출력" << endl;
        cout << "4. 프로그램 종료" << endl;
        cout << " : ";
        cin >> choice;
 
        if (choice == 4return 0;
        system("cls");
    } while (choice < 1 || choice > 3);
 
    switch (choice)
    {
    case 1:
        cout << "삽입!" << endl;
        break;
 
    case 2:
        cout << "삭제!" << endl;
        break;
 
    case 3:
        cout << "출력" << endl;
        break;
 
    default:
        break;
    }
    
 
    return 0;
}
cs

 

아래 쪽 switch 문을 주목해서 보시기 바랍니다.

choice의 결과에 따라서 '삽입', '삭제', '출력' 중 한 문구를 출력하는 함수입니다.

하지만 프로그램을 딱 봤을 때는 case 1이 무엇을 뜻하는 지는 작성한 사람 말고는 잘 모르겠죠?

 

여기서 아까 선언한 enum을 사용할겁니다 ㅎㅎ

 

※ MM은 제가 임의로 정한 main menu의 약자입니다

자 코드가 어떠신가요?

choice의 값에 따라서 각각 어떤 역할을 수행할 지 직관적으로 파악이 가능해졌습니다!

 

추가로 처음 choice 값을 받을 때 

 

빨간색 네모친 부분에서 4를 또 enum을 이용하여

 

 

아래 사진처럼 표현이 가능합니다!

 

여기까지가 enum에 대한 설명이었습니다

반응형
  Comments,     Trackbacks