반응형

오랜 시간 미뤄온 c, c++ 언어 학습 내용을 티스토리에 차근차근 업로드하려고 한다.

 

먼저 구조체 관점에서의 얕은 복사, 깊은 복사가 어떤 차이점이 있을까?

 

  1. 얕은 복사
#include <iostream>
#include <cstring>

using namespace std;

typedef struct Student
{
	float height;
	char* name;
} Student;

int main() {
	
	Student alice, bob;
	char alice_s[] = "alice";
	char bob_s[] = "bob";
	
	alice.name = alice_s;
	alice.height = 160;
	bob.name = bob_s;
	bob.height = 170;

	bob = alice;

	printf("alice name: %s, height: %lf\n", alice.name, alice.height);
	printf("bob name: %s, height: %lf\n", bob.name, bob.height);
	return 0;
}

 

학생들의 키 정보를 가진 구조체를 생성하고 alice, bob 이라는 학생 구조체를 선언했다. 그리고 이름과 키 정보를 저장하고 있는 alice의 정보를 bob 구조체에 복사하려고 한다.

 

= 을 이용하여 bob 구조체 주소에 alice의 구조체 주소를 복사하니 bob 구조체에는 alice의 정보들이 담겼다. 

alice name: alice, height: 160.000000
bob name: alice, height: 160.000000

 

하지만 얕은 복사 시 주의해야 할 점이 있다.

만약 alice의 이름을 chris 수정했다고 해보자.

#include <iostream>
#include <cstring>

using namespace std;

typedef struct Student
{
	float height;
	char* name;
} Student;

int main() {
	
	Student alice, bob;
	char alice_s[] = "alice";
	char bob_s[] = "bob";
	
	alice.name = alice_s;
	alice.height = 160;
	bob.name = bob_s;
	bob.height = 170;

	bob = alice;
	/*printf("address: %p, %p\n", bob, alice);*/
	printf("name address: %p, %p\n", bob.name, alice.name);
	printf("height address: %p, %p\n", bob.height, alice.height);

	printf("alice name: %s, height: %lf\n", alice.name, alice.height);
	printf("bob name: %s, height: %lf\n", bob.name, bob.height);

	strcpy(alice.name, "chris");

	printf("<after mod> alice name: %s, height: %lf\n", alice.name, alice.height);
	printf("<after mod> bob name: %s, height: %lf\n", bob.name, bob.height);
	return 0;
}
name address: 0x7ffdbbdd1992, 0x7ffdbbdd1992
height address: 0x622e4bab02b0, (nil)
alice name: alice, height: 160.000000
bob name: alice, height: 160.000000
<after mod> alice name: chris, height: 160.000000
<after mod> bob name: chris, height: 160.000000

 

구조체의 얕은 복사 결과 alice와 bob의 이름 변수의 주소가 동일해졌고 alice의 이름을 chris로 바꾸니 bob의 이름도 chris가 되어버렸다. 구조체 내의 포인터 변수인 이름 멤버 변수가 값을 복사하지 않고 alice의 이름 주소값을 복사해옴으로써 발생한 문제점이다. 

 

그렇다면, 위의 문제를 해결하려면 어떻게 해야 할까?

 

반응형

 

  2. 깊은 복사

#include <iostream>
#include <cstring>

using namespace std;

typedef struct Student
{
	float height;
	char* name;
} Student;

