도서 : 초보자를 위한 C++ 프로그래밍(성안당)

지음 : 강성수 지음


  • 포인터와 1차원 배열
    • 형식 : 배열명[첨자]는 *(배열명 + 첨자)와 같은 결과를 갖는다.
    • str[3]; // 1차원 배열 -> *(str + 3); //1차원 배열을 포인터 수식으로 표현
    • int array[3] = { 5, 10, 15}; -> int* pt; -> pt = array; //array는 배열명이므로 &array[0]과 동일
  • 포인터와 2차원 배열
    • 형식 : 배열명[첨자1][첨자2]는 *(*(배열명 + 첨자1) + 첨자2)로 변경한다.
    • int a[2][3] = ... ; -> int (*p)[3]; //2차원 배열의 포인터 선언 -> p = a; //포인터 초기화
  • 3차원 배열 역시 위와 비슷하게 int (*p)[2][3]; 처럼 늘려주면 된다.
  • p, &p[0], *p, **p 모두 시작 번지 값으로 같다.

  • 포인터와 함수
    • 포인터를 사용하면 다른 함수의 변수를 간접으로 접근할 수 있으므로 함수에 변수나 배열을 전달하기도 하고, 처리한 결과값을 반환받을 때 사용한다.
    • 실습 - 하나의 변수를 인자로 한 함수 사용하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;
 
void chonge(int* p); //함수 원형
 
int main() {
    int x = 1004;
 
    chonge(&x);
    cout << "chonge() 함수 호출 후 변수 x의 값 : " << x << endl;
 
    return 0;
}
 
void chonge(int* p) {
    cout << "chonge() 함수 : " << *<< endl//main() 함수의 x값 출력
    *= 375// x값을 변경
}
cs
    • 결과

  • 함수 포인터
    • 프로그램을 작성하여 컴파일하고 링크하여 실행 파일을 만들고 실행하면 프로그램의 기계어로 번역된 모든 부분이 메모리 영역에 저장되고 실행된다.
    • 함수명은 함수 포인터가 되는 것으로, 함수를 호출할 때 함수명을 사용하는 것이다. 함수의 시작 주소를 갖는 포인터를 사용하면 해당 함수를 호출할 수 있다.
    • 예제 : 함수 포인터 사용
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
#include <iostream>
using namespace std;
 
int add(int x, int y);
int mult(int x, int y);
void fn(int(*fp)(int x, int y));
 
int main() {
    fn(add);
    fn(mult);
 
    return 0;
}
 
void fn(int(*fp)(int x, int y)) {
    int a = 3, b = 7, dap;
 
    dap = fp(a, b);
    
    cout << "결과 = " << dap << endl;
 
}
 
int add(int x, int y) {
    return x + y;
}
 
int mult(int x, int y) {
    return x * y;
}
cs
    • 결과

 

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

inline 함수  (0) 2019.03.01
공용체  (0) 2019.03.01
void * 형 포인터는 캐스트 변환 (타입 *)으로  (0) 2019.02.28
문자열 함수  (0) 2019.02.28
continue문 - 흐름 이동  (0) 2019.02.27

도서 : 신경망 첫걸음(한빛미디어)

지음 : 타리크라시드(송교석 옮김)


 

  • 이전 챕터에서 우리는 y = ax + c 라는 한 개의 선으로 분류할 수 있다는 것을 알게 되었습니다. 그러나 그 중에는 한 개의 분류자로 분류할 수 없는 것들도 있습니다.
    1. 우선 AND와 OR의 연산을 살펴보았을 때, AND는 입력 값이 (1,1) 일 때만 1의 결과가 나오고 나머지 (0,0), (0,1), (1,0) 의 입력은 모두 0이라는 결과가 나옵니다. 이를 그림으로 표현해보면 다음과 같은 그림이 나옵니다
    2. 그리고 OR는 입력 값이 (0,0) 일 때만 0의 결과가 나오고 나머지는 1이라는 결과가 나옵니다. 마찬가지로 그림으로 표현해본다면 다음과 같습니다.
    3. 그러나 XOR라는 연산자가 있습니다. 이 연산자는 (1,0), (0,1)과 같이 서로 다른 BOOL값이 입력되었을 경우에만 1이라는 결과가 나옵니다. 이는 하나의 선으로 표현할 수 없게됩니다.
    4. 따라서 이러한 XOR의 경우는 다음과 같은 두 개의 선으로 표현할 수 있습니다.

