728x90
반응형
728x90
반응형
728x90
반응형

영구 데이터 구조 Persistent Data Structures

Persistent Data Structures 란 데이터 구조를 변경할 때, 기존 버전을 유지하며 새로운 버전을 생성하는 데이터 구조를 말합니다.

이러한 구조는 데이터를 복사하거나 덮어쓰지 않고, 불변성 을 유지하면서 효율적으로 새로운 상태를 관리할 수 있습니다.


1. Persistent Data Structures의 특징

  1. 불변성 (Immutability)

    • 기존 데이터 구조는 변경되지 않고, 새로운 데이터 구조가 생성됩니다.
    • 데이터 구조의 이전 버전은 항상 유지됩니다.
  2. 공유 및 효율성

    • 변경되지 않는 부분은 기존 데이터 구조를 재사용합니다.
    • 데이터 복사를 최소화하여 메모리 및 성능 효율성을 높입니다.
  3. 버전(Version) 관리

    • 여러 상태를 동시에 관리할 수 있으므로, Undo/Redo 기능 구현이 간단해집니다.
  4. 함수형 프로그래밍

    • 불변성을 선호하는 함수형 프로그래밍에서 자주 사용됩니다.

2. Persistent Data Structures의 예

(1) 완전한 불변 구조 (Fully Persistent)

  • 모든 버전을 읽고 수정할 수 있습니다.

(2) 부분 불변 구조 (Partially Persistent)

  • 이전 버전을 읽을 수 있지만, 수정은 가장 최신 버전에서만 가능합니다.

(3) 완전 삭제 불변 구조 (Ephemeral)

  • 기존 버전은 삭제되며, 최신 버전만 유지됩니다.

3. 예제: Persistent Linked List

Persistent Linked List는 노드 변경 시, 기존 리스트를 유지하며 새로운 노드를 생성합니다.

예제 코드 (C++):

cpp

#include <iostream>
#include <memory>

// 노드 정의
struct Node {
    int value;
    std::shared_ptr<Node> next;

    Node(int value, std::shared_ptr<Node> next = nullptr)
        : value(value), next(next) {}
};

// 리스트의 헤드 출력
void printList(const std::shared_ptr<Node>& head) {
    std::shared_ptr<Node> current = head;
    while (current) {
        std::cout << current->value << " -> ";
        current = current->next;
    }
    std::cout << "nullptr\n";
}

// 새로운 노드를 추가하는 함수
std::shared_ptr<Node> addNode(const std::shared_ptr<Node>& head, int value) {
    return std::make_shared<Node>(value, head);
}

int main() {
    // 초기 리스트 생성
    std::shared_ptr<Node> list1 = std::make_shared<Node>(1);
    list1 = addNode(list1, 2);
    list1 = addNode(list1, 3);

    // 새로운 리스트 생성 (list1의 복사본)
    std::shared_ptr<Node> list2 = addNode(list1, 4);

    // 두 리스트 출력
    std::cout << "List 1: ";
    printList(list1);

    std::cout << "List 2: ";
    printList(list2);

    return 0;
}

실행 결과:

List 1:      3 -> 2 -> 1 -> nullptr
List 2: 4 -> 3 -> 2 -> 1 -> nullptr

설명:

  • list1을 변경하지 않고, list2는 새로운 노드를 추가한 버전입니다.
  • list1list2는 서로 다른 리스트이지만, 변경되지 않은 노드를 공유합니다.

4. 예제: Persistent Binary Tree

Persistent Binary Tree는 노드의 변경이나 추가 시, 필요한 경로의 노드만 복사하여 새로운 버전을 만듭니다.

예제 코드 (C++):

cpp

#include <iostream>
#include <memory>

// 트리 노드 정의
struct TreeNode {
    int value;
    std::shared_ptr<TreeNode> left;
    std::shared_ptr<TreeNode> right;

    TreeNode(int value, std::shared_ptr<TreeNode> left = nullptr, std::shared_ptr<TreeNode> right = nullptr)
        : value(value), left(left), right(right) {}
};