int main() {
	
	Student alice, bob;

	alice.name = (char*)malloc(sizeof(char) * 30);
	bob.name = (char*)malloc(sizeof(char) * 30);

	strcpy(alice.name, "alice");
	alice.height = 160;
	strcpy(bob.name, "bob");
	bob.height = 170;

	bob.height = alice.height;
	strcpy(bob.name, alice.name);

	printf("address: %p, %p\n", bob, alice);
	printf("name address: %p, %p\n", bob.name, alice.name);
	printf("height address: %p, %p\n", bob.height, alice.height);
	
	printf("alice name: %s, height: %lf\n", alice.name, alice.height);
	printf("bob name: %s, height: %lf\n", bob.name, bob.height);

	strcpy(alice.name, "chris");

	printf("<after mod> alice name: %s, height: %lf\n", alice.name, alice.height);
	printf("<after mod> bob name: %s, height: %lf\n", bob.name, bob.height);
	free(alice.name);
	free(bob.name);
	return 0;
}
address: 0x65395ed082e0, 0x65395ed082b0
name address: 0x65395ed082e0, 0x65395ed082b0
height address: 0x65395ed08310, (nil)
alice name: alice, height: 160.000000
bob name: alice, height: 160.000000
<after mod> alice name: chris, height: 160.000000
<after mod> bob name: alice, height: 160.000000

 

Student 구조체의 멤버 변수로 포인터 변수를 그대로 사용하려면 메모리 동적 할당해주고 하나하나 복사하면 된다.

또 다른 방법으로는 이름 멤버 변수를 배열로 바꾸고 하나하나 복사하면 된다. 

 

이런 개념은 확실하게 알아둬야 나중에 고생하지 않는다...!

반응형
반응형

github로 프로젝트를 관리하기 위해서는 git bash를 이용하여 일련의 명령어들을 입력해주어야 한다.

계속 찾아보기엔 내 시간이 아깝기 때문에 저장해놓고 용이하게 찾아보려고 한다.

 

1. 깃 설정을 바꾸기 위해 다음과 같이 입력한다.

git config --global init.defaultBranch main

위 명령어를 입력하면 기본 브랜치를 master가 아닌 main으로 바뀐다.

 

(과거에는 master를 기본 브랜치로 사용했다고 한다.)

 

2. 이제 현재 위치한 프로젝트 폴더에서 깃 저장소를 만들고 초기화한다.

git init

위 명령어를 입력하면 현재 위치에서 .git 폴더가 생성된다. 이 폴더는 깃이 사용하는 모든 정보(버전 기록 )를 담고 있다. 현재 위치는 깃이 관리하는 프로젝트 디렉토리가 된다. 

 

3. github 계정 정보를 입력한다. 

git config --global user.email "<email_address>"
git config --global user.name "<user_name>"

 

4. 커밋할 파일들을 staging area에 넣는다.

git add <file_location>

 

그리고 아래 명령어를 입력하면 파일이 잘 담겼는지 확인할 수 있다.

git status

 

5. staging area에 있는 파일들로 새로운 버전을 만든다.

git commit -m "<description>"

 

파일의 내용을 만들고 두번째 버전을 만드려면

git add *
git commit -m "<description>"

아래 명령을 입력하면 커밋된 정보를 확인할 수 있다.

git log

위 명령어를 입력하 여러 커밋과 사용자의 이름, 정보가 나온다.

 

파일을 커밋하면 어느 버전이든지 고유의 커밋 식별자를 이용하여 자유롭게 되돌아갈 수 있다. 

git checkout <식별자>

커밋의 식별자는 git log 명령어를 통해 확인할 수 있으며 식별자 앞 7자만 입력하여도 된다.

 

6. 이제 깃의 원격 저장소와 로컬 저장소를 연결한다.

git remote add <원격 저장소를 부르는 별명> <원격 저장소의 주소>

아래 명령어를 입력하면 현재 연결된 원격 저장소를 확인할 수 있다.

git remote -v

아래 명령어는 연락을 끊는다.

git remote remove <원격 저장소의 별명>

 

7. 이제 커밋 파일을 원격 저장소에 올린다.

git push <원격 저장소의 별명> main

 

만약 main이 아닌 다른 브랜치에 올리고 싶다면,

git fetch <원격 저장소 별명>

위 명령어는 원격 저장소(origin)의 최신 브랜치 상태를 로컬에 갱신한다.