이런식으로 여러 개의 선형 분류자를 이용해 데이터를 분류하면 더욱 깔끔하고 자세한 판단을 하도록 만들 수 있게 되는 것입니다.


  • 대자연의 컴퓨터, 뉴런

    • 뉴런이란 신경세포로 전기신호를 전송하여 우리가 어떤 것을 인지할 수 있도록 해주는 세포입니다. 인간의 뇌는 약 1천억 개의 뉴런을 가지고 있습니다. -> 초파리는 10만 개 정도의 뉴런으로도 음식을 찾아 섭취하고, 위험을 피하는 등 상당히 복잡한 업무를 수행할 수 있습니다. 오늘날의 컴퓨터가 충분히 복제할 수 있는 수준입니다.

    • 결론적으로 우리는 앞서 해봤던 대로 뉴런을 선형함수로 표현할 수 있을까요? 정답은 아니오 입니다. 생물학적 뉴런은 단순한 선형함수가 아니라는 것입니다.

    • 과학자들의 관찰에 의하면 뉴런은 입력을 받았을 때 즉시 반응하는 것이 아니라 입력이 누적되어 어떤 수준으로 커진 경우에만 출력을 하게 됩니다. 이것을 분계점(threshold)이라고 합니다.

    • 입력 신호를 받아 특정 분계점을 넘어서는 경우에 출력 신호를 생성해주는 함수를 활성화 함수(activation function)라고 합니다. -> ex)계단 함수(step function) - 입력 값이 작은 경우 출력 값은 0이다가 입력 값이 분계점 이상이 되면 출력 값이 갑자기 올라감

    • fire(작동한다) : 입력 값이 분계점에 이르러 출력을 발생시키는 현상

    • 이번에는 시그모이드 함수(sigmoid function)입니다. 시그모이드 함수는 부드러운 형태를 가집니다.

    • 앞으로 인공 신경망을 만들 때 이처럼 부드러운 S자 형태를 가지는 시그모이드 함수를 활용할 것입니다. 로지스틱 함수(rogistic function)라고 부르기도 합니다. 수식은 다음과 같습니다.

    • 뉴런이 여러 개의 입력을 받아 처리하는 방식 - 만약 a, b, c의 합인 x가 분계점을 넘어설 정도로 충분히 크지 않다면 시그모이드 함수는 아무것도 출력하지 않게 됩니다.

    • 생물학적인 뉴런을 인공적으로 모델화한다면 뉴런을 여러 계층(layer)에 걸쳐 위치시키고, 각각의 뉴런은 직전 계층과 직후 계층에 있는 모든 뉴런들과 상호 연결되어 있는 식으로 표현하면 될 것입니다.

    • 이 그림에서는 3개의 계층이 있으며, 각각의 계층에는 뉴런이 3개씩 존재함을 확인할 수 있습니다. 이 각각의 인공 뉴런을 노드(node)라고 합니다.

    • 가중치(weight) : 낮은 가중치는 신호를 약화하며 높은 가중치는 신호를 강화합니다.

    • FC(fully - connected) : 모든 노드가 연결되는 것 -> 사실 꼭 모든 노드가 연결되어야 한다는 법은 없습니다. 일반적으로 모든 노드 간에 연결을 하는 이유는, 이렇게 연결해야 프로그램으로 구현하기가 편리하며, 문제를 해결하기 위해 필요로 하는 절대적인 최솟값보다 연결이 몇 개 더 있다고 해서 별문제가 되는 것도 아니기 때문입니다.

    • 일부 가중치들은 0에 가까운 값 또는 심지어 0이 될 수도 있습니다. 가중치가 0이 된다면 신호에 0을 곱한다는 말이므로, 신호가 0이되는 해당 연결은 사실상 끊어진 것이나 다름없게 됩니다.

문제 출처(BAEKJOON ONLINE JUDGE) : https://www.acmicpc.net/problem/4673

  • 문제 : 셀프 넘버는 1949년 인도 수학자 D.R. Kaprekar가 이름 붙였다. 양의 정수 n에 대해서 d(n)을 n과 n의 각 자리수를 더하는 함수라고 정의하자. 예를 들어, d(75) = 75+7+5 = 87이다.

    양의 정수 n이 주어졌을 때, 이 수를 시작해서 n, d(n), d(d(n)), d(d(d(n))), ...과 같은 무한 수열을 만들 수 있다. 

    예를 들어, 33으로 시작한다면 다음 수는 33 + 3 + 3 = 39이고, 그 다음 수는 39 + 3 + 9 = 51, 다음 수는 51 + 5 + 1 = 57이다. 이런식으로 다음과 같은 수열을 만들 수 있다.

    33, 39, 51, 57, 69, 84, 96, 111, 114, 120, 123, 129, 141, ...

    n을 d(n)의 생성자라고 한다. 위의 수열에서 33은 39의 생성자이고, 39는 51의 생성자, 51은 57의 생성자이다. 생성자가 한 개보다 많은 경우도 있다. 예를 들어, 101은 생성자가 2개(91과 100) 있다. 

    생성자가 없는 숫자를 셀프 넘버라고 한다. 100보다 작은 셀프 넘버는 총 13개가 있다. 1, 3, 5, 7, 9, 20, 31, 42, 53, 64, 75, 86, 97

    10000보다 작거나 같은 셀프 넘버를 한 줄에 하나씩 출력하는 프로그램을 작성하시오.

  • 입력 : 없음

  • 출력 : 10,000보다 작거나 같은 셀프 넘버를 한 줄에 하나씩 증가하는 순서로 출력한다.

  • 내가 작성한 답안

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<stdio.h>
 
int d(int N) {
    for (int i = 1; i < 10001; i++) {
        int result = i + i % 10 + (i % 100/ 10 + (i % 1000/ 100 + (i % 10000/ 1000;
        if (result == N)
        {
            return 1;
        }
    }
    return 0;
}
 
int main() {
    int selfnumberF = 1;
    for (int j = 1; j < 10001; j++) {
        selfnumberF = d(j);
        if (selfnumberF == 0) {
            printf("%d\n",j);
        }
    }
    return 0;
}
cs
  • 결과

 

 

  • 어려워 보였지만 for문을 사용하니 생각보다 쉬운 문제였다.
  • 그러나 코딩 고수들의 답은 시간도 매우 짧게 걸리고 코드의 길이 또한 매우 짧았다.

+ Recent posts