// 새로운 노드를 추가하는 함수
std::shared_ptr<TreeNode> addNode(const std::shared_ptr<TreeNode>& root, int value) {
    if (!root) {
        return std::make_shared<TreeNode>(value);
    }
    if (value < root->value) {
        return std::make_shared<TreeNode>(root->value, addNode(root->left, value), root->right);
    } else {
        return std::make_shared<TreeNode>(root->value, root->left, addNode(root->right, value));
    }
}

// 트리 출력 (중위 순회)
void printTree(const std::shared_ptr<TreeNode>& root) {
    if (!root) return;
    printTree(root->left);
    std::cout << root->value << " ";
    printTree(root->right);
}

int main() {
    // 초기 트리 생성
    std::shared_ptr<TreeNode> tree1 = addNode(nullptr, 10);
    tree1 = addNode(tree1, 5);
    tree1 = addNode(tree1, 15);

    // 새로운 트리 생성 (tree1의 복사본)
    std::shared_ptr<TreeNode> tree2 = addNode(tree1, 12);

    // 두 트리 출력
    std::cout << "Tree 1: ";
    printTree(tree1);

    std::cout << "\nTree 2: ";
    printTree(tree2);

    return 0;
}

실행 결과:

Tree 1: 5 10    15 
Tree 2: 5 10 12 15 

설명:

  • tree1은 변경되지 않고 유지됩니다.
  • tree212를 추가한 새로운 트리 버전입니다.
  • 변경되지 않은 노드는 공유됩니다.

5. Persistent Data Structures의 장단점

장점

  1. 데이터 안정성
    • 데이터의 이전 버전을 항상 보존합니다.
  2. Undo/Redo 기능 구현 용이
    • 이전 상태로의 복원이 간단합니다.
  3. 불변성으로 인한 동시성 안전
    • 멀티쓰레드 환경에서도 안전하게 사용할 수 있습니다.

단점

  1. 메모리 사용량 증가
    • 변경된 노드의 복사가 추가 메모리를 요구합니다.
  2. 성능 오버헤드
    • 공유되지 않는 데이터가 많아지면 복사 비용이 증가할 수 있습니다.
  3. 복잡성 증가
    • 구조 설계 및 구현이 더 복잡할 수 있습니다.

6. 활용 사례

  1. Undo/Redo 시스템

    • 텍스트 에디터, 그래픽 편집기에서 사용.
  2. 버전(Version) 관리

    • Git과 같은 분산 버전 관리 시스템.
  3. 분산 시스템

    • 데이터베이스 및 네트워크 시스템에서 데이터 상태 관리.
  4. 함수형 프로그래밍

    • Immutable한 데이터 구조를 사용하는 언어 (예: Haskell, Clojure)에서 활용.

Persistent Data Structures는 데이터 불변성과 효율성을 동시에 제공하며, 다양한 시스템에서 데이터의 안정적이고 효율적인 관리에 활용됩니다.

728x90
반응형
728x90
반응형

캐시-친화 알고리즘 Cache-Friendly Algorithms

Cache-Friendly Algorithms 는 CPU 캐시 메모리의 작동 방식을 최대한 활용하여 성능을 최적화한 알고리즘입니다.

현대 CPU의 성능은 캐시를 얼마나 효과적으로 활용하느냐에 크게 의존하므로, 캐시 친화적인 알고리즘은 연산 속도와 메모리 접근 속도를 크게 향상시킬 수 있습니다.


1. 캐시 메모리와 지역성의 이해

캐시 메모리는 CPU와 RAM 사이의 고속 메모리로, 자주 사용되는 데이터를 저장해 CPU가 빠르게 접근할 수 있도록 합니다.