git branch --set-upstream-to=origin/<브랜치명> <브랜치명>

위 명령어를 입력하여 기존 <브랜치명>을 <브랜치 별명/브랜치명>에 연결하여 추적 관계를 설정한다.

git branch -vv

이후, 다음 명령어 입력 결과가 <브랜치명> 옆에 <브랜치 별명/브랜치명>이 출력된다면 성공적으로 연결됐다는 의미이다.

 

8. 이제 github에 커밋 파일을 확인할 수 있다.

 

만약 팀원이 github의 파일을 수정했다면 아래 명령어를 통해 파일을 받아올 수 있다.

git clone <원격 저장소 주소>
cd <저장소_이름>
git checkout <커밋해시>

위 명령어를 수행하면 다음과 같은 폴더/ 파일이 생성된다.

 

(현재 디렉토리)
└── 입력한 브랜치 이름/
    ├── .git/
    ├── README.md
    └── other_project_files...


 

8-1. git add로 staging area에 다른 파일을 실수로 올릴때 다음과 같은 명령어를 입력한다.

git restore --staged [제외할 파일 이름]

그럼 add하여 staging area에 올린 수정 작업을 작업 디렉토리에 되돌려 staging area의 파일을 제거한다. 작업 디렉토리의 수정 작업은 그대로 유지된다.

참고로 이 명령어를 사용할 때는 커밋이 하나라도 있어야 한다.
만약 커밋이 하나도 없다면 에러가 발생한다.

다음과 같은 명령어를 입력하면 수정 작업까지 제거되고 이전 상태를 복원한다. 

git restore <제외할 파일 이름>

 

8-2. 다음과 같은 명령어는 커밋한 파일을 취소한다.

git reset --soft HEAD^

 

위 명령어는 가장 최근의 커밋을 취소하며 staging area에는 수정 작업이 그대로 남는다. 

 

커밋과 staging area까지도 취소하고 싶다면 다음과 같은 명령어를 입력한다. 

git reset --mixed HEAD^

위 명령어는 커밋과 staging도 취소한다. 

 

커밋과 staging, 작업 디렉토리의 수정 작업까지도 취소하고 싶다면 다음과 같은 명령어를 입력한다. 

git reset --hard HEAD^

 

특정 커밋 내용을 취소하고 싶다면 다음과 같은 명령어를 입력한다. 

git revert <커밋 해시>

특정 커밋을 취소하기 위해서 새 커밋을 생성한다. 히스토리를 유지하면서 잘못된 커밋만 뮤효화할 때 사용한다. 

반응형
반응형

 

각종 프로젝트를 맡게 되면서 더이상 이동식 디스크, 로컬 파일로 버전 관리가 벅차서 깃허브를 만들었다.

초창기 SW 개발 공부를 시작했을 때부터 지금까지의 자료를 정리하고 업로드 하는 일이 여간 복잡한게 아니었다!

(모든 것은 과거의 내가 자초한 것.. 이제부턴 미루지 말아야지...!!)

 


Git Hub 연결


git push <remote_name> <branch_name>

<remote_name> : myProject_git

<branch_name> : main

으로 하여 push 명령어를 입력했다.

 

git push myProject_git main
error: src refspec main does not match any
error: failed to push some refs to 'https://github.com/<user_name>/<repository_name>.git'

 

첫번째 에러가 떴다.

 

무슨 말인지 모르겠으니 검색했다.

기본 repository 이름을 master로 바꿨더니 성공했다는 글이 보였다.

 

시도...!

성공은 했지만 원인은 뭔지 모르는 찝찝한 상황...

 

내 기본 remote의 repository 이름은 main인데 master로의 push만 성공한다니

 

로컬 저장소 브랜치명을 확인하기 위해 

# git show-ref 명령어를 입력했다.

아.. master로 되어있다.

 

로컬 저장소 브랜치명을 master에서 main으로 바꿔보았다.

 git branch -m master main

또는 

git config --global init.defaultBranch main

 

 

main으로 잘 바뀌었다.

이제 다시 main에 push를 시도해보았다.

음.. 다른 에러가 발생했다!

 

에러가 fetch하라고 했으므로 fetch도 해보고 pull도 해봤지만 에러는 여전히 발생했다..

갈피를 못잡고 있다가.. 근본적인 원인을 발견했다.

 

처음 repository를 생성할 때, README.txt 파일을 자동으로 생성되게끔 만들었댔다!

그래서 로컬 repository와 remote repository가 일치하지 않아 발생된 에러였다.

이는 기존 데이터 손실을 막기 위해 push에서 에러가 발생한 것이었다,

즉, 애초에 동기화만 잘 해줬어도 끝났을 문제!

 

동기화

#git pull --rebase <repository_name><branch_name>

 

다시 push 시도!

 

음.. 이제 잘된다!

 

결론: 동기화를 잘 하자!

반응형
반응형

 

이제 스플라인 보간법을 이용하여 점을 잇는 간단한 프로그램을 만들어 보려고 한다.

Qt 프레임워크를 이용, CMake 빌드도구로 만들어보았다.

 

동작:

마우스의 왼쪽 버튼을 누르면 점이 찍히고, 화면에 3개 이상의 점이 찍히면 점을 찍은 순서대로 잇는다.

마우스의 오른쪽 버튼을 누르면 화면에 그려진 그림을 지운다.

 

방법:

QWidget의 멤버 함수인 마우스 이벤트 함수(mousePressEvent)를 오버라이드

QTimer의 멤버 함수인 타이머 함수(timerEvent) 오버라이드

QGraphicsScene 변수를 정의하고 addPixmap 함수 input에 QPixmap 그림을 추가하는 방법으로 마우스가 눌릴 때마다 그려지는 그림 변경

 


.h

멤버 변수는 다음과 같이 정의했다.

bool b_clicked;				// 왼쪽 마우스 클릭 플래그
int index;					// 화면에 찍힌 점의 개수

QPixmap * Pixmap;			// 갱신할 pixmap 이미지
QPoint * Point;				// 점의 위치 저장

QGraphicsView * m_view;		
QGraphicsScene * m_scene;

 

.cpp

생성자에 다음과 같이 m_view, m_scene, 점, 이미지 배열을 초기화 하였다.

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{

    index = 0;
    b_clicked = false;
    
    // m_view에 사용할 장면 설정
    this->setCentralWidget(m_view = new QGraphicsView());
    m_scene = new QGraphicsScene;
    m_view->setScene(m_scene);
    m_view->setGeometry(10,30,TILE_WIDTH,TILE_HEIGHT);

    
    // 점, 이미지를 저장할 배열 초기화
    Pixmap = new QPixmap[MAXLENGTH];
    Point = new QPoint[MAXLENGTH];
    QPixmap pixmapWhite(TILE_WIDTH, TILE_HEIGHT);		
    pixmapWhite.fill(Qt::white);					// 띄울 첫 이미지는 하얀 바탕으로 함
    Pixmap[0] = pixmapWhite;

    this->startTimer(0);							// 타이머 시작
}

 

 

마우스가 눌릴 때의 이벤트를 처리하는 함수는 왼쪽과 오른쪽 마우스 클릭을 구분할 수 있다.

왼쪽 마우스가 눌렸을 경우 왼쪽 마우스 클릭 플래그 b_clicked를 true로 하고 점의 개수를 카운트하는 index에 1 증가시킨다. mousePressEvent의 매개 변수 event를 통해 왼쪽 마우스가 눌린 위치를 가져와 점 배열에 저장한다.