캐시를 잘 활용하려면 다음 두 가지 "지역성(Locality)" 원칙을 이해해야 합니다:

  • 시간적 지역성 (Temporal Locality):
    최근에 접근한 데이터는 가까운 미래에 다시 접근될 가능성이 높다.

    예: 루프(loop)에서 반복적으로 사용하는 변수.

  • 공간적 지역성 (Spatial Locality):
    최근에 접근한 데이터의 주변 데이터도 곧 접근될 가능성이 높다.

    예: 배열(array)의 연속된 요소 접근.

캐시 친화적인 알고리즘은 이 두 원칙을 활용하여 데이터를 효율적으로 배치하고 접근합니다.


2. Cache-Friendly Algorithm 설계 원칙

  1. 연속적인 메모리 접근
    데이터를 배열과 같이 연속적인 메모리에 저장하여 캐시 미스(miss)를 줄입니다.

    • 배열(Array): 연속적이므로 캐시 효율이 높음.
    • 링크드 리스트(Linked List): 노드가 분산되어 있어 캐시 비효율적.
  2. 블록 처리 (Blocking or Tiling)
    큰 데이터를 작은 블록으로 나누어 캐시 크기 내에서 작업을 처리합니다.

    예: 행렬 곱셈에서, 행렬을 블록 단위로 처리해 캐시 재사용성을 극대화.

  3. 데이터 재사용 극대화
    메모리에 접근할 때, 동일한 데이터에 여러 번 접근하도록 설계합니다.

    예: 한 번 로드한 데이터가 여러 계산에서 사용되도록 알고리즘 설계.

  4. 데이터 구조 최적화
    데이터 구조를 캐시 친화적으로 설계합니다.

    예: 트리 구조를 배열로 표현하거나, BFS 탐색에서 큐 대신 배열 사용.


3. Cache-Friendly Algorithm 사례

(1) 행렬 곱셈 (Matrix Multiplication)

기본 행렬 곱셈 알고리즘은 캐시 효율이 낮습니다.

cpp

// 비효율적 행렬 곱셈
for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
        for (int k = 0; k < N; k++) {
            C[i][j] += A[i][k] * B[k][j];
        }
    }
}

개선된 캐시 친화적 알고리즘: Blocking
행렬을 작은 블록으로 나눠 연속적으로 작업합니다.

cpp

int blockSize = 16;
for (int ii = 0; ii < N; ii += blockSize) {
    for (int jj = 0; jj < N; jj += blockSize) {
        for (int kk = 0; kk < N; kk += blockSize) {
            for (int i = ii; i < ii + blockSize; i++) {
                for (int j = jj; j < jj + blockSize; j++) {
                    for (int k = kk; k < kk + blockSize; k++) {
                        C[i][j] += A[i][k] * B[k][j];
                    }
                }
            }
        }
    }
}
  • 효과: 블록 내 데이터가 캐시에 적재된 상태로 반복 사용되므로 캐시 미스가 감소합니다.

(2) 병렬 퀵소트 (Parallel QuickSort)

기본 퀵소트는 재귀 호출이 깊어질수록 비캐시 친화적입니다.

  • 개선: 작은 서브배열에서는 재귀 대신 반복문으로 전환하거나, 배열 대신 연속된 메모리 블록을 사용합니다.

(3) 트리 순회 (Tree Traversal)

기본 트리 순회는 비캐시 친화적입니다. 이유는 트리가 포인터 기반으로 설계되어 메모리가 비연속적이기 때문입니다.

  • 개선: Cache-Oblivious Layout
    트리를 메모리 레이아웃 상에서 순차적 접근이 가능하도록 재구성합니다.

4. Cache-Friendly 알고리즘의 장단점

장점:

  • 성능 향상: 캐시 미스를 줄여 연산 속도가 크게 증가.
  • 효율적인 메모리 사용: 캐시 크기와 대역폭을 최대로 활용.
  • 전력 소비 감소: 메모리 접근이 줄어들어 전력 효율이 높아짐.

단점:

  • 복잡성 증가: 캐시 친화적으로 설계하는 데 추가적인 노력이 필요.
  • 하드웨어 의존성: 캐시 크기, 계층 구조 등 하드웨어 특성에 따라 성능이 다를 수 있음.