void MainWindow::mousePressEvent(QMouseEvent * event)
{
	// 왼쪽 마우스 클릭
    if (event->button() == Qt::LeftButton) 
    {
        b_clicked = true;
        Point[index] = event->pos();
        index++;  

        qDebug() << "window value at (" << point.x() << ", " << point.y() << "): " << point;
    }
    // 오른쪽 마우스 클릭
    else if(event->button() == Qt::RightButton)
    {
        index = 0;			// 점 개수 0으로 초기화

        QPixmap p(TILE_WIDTH, TILE_HEIGHT);
        p.fill(Qt::white);
        Pixmap[0] = p;
        m_scene->addPixmap(Pixmap[0]);
    }
    QWidget::mousePressEvent(event);
}

 

이제 왼쪽 마우스가 클릭 됐음을 인식하여 점과 곡선을 그려주기 위해 timerEvent 함수를 처리할 것이다. (그려지는 동작을 mousePressEvent 함수가 아니라 timerEvent 함수에 처리하는 이유는 추후 다른 옵션을 추가하더라도 timerEvent 함수에서만 수행할 수 있도록 하기 위해서이다. 구현 방법은 다양하므로 각자 상황에 맞는 코드를 짜면 되겠다.) 

void MainWindow::timerEvent(QTimerEvent *)
{
    if(b_clicked)
    {
        QPixmap linePix;
        QImage img = Pixmap[index - 1].toImage();

        // 점 그리기
        linePix = generateDot(img, point, 4);
        // append it to the buffer
        Pixmap[index] = linePix;

        if(index >= 3)
        {
        	// 곡선 그리기
            linePix = generateCurve(img);
        }

        m_scene->addPixmap(linePix);
        b_clicked = false;
    }
}

generateCurve 함수는 최소 점이 3개 이상 찍혔을 때 Spline interpolation에 의해 생성된 스플라인 함수를 그리는 역할이다. 점과 곡선을 그린 후, m_scene에 그린 pixmap을 띄우고 왼쪽 마우스 클릭 플래그를 off 한다.

 

자, 이제  Point에 저장된 점 사이에 생성되는 스플라인 함수를 구해보자. 나의 경우는 재귀함수로 구현했지만, 임베디드 시스템처럼 자원이 제한적인 시스템에서는 재귀함수로 구현하기 보단 배열을 이용하는 방법이 적합할 것이다. 스플라인 함수의 계수를 저장하는 배열을 멤버 변수에 저장하고 계수 저장 배열을 계속해서 검색, 갱신해가며 찾는 방법이 있다.

double * MainWindow::getSplineInterpolation(int n)
{
    if(n == 2)
    {
    	// a_1, b_1, c_1 계산
        double * para = new double[3];
        double dividend = ((double)Point[n - 2].y() - (double)Point[n - 1].y());
        double divisor = (pow((double)Point[n - 2].x(), 2) - pow((double)Point[n - 1].x(), 2) - 2 * Point[n - 1].x() * ((double)Point[n - 2].x() - (double)Point[n - 1].x()));
        if(divisor == 0)
        	divisor = 0.0000001;
        para[0] = (dividend / divisor);

        para[1] = para[0] * (-2) * Point[n - 1].x();
        para[2] = Point[n - 2].y() - para[0] * pow(Point[n - 2].x(), 2) - para[1] * Point[n - 2].x();
        return para;
    }
    else
    {
    	// a_n-1, b_n-1, c_n-1 계산
        double * prev_para = getSplineInterpolation(n - 1);
        double * para = new double[3];

        double dividend = (((double)Point[n - 2].y() - (double)Point[n - 1].y())
        	+ (-2 * prev_para[0] * Point[n - 2].x() - prev_para[1]) * (Point[n - 2].x() - Point[n - 1].x()));
        double divisor = (pow(Point[n - 2].x(), 2) - pow(Point[n - 1].x(), 2) -2 * Point[n - 2].x() * (Point[n - 2].x() - Point[n - 1].x()));
        if(divisor == 0)
        	divisor = 0.0000001;
        para[0] = (dividend / divisor);
        para[1] = 2 * prev_para[0] * Point[n - 2].x() + prev_para[1] -2 * para[0] * Point[n - 2].x();
        para[2] = Point[n - 2].y() - para[0] * pow(Point[n-2].x(), 2) - para[1] * Point[n-2].x();
        return para;
    }
}

앞선 글에서 구한 a_1, b_1, c_1, a_p, b_p, c_p 값이 위와 같음을 이용하였다. (궁금하신 분은 이전의 글을 보면 도움이 될 것이다.) 

getSplineInterpolation 함수 내에서 자기 자신을 다시 호출하고, 현재의 점(n)구간보다 1번 더 이른 시점의 점(n-1)구간의 계수를 prev_para에 저장하여 반환한다. prev_para[0], prev_para [1] 값을 이용하여 계수 a_n, b_n을 계산한다.

 


결과

알고리즘을 검증하기 위해 이미 알려져 있는 점에서 알고리즘을 통해 계산한 다항식의 계를 도출해보았다. 

x y
1 5
2 12
3 23

x구간 [1, 2]에서의 다항식 계

x구간 [2, 3]에서의 다항식 계

 

이미 알려진 점에서의 다항식 계와 일치하였다.


고찰

이렇게 하면, getSplineInterpolation 의 계수를 이용하여 1~n까지의 계수를 구할 수 있을 것이다. 사실, 위와 같은 재귀함수 방법으로 구현하는 것은 상당히 비효율적인 방법이라고 할 수 있다. 1번째 스플라인 함수를 구하기 위해 getSplineInterpolation 함수는 1번 호출되고, 2번째 스플라인 함수를 구하기 위해 getSplineInterpolation 함수는 2번 호출되니 말이다. 그럼 n번째 스플라인 함수는 n번 호출된다. 즉, 동일 작업을 반복하고 있는 꼴이다. 이 반복을 피하기 위해 param[3][n] 변수를 정의하고 1번째 스플라인 함수 계수를 0번째 저장, 2번째 스플라인 함수는 param의 0번째 값을 이용하여 1번째에 저장하면 n번째까지 저장할 수 있다.

 

반응형

'프로젝트 > 보간법' 카테고리의 다른 글

Spline interpolation (스플라인 보간법) 개념  (0) 2025.03.17
반응형

공학, 과학에서는 다양한 데이터 종류들이 있다. 이와 같은 데이터들은 보통 제한된 수의 데이터들이므로 공학자, 과학자들은 주어진 데이터 사이의 (공백으로 남겨진..) 값을 궁금해한다.

따라서 데이터의 원래 값을 어떻게 하면 최대한 보간할 수 있는지에 대한 방법들에 관심이 많을 수밖에 없다.

자, 그렇다면 아래와 같은 점들을 연속적으로 이어지도록 하려면 어떻게 해야 할까? 

 

방법은 다양하지만 아래 2가지 방법으로 점을 이어보았다.

 

 

각 점을 직선으로만 근사한 이미지를 보면.... 근사한 값의 오차가 클 것임을 알 수 있다. 이는 주어지는 점(정보)의 개수가 적으면 적을수록 이러한 오차가 더욱 크게 나타날 것이다. 

하지만 곡선으로 근사했을 경우 이러한 오차를 줄일 수 있다.

 

곡선 형태의 근사방법에는 여러 방법들이 존재한다. 

예를 들어 다항식 보간법은 N개의 점에 대해 N-1차 다항식으로 보간하는 방법이다. 당연히 1차 다항식으로 보간하는 방법보다 더 높은 차수로 보간하면 오차율이 줄어든다. 더 많은 차수의 항을 포함하므로 원 데이터를 더욱 세밀하게 추적할 수 있기 때문이다. 하지만 너무 큰 차수의 다항식으로 보간하게 되면 오버슈트(overshoot), Runge's phenomenon 현상으로 오히려 오차율이 증가할 수 있다.