5. Cache-Friendly 알고리즘 설계 시 고려사항

  1. 캐시 크기와 계층 구조
    알고리즘 설계 시, L1/L2/L3 캐시 크기 및 라인 크기를 고려해야 합니다.

  2. 데이터 배치 최적화
    데이터 구조의 메모리 레이아웃을 재구성하여 캐시 효율을 높입니다.

  3. 워크로드 분석
    데이터 접근 패턴을 분석해 캐시 친화적인 방식으로 최적화합니다.


Cache-Friendly Algorithms는 하드웨어와 알고리즘 설계를 밀접히 연결하여, CPU 캐시를 최대한 활용하는 효율적인 방식입니다.

특히 대규모 데이터 처리, 고성능 컴퓨팅, 게임 개발 등에서 핵심적인 성능 최적화 기법으로 사용됩니다.

728x90
반응형
728x90
반응형

유클리드 호제법

  • 유클리드 호제법은 두 자연수의 최대공약수(GCD)를 효율적으로 구하는 알고리즘입니다.

  • 이 방법은 큰 수를 작은 수로 나누어 나머지를 구하고, 이 과정을 나머지가 0이 될 때까지 반복합니다.

  • 나머지가 0이 되는 순간의 나누는 수가 두 수의 최대공약수가 됩니다.

알고리즘 단계:

  1. 두 수 중 큰 수를 작은 수로 나누어 나머지를 구합니다.
  2. 이전 단계에서 작은 수를 이번 단계의 나머지로 대체하고, 나머지가 0이 될 때까지 이 과정을 반복합니다.
  3. 나머지가 0이 되면, 그때의 작은 수가 최대공약수입니다.

예시:

  • 1071과 1029의 최대공약수를 구해보겠습니다.
Step 1. 1071 % 1029 = 42
Step 2. 1029 % 42 = 21
Step 3. 42 % 21 = 0
  • 따라서, 1071과 1029의 최대공약수는 21입니다.

파이썬 예제 코드:

python

def gcd(m, n):
    while n != 0:
        t = m % n
        m = n
        n = t
    return abs(m)

# 예제 사용
num1 = 1071
num2 = 1029
print(f"{num1}와 {num2}의 최대공약수는 {gcd(num1, num2)}입니다.")
  • 이 코드는 두 수의 최대공약수를 계산하여 출력합니다.

참고

728x90
반응형
728x90
반응형

문자열(string) 필수 기능

문자열(string)
├── 문자열의 설정
├── 두 문자열 비교하기
├── 다른 형(type)의 자료형을 문자열로 변환하기
├── 문자열을 다른 자료형으로 변환하기
├── 문자열 중에서 임의의 문자(또는 문자열)을 찾기
│   ├──문자열 처음부터 찾기
│   ├──문자열 맨 뒤부터 찾기
│   ├──문자열 중 임의의 위치에서 찾기
├── 문자열의 길이 얻기
├── 임의의 문자로 검사
│   ├──임의의 문자로 시작하는지의 검사 여부
│   ├──임의의 문자로 종료하는지의 검사 여부
├── 문자열 중 임의의 문자열을 위치(offset), 길이(length)에 따라 추출하기
├── 문자열 중 임의의 문자(또는 문자열)을 다른 문자(또는 문자열)로 변경하기
├── 전체 모두 변경하기
├── 임의의 지정한 영역에서만 변경하기
├── 영어(English)일 경우, 대문자화, 소문자화

문자열은 프로그래밍에서 매우 중요한 요소로, 다양한 기능을 제공합니다. 이 글에서는 문자열의 필수 기능을 이해하기 쉽게 설명하겠습니다.

1. 문자열의 정의

문자열은 문자들의 연속으로, 텍스트 데이터를 표현하는 데 사용됩니다. 예를 들어, "Hello, World!"는 문자열입니다.

2. 문자열의 길이 확인

문자열의 길이는 문자열에 포함된 문자 수를 의미합니다. 프로그래밍 언어마다 문자열의 길이를 확인하는 방법이 다릅니다. 예를 들어, Python에서는 len() 함수를 사용하여 문자열의 길이를 확인할 수 있습니다.

3. 문자열 연결

두 개 이상의 문자열을 하나로 합치는 것을 문자열 연결이라고 합니다. 예를 들어, "Hello"와 "World"를 연결하면 "HelloWorld"가 됩니다. Python에서는 + 연산자를 사용하여 문자열을 연결할 수 있습니다.

4. 부분 문자열 추출

문자열의 일부분을 추출하는 것을 부분 문자열 추출이라고 합니다. 예를 들어, "Hello, World!"에서 "World"를 추출할 수 있습니다. Python에서는 슬라이싱(slicing)을 사용하여 부분 문자열을 추출할 수 있습니다.

5. 문자열 검색

문자열 내에서 특정 문자열이나 문자의 위치를 찾는 것을 문자열 검색이라고 합니다. 예를 들어, "Hello, World!"에서 "World"의 위치를 찾을 수 있습니다. Python에서는 find() 메서드를 사용하여 문자열을 검색할 수 있습니다.

6. 문자열 대체

문자열 내의 특정 문자열이나 문자를 다른 문자열이나 문자로 바꾸는 것을 문자열 대체라고 합니다. 예를 들어, "Hello, World!"에서 "World"를 "Python"으로 바꾸면 "Hello, Python!"이 됩니다. Python에서는 replace() 메서드를 사용하여 문자열을 대체할 수 있습니다.

7. 문자열 분할

문자열을 특정 구분자를 기준으로 나누는 것을 문자열 분할이라고 합니다. 예를 들어, "apple,banana,cherry"를 쉼표(,)를 기준으로 나누면 ["apple", "banana", "cherry"] 리스트가 됩니다. Python에서는 split() 메서드를 사용하여 문자열을 분할할 수 있습니다.

8. 문자열 결합

리스트 등의 여러 문자열을 하나의 문자열로 합치는 것을 문자열 결합이라고 합니다. 예를 들어, ["apple", "banana", "cherry"] 리스트를 쉼표(,)로 결합하면 "apple,banana,cherry" 문자열이 됩니다. Python에서는 join() 메서드를 사용하여 문자열을 결합할 수 있습니다.

9. 문자열 대소문자 변환

문자열의 모든 문자를 대문자나 소문자로 변환할 수 있습니다. 예를 들어, "Hello, World!"를 모두 대문자로 변환하면 "HELLO, WORLD!"가 됩니다. Python에서는 upper()lower() 메서드를 사용하여 문자열의 대소문자를 변환할 수 있습니다.

10. 문자열 공백 제거

문자열의 앞뒤에 있는 공백을 제거할 수 있습니다. 예를 들어, " Hello, World! "에서 앞뒤 공백을 제거하면 "Hello, World!"가 됩니다. Python에서는 strip() 메서드를 사용하여 문자열의 공백을 제거할 수 있습니다.

이러한 문자열의 기본 기능들을 이해하고 활용하면 프로그래밍에서 문자열을 효과적으로 다룰 수 있습니다.

728x90
반응형
728x90
반응형

디피 헬만(Diffe-Hellman) 알고리즘 이해하기

디피-헬만(Diffie–Hellman) 알고리즘은 왠지 호감이 가는(?) 인상의

아저씨들이 1976년에 만든 알고리즘입니다.

(사진은 1977년의 것입니다. 왼쪽부터 Ralph Merkle, Martin Hellman, Whitfield Diffie)

원리는 간단히 이야기하자면,

--- 아주 큰 2 개의 소수를 양쪽(peer)에서 공유하면서 시작합니다. ---

소수(prime number)는 자신과 1 로만 나누어 지는 수를 말합니다. (2, 3, 5, 7, ...)