(이에 대한 자세한 설명은 신호 처리에 대한 내용을 올릴 때 자세히 적도록 하겠습니다.)

 

Spline interpolation (스플라인 보간법)

스플라인 보간법은 각 점 사이의 구간마다 서로 다른 스플라인 함수를 구성하여 보간하는 방법이다. N개의 점의 N-1개의 구간에서 각 구간마다 스플라인 함수를 가진다.

 

스플라인 보간법은 사용되는 스플라인 함수의 차수에 따라 1차 스프라인 보간법, 2차 스프라인 보간법, 3차 스프라인 보간법이 존재한다. 이 중 2차 스플라인 함수를 구현해보고 간단한 곡선 그리기 프로그램에 Qt로  적용해보려고 한다.

 

2차 스플라인 보간법에서 각 스플라인 함수들은 아래와 같은 함수 형태일 것이다.

N개의 점에서 N-1개의 함수를 가질것이고 함수 당 3개의 미지수가 존재하기 때문에 3×(N-1)개의 미지수가 존재, 3×(N-1)개의 연립방정식이 필요하다.

 

[2차 스플라인 보간법의 규칙]

  1. 각 점에서 이웃한 다항식의 함수값은 같아야 한다.
  2. 함수는 첫 점과 끝 점을 지나야 한다.
  3. 각 점에서 이웃한 1차 도함수는 연속이어야 한다. (이웃한 다항식의 1차 도함수는 같아야 한다.)
  4. 첫 구간에서의 도함수는 0이라고 가정한다. 

위의 규칙들을 이용하면 3×(N-1)개의 미지수의 해를 풀 수 있다.

규칙 1번에 의해 2×(N-2)개의 식을 만들 수 있다. (양 끝점을 제외하면 N-2개의 점에서 이웃하는 다항식이 2개 존재하므로)

규칙 2번에 의해 2개의 식을 만들 수 있다. (각 구간의 함수는 첫 점과 끝점 2개의 점을 지나므로)

규칙 3번에 의해 N-2개의 식을 만들 수 있다. (양 끝점을 제외한 도함수는 교점에서 함수값이 동일해야 하므로 N-2개)

규칙 4번에 의해 1개의 식을 만들 수 있다.

따라서 총 3×(N-1)개의 미지수의 해를 풀 수 있다.

 


위 조건들을 이용하여 n개의 점이 존재할 경우 f(n-1)(x)함수에 대한 계수 a(n-1) , b(n-1) , c(n-1)을 구해보았다.

총 n개의 점에 대해 n-1개의 스플라인 함수가 존재하므로 위와 같이 표현하였다.

 

규칙 4번에 의해 x1, y1 점에서의 도함수가 0이라고 가정하였다.

 

규칙 1, 2번에 의해

이므로 b1에 -2a1x2를 대입하면,

이렇게 a1, b1, c1에 대한 첫번째 스플라인 함수의 계수를 구하였다.

 

이제 2~n-1의 스플라인 함수를 구할 수 있다.

p가 2보다 크거나 작고 n-1보다 작거나 같을 때, p-1 스플라인 함수의 도함수와 p 스플라인 함수의 도함수는 다음과 같이 표현할 수 있다.

규칙 3번에 의해 

다음과 같은 다항식이 성립한다.

규칙 1번에 의해 

b_p에 -2a_p x_p+2a_((p-1)) x_p+b_((p-1))를 대입하면,

이제, a_p에 대해 정리하면,

이렇게 첫번째 스플라인 함수의 계수 a1, b1, c1을 먼저 구한 후 이를 이용하여 a_p, b_p, c_p를 구할 수 있다.

(2≤p≤n-1)

 

계수를 구했으니 다음 글은 Qt를 이용하여 spline interpolation에 의해 점을 잇는 프로그램을 만드는 과정을 올릴 것이다.

반응형

'프로젝트 > 보간법' 카테고리의 다른 글