일단 소수 1개(N)를 정의하여 두 노드간에 값을 공유합니다.

소수 N의 크기가 매우 클수록 암호화 수준이 올라갑니다.

G는 1부터 N-1 사이의 자연수입니다.

-- 여기까지는 이해가 물론 가시죠? --

그러면 이상태에서,

한쪽(1번측이라고 봅시다)에서 공개키(public key)인 R1을 생성합니다.

R1 = G^x mod N 입니다.

(^는 승수를 의미하고, mod는 나머지를 의미합니다. 2^3=8 이고 10 mod 3 = 1 입니다.)

이 때, x 는 물론 1번측의 비밀키(private key)입니다.

비밀키(private key)는 공개키와 다르게 외부에 노출되면 안되는 값입니다.

그리고 이 공개키인 R1 을 다른측(2번측)으로 전송합니다.

R1은 통신(전송)을 할 경우 값이 외부로 값이 노출 될 수 있습니다.

--- 여기까지는 이해가 가시죠? ---

그리고 다른쪽(=2번측)에서도 마찬가지로 2번측의 공개키인 R2를 만듭니다.

R2 = G^y mod N 입니다.

이 때, y 는 2번측의 비밀키(private key)입니다. 당연히 y는 공개되면 안되죠.

그리고 R2를 2번측에서 1번측으로 전송합니다.

이제 양쪽은 다른 쪽이 전송한 수(R1,R2)를 서로서로 알고 있습니다.

즉, 1번측에서는 2번측이 전송한 R2 를 알고 있고요,

반대로, 2번측에서는 1번측이 전송한 R1 을 알고 있습니다.

단, 상대방의 비밀키(x, y)는 모릅니다. 비밀키는 자신만이 알고 있습니다.

--- 여기까지도 이해가시리라 믿고요 ---

그 상태에서 1번측은

전송받은 수 R2 를 가지고 K1 = R2^x mod N 값을 계산합니다.

그리고 마찬가지로,

2번측도 전송받은 수 R1 으로 K2 = R1^y mod N 값을 계산합니다.

그러면, 이때 K1 = K2 은 관계가 성립합니다!

(둘은 수학적으로 동일하게 증명됩니다.)

그리고, K1 = K2 = K = G^(xy) mod N 의 관계도 성립하게 됩니다.

K는 1번측과 2번측 모두가 알 수 있는 키값입니다.

--- 이제 서로 키 교환과 연산은 끝났습니다. ---

마지막으로 생성된 키 K 를 이용하여서

일반적인 세션키 알고리즘(AES, SEED 등)의 키로

이용하는 것이 일반적인 사용방법입니다.

--- 이해되시나요? 설명이 별로죠. 죄송합니다. ㅋ ---

예전에는 귀찮아서 대충 라이브러리 함수로 대충 코딩했는데

지금 정리해보니까 대충 이런 내용이군요 ㅎ

728x90
반응형
728x90
반응형

과연 정렬 알고리즘을 몇 가지 이상 알아야 훌륭한 개발자인가?

개발자 채용 면접에서 정렬 알고리즘에 대한 질문이 자주 등장합니다. 일부 면접관은 지원자가 버블 정렬 이상의 알고리즘을 알고 있어야 합격 가능성이 높다고 생각합니다. 그러나 실제 업무에서 다양한 정렬 알고리즘을 모두 숙지하는 것이 얼마나 중요한지 의문이 듭니다.

모든 상황에 완벽하게 적용되는 '최적의' 정렬 알고리즘은 존재하지 않습니다. 데이터의 분포와 특성은 각기 다르기 때문에, 특정 알고리즘이 항상 최선의 선택이 될 수는 없습니다. 또한, 많은 프로젝트에서는 최적의 결과보다는 빠른 개발과 결과물을 중요시합니다.