Spline interpolation (스플라인 보간법) 적용  (0) 2025.03.19
반응형

2. 오일러의 공식 증명

 

: 미분값이 자기 자신인 함수

 

(자연 상수 e는 무리수이며 순환하지 않는 무한 소수이다)


함수 f(x)의 미분은 다음과 같다.

x변화율, ⊿x를 양변에 곱하면,

f(x)를 양변에 더하면,

이때, 함수 f(x)의 미분값은 자기 자신이므로, 

위와 같이 정리할 수 있다. 

x에 값을 대입하여 위 공식을 분석해보자.

 

 

n⊿x값을 치환해보자.

n⊿x에 x를 대입하면, ⊿x는 0에 가까이 가야하므로 n은 무한대에 가깝게 간다.

대입하여 정리해보자.

f(x)는 무한 항을 가진 무한 차수가 된다.

위 공식을 그래프로 그리면, x가 0일때, 1을 지나며 점근선은 y=0이다.

 

반응형

 

그렇다면? x에 허수 i를 곱한 e^ix는 어떨까?


f(x)의 미분은 ...

⊿x를 양변에 곱하면,

f(x)를 양변에 더하면,

이때, 함수 f(x)의 미분값은 허수 i를 곱한 자기 자신이므로, 

 

자, 이제 다시 x에 값을 대입하여 위 공식을 분석해보자.

 

n⊿x값을 치환하자.

e^x 함수에서 x에 허수를 곱했더니, 실수부, 허수부로 이뤄진 무한 차수가 도출되었다.

이때, x가 1일 때의 함수값을 a+bi라고 하면,

이를 좌표계에 표현하기 위해 허수축을 추가하여 그려보자.

그럼 x=1일때, 실수부는 a, 허수부는 b이므로 다음과 같이 그려진다.

 

 

미분 공식에서 유도한 다음과 같은 다항식을 그래프에 표현하여 어떻게 그려지는지 확인해보자.

아래와 같이 x축을 기준으로 바라보는 그래프로 보자.

 

복소평면과 같은 좌표계가 보이고, 이 좌표계에 f(x) 점을 찍었다.

i⊿xf(x)는 f(x)에 0에 가까이 가는 ⊿x를 곱하여 amplitude가 0에 수렴할 것이고, 허수 i를 곱하여 f(x)와 시계반대방향으로 90º의 각을 이루며 위치할 것이다. 따라서 f(x)와 i⊿xf(x)를 더한 f(x+⊿x)는 다음과 같이 빨간 선으로 그려진다.

그래프에 따르면, f(x)와 i⊿xf(x)는 항상 수직을 이룰것이다. 이 정의로 미루어보아, f(x)는 원의 특성과 일치.

f(x)는 허수축을 추가한 좌표평면을 x축 기준으로 봤을 때 그래프는 원의 형태로 그려진다라는 것을 알 수 있다.

 

원은 원점에서 원 위의 어느 한 점에 발을 내린 선분과 발을 내린 점의 접선과 항상 수직인 특성을 가지고 있다. 

 

 

 


e의 ix승은 x값이 증가함에 따라 실수부, 허수부 사이에서 나선형을 그리는 주기 함수로 그릴 수 있다.

 

여기서 정말 신기한 부분!

이 함수 e의 ix승의 그래프를 

(1) y축으로 볼 때 (말하자면) x축, 허수축 사이에서 그려지는 그래프 모양

(2) 허수축으로 볼 때 (말하자면) x축, y축 사이에서 그려지는 그래프 모양

으로 본다면 쾌감을 느낄 수 있을 것이다.

 

(1) y축으로 본 그래프 모양

 

(2) 허수축으로 본 그래프 모양

 

 


즉, e의 ix승의 함수의 그래프는 x가 증가함에 따라 반시계방향으로 돌 때, sin파와 cos파의 융합이다.

 

반응형

+ Recent posts