예를 들어, 최상의 결과를 얻는 데 1년이 걸리고, 상용화 가능한 수준에 도달하는 데 6개월, 기본적인 데모 버전을 만드는 데 한 달이 소요된다고 가정해봅시다. 이러한 상황에서 경영진이나 프로젝트 관리자는 항상 최적의 결과만을 기다릴 수 없습니다. 때로는 버블 정렬과 같은 간단한 방법으로라도 데모 버전을 만들어 투자자나 이해관계자에게 빠르게 보여주는 것이 필요할 수 있습니다.

물론, 정렬 알고리즘에 대한 질문은 지원자의 기본적인 소양을 평가하기 위한 것입니다. 그러나 특정 기술에만 집중하는 것은 다양한 역량을 가진 인재를 놓칠 위험이 있습니다. 따라서, 면접에서는 지원자의 문제 해결 능력과 상황에 맞는 판단력을 함께 평가하는 것이 중요합니다.

728x90
반응형
728x90
반응형



한변의 길이가 2x인 정팔각형이 있다. 또한 정팔각형에 접하는 정사각형의 한변의 길이가 D이다.
이때 x를 D값으로 표현하면 어떻게 되는가?

답은 아래에 흰 글씨로 적어둔다.

답 :
----------------------------------------------------------
 x = 1 / 2 * ( 1 + root(2) ) * D
 root(2) = 문자그대로 루트 2이다. 2의 1/2승
 * 는 곱하기를 의미한다.

----------------------------------------------------------






728x90
반응형
728x90
반응형

버스트소스(Burstsort) : 알파벳 소문자 정렬의 초고속 정렬

Burstsort는 캐시 아키텍처에서 문자열을 정렬하는 데 매우 효율적인 알고리즘입니다.

이 알고리즘은 특히 알파벳 소문자로 구성된 문자열을 빠르게 정렬하는 데 최적화되어 있습니다.

2007년 5월 27일에 발표된 1.0 버전은 26개의 라틴 소문자로 이루어진 문자열만을 지원하며, 다양한 알파벳에 대한 지원은 향후 버전에서 추가될 예정입니다.

또한, 현재의 메모리 할당 전략이 최적이 아닐 수 있으며, 논문에서 제시된 결과와의 비교는 이루어지지 않았습니다.

이 라이브러리는 GPL 라이선스 하에 제공되며, 초기 소스 코드는 Visual C++ 2005로 작성되었습니다. (2007년)

현재(2007년)는 소문자 알파벳 26자만을 지원하지만, 향후 버전에서는 다양한 문자열과 한국어 등 다른 언어도 지원되기를 기대하고 있습니다.

자세한 내용과 소스 코드는 소스포지 프로젝트 페이지 에서 확인하실 수 있습니다.

728x90
반응형
728x90
반응형

두 선분의 교차 여부 체크

[발췌] 이재규, 2000

  • 세 점의 방향판단은 세 점 A, B, C 를 차례로 선분으로 연결할때 그 방향이 시계방향인지, 반시계방향인지를 판단하는 것이다.

  • 이때 벡터의 외적을 이용한다.

  • 오른손 법칙에 의하여, 세 점의 방향이 시계방향 🔃 일 경우 벡터의 외적은 ➖가 되고, 반시계방향 🔄 일 경우 ➕가 되게 된다.

  • 즉, 벡터 외적의 음양 부호에 따라서 판단하게 된다.
728x90
반응형
728x90
반응형

피보나치 수열 (Fibonacci number)

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, ...

  • 간단히 위와 같은 수의 열거(수열)이다.
  • 최초는 0 과 1 부터 시작하고,
  • 앞의 두 값을 합하여
  • 계속 새로운 수로 증가시킨다...

구현 알고리즘

1️⃣ n = 0 일때, f(n) = 0

2️⃣ n = 1 일때, f(n) = 1

3️⃣ n > 1 이면, f(n) = f(n-2) + f(n-1)

현재까지 소수와 다음 소수까지의 규칙성을 알아낸 수학 공식은 없으나, 작은 소수들간의 규칙성을 연구하는 분야는 계속 진행되어 왔다.

728x90
반응형

+ Recent posts