인덱스 바이너리

마지막 업데이트: 2022년 4월 18일 | 0개 댓글
  • 네이버 블로그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 트위터 공유하기
  • 카카오스토리 공유하기
익숙하게 만나는 Xcode 화면입니다. 나 인덱싱 중이니까 상태에 말 시키지마.

Crocus

펜윅 트리(Fenwick Tree, Binary Indexed Tree, BIT)

펜윅 트리(Fenwick Tree, Binary Indexed Tree, BIT)란?

이전 게시물에서는 세그먼트 트리에 대해 게시물을 올렸다. (세그먼트 트리 :: http://www.crocus.co.kr/648)

이 펜윅 트리를 이해하기 인덱스 바이너리 위해서는 세그먼트 트리가 필요충분조건은 아니지만,

세그먼트 트리를 먼저 공부하고 펜윅 트리를 공부하는 것을 추천한다.

펜윅 트리는 세그먼트 트리의 메모리를 더 절약하기 위해 만든 방법으로 만들어졌고 실제로 코드도 매우 간결하다.

즉, 펜윅 트리는 세그먼트 트리의 변형으로써

update O(lgN)에 그리고 query를 O(lgN)에 수행 할 수 있으며 메모리는 세그먼트 트리에 비해 덜 소모된다는 것이다.

펜윅 트리의 핵심 은 구간 합 대신 부분 합만을 빠르게 계산할 수 있는 자료 구조를 만들어도 구간 합을 계산할 수 있다 는 것이다.

(이때 부분 합은 0~k까지 합이고 구간 합은 a~b까지 합을 의미한다.)

자세한 내용은 아래에서 확인해보자.

펜윅 트리(Fenwick Tree, Binary Indexed Tree, BIT)와 세그먼트 트리(Segment Tree)

펜윅 트리와 세그먼트 트리가 어떻게 다른지 알아보자.

세그먼트 트리로 다음과 같이 되어있다고 보자.

이 세그먼트 트리를 펜윅 트리로 변형하면 아래와 같은 그림의 형식으로 변한다.

이 그림을 다음과 같이 변형 할 수 있다.

위의 트리 그림과 아래 도식화 그림이 같다는 느낌으로 다가온다면 지금부터 펜윅 트리의 진가를 알 수 있게 된다.

우선 펜윅 트리는 비트를 가지고 놀기 때문에 인덱스를 0번부터가 아닌 1번부터로 바꾸어준다.(코드에서 +1만 하면된다.)

펜윅 트리(Fenwick Tree, Binary Indexed Tree, BIT) 동작 방식

펜윅 트리 업데이트 방식

만약 1번 인덱스의 값을 5로 변경(update)하면 펜윅 트리에서 어떤 부분이 변경되어야 할까?

다음과 같이 1의 값을 5로 update 했다면 1, 1-2, 1-4, 1-8, 1-16의 부분을 업데이트 해주면 된다.

인덱스 바이너리

안녕하세요! MightyCoder입니다. 2021년 첫 포스트네요!(사실 이전 포스트도 몇개 없었지만 앞으로 더 열심히 블로그를 운영할 계획입니다..ㅎㅎ)

이번 포스트에서는 Javascript를 이용한 이진 탐색(Binary Search)을 소개하려고 합니다.

1. 알고리즘 설명

이진 탐색은 탐색할 범위를 축소해가며 원하는 값을 찾는 탐색 알고리즘입니다. 모든 탐색 범위를 전부 탐색하는 선형 탐색(Linear Search)보다 속도 면에서 빠르다는 장점이 있습니다. 그렇다면 어떠한 방식으로 탐색 범위를 축소하는 걸까요?

이진 탐색을 설명하기 위해 먼저 10개의 정수를 포함하고 있는 배열 arr 를 선언하겠습니다.

이제 위의 배열에서 8을 찾아보겠습니다. 아 인덱스 바이너리 그전에 이진 탐색에서 가장 중요한 부분! 이진 탐색은 정렬된 배열에 대해서만 수행이 가능합니다. 하지만 arr 배열은 현재 정렬되지 않은 상태입니다. 그렇다면 먼저 오름차순 정렬을 수행하겠습니다.

Javascript의 빌트인 함수인 sort를 이용해 오름차순 정렬을 수행했습니다. (sort 함수에 대한 자세한 설명은 이곳을 참고해주세요) 이분 탐색은 두 개의 포인터를 사용합니다. 보통 이 포인터들을 left, right로 네이밍하여 사용하는데 이름 그대로 왼쪽 부분에 존재하는 포인터와 오른쪽 부분에 있는 포인터를 가르킵니다. 그리고 이 포인터들의 위치하는 배열의 인덱스값을 사용해 배열의 중앙 인덱스값을 구합니다. 그럼 구현해보겠습니다.

left를 배열의 첫 인덱스 그리고 right를 마지막 인덱스로 초기화해줍니다. 그런 후 해당 인덱스값을 이용하여 중간 인덱스값인 mid를 구해줍니다. 이제 인덱스 바이너리 찾으려는 값과 배열의 중간 인덱스값을 비교하고 같을 경우 찾으려는 값을 반환하고 탐색을 종료합니다.

한번에 원하는 인덱스 바이너리 값을 찾는 경우는 정말 좋은 경우이지만 흔히 볼 수 있지는 않습니다. 찾고자 하는 값이 중간값이 아닐 인덱스 바이너리 경우가 더 많겠죠? 이 경우 이진 탐색은 이제 mid를 찾고자 하는 값과 비교합니다. 만약 mid가 찾고자 하는 값보다 작을 경우 left의 값을 mid보다 1만큼 증가시키고 반대의 경우는 right의 인덱스값을 mid보다 1만큼 작게 하락 시켜 탐색 범위를 줄입니다. 그림으로 다시 한번 8을 찾는 과정을 알아보겠습니다.

left는 현재 0번 인덱스에 자리하고 있고 right는 9번 인덱스에 있습니다. left와 right의 값을 가지고 mid를 구하면 5가 나옵니다.(반올림을 하느냐 안하느냐에 따라 6이 나올수도 있습니다) 일단 위의 경우는 mid가 5번 인덱스를 가르키고 있습니다.

배열의 5번째 인덱스에는 6이라는 값이 저장되어 있습니다. 우리가 찾는 값인 8은 아니고 8보다 작은 값입니다. 원하는 값보다 작으니 left 인덱스의 위치를 변경해줍니다. 새로운 left의 위치는 mid + 1이므로 6이 됩니다.

left의 값이 6으로 변경되었으니 새로운 mid의 값을 구해줍니다. Math.floor(left + right)를 해주면 7이 나오죠. 이제 배열의 7번 인덱스 값과 우리가 찾는 값을 비교해봅시다. 8과 8로 동일합니다. 그럼 원하는 값을 찾았으니 8을 반환해주고 함수를 종료하면 됩니다.

앞선 설명을 통해 이진 탐색을 살펴보았습니다. 이제부터는 실제로 Javascript를 이용해 구현해보도록 하겠습니다. 이진 탐색의 소스코드는 다음과 같습니다.

소스 코드에 대해 설명하겠습니다. binarySearch 함수는 매개변수로 탐색을 수행할 array와 탐색을 원하는 숫자인 targetValue를 받습니다. 함수 내부에서는 먼저 left와 right를 각각 배열의 0번째, 마지막 인덱스로 초기화해줍니다. 그 뒤에 while 문을 사용하여 left가 right보다 작거나 같을 동안 탐색을 수행합니다. while 문 안에서는 mid의 값을 구해주고 조건문을 통해 mid에 해당하는 인덱스의 요소가 탐색하고 있는 값과 같은지 비교합니다. 조건들은 다음과 같습니다.

  1. 같을 경우 → 탐색을 원하는 값을 반환하고 탐색을 종료합니다.
  2. mid 인덱스의 요소 > 탐색하는 값 → right의 값을 미드보다 1작은 값으로 설정(right = mid - 1)
  3. mid 인덱스의 요소 < 탐색하는 값→ left의 값을 미드보다 1 큰 값으로 설정(left = mid + 1)

만약 while 문이 끝날 때까지 원하는 값을 찾지 못한다면 while 문을 빠져나오고 -1을 반환하고 탐색을 종료합니다.

3. 시간복잡도

이진 탐색의 시간복잡도는 다음과 같습니다.

  • 최선: O(1)
  • 평균: O(log N)
  • 최악: O(log N)

최선의 경우는 첫 루프에서 원하는 값을 찾은 경우입니다. 이럴 경우 O(1)의 시간복잡도를 가지게 됩니다. 그 외의 경우에는 탐색할 범위가 대략 절반씩 줄어들기때문에 O(log N)의 시간복잡도를 인덱스 바이너리 가집니다.

  • 이진 탐색은 정렬된 배열에 대해서만 수행 가능하다.
  • 탐색을 위해 배열의 인덱스를 저장하는 left, right 포인터로 범위를 지정하고 해당 포인터들을 이용해 중앙 인덱스 값인 mid를 구한다.
  • mid에 해당하는 배열의 요소 값과 찾고자 하는 값을 비교한다.
  • left가 right보다 작거나 같을 때까지 탐색 과정을 반복한다.
  • left가 right보다 커질경우 탐색을 종료한다. 해당 경우는 원하는 값이 존재하지 않는 경우다.
  • 시간복잡도는 최선의 경우 O(1), 평균 및 최악의 경우 O(log N)이다.

이상으로 Javascript를 이용한 이진 탐색 소개를 마치도록 하겠습니다. 본 포스트는 개인적으로 공부를 하며 정리한 내용이므로 잘못된 내용이 포함되어 있을 수 있습니다. 잘못된 인덱스 바이너리 내용을 파악하셨을 경우 피드백 주시면 정말 감사하며 적극 반영하겠습니다. 감사합니다.

KB4055758-FIX: SQL Server에서 CHAR 및 BINARY 데이터 끝에 있는 후행 공백을 반환 하는 일관성 없는 동작

SQL Server 2016 Developer SQL Server 2016 Enterprise SQL Server 2016 Enterprise Core SQL Server 2016 Standard SQL Server 2016 Service Pack 1 SQL Server 2014 Developer SQL Server 2014 Enterprise SQL Server 2014 Enterprise Core SQL Server 2014 Standard SQL Server 2014 Service Pack 2 SQL Server 2017 Developer on Windows SQL Server 2017 Enterprise on Windows SQL Server 2017 Enterprise Core on Windows SQL Server 2017 Standard on Windows 더 보기. 간단히

하나 이상의 인덱스가 기본으로 제공 되는 Microsoft SQL Server 테이블을 사용 하 고 있다고 가정 합니다. 테이블에 대해 쿼리하면 SQL Server가 CHAR 및 BINARY 열에 대해 일관 되지 않은 데이터 패딩을 수행할 수 있습니다. CHAR 및 BINARY 데이터 열의 끝에 있는 후행 공백을 반환 하는 일관성 없는 동작은 다음 조건에 따라 달라 집니다.

SQL Server 테이블에 열이 만들어질 때 ANSI_PADDING의 인덱스 바이너리 설정입니다.

열에서 테이블 스캔 또는 인덱스 검색을 수행 하기로 결정 합니다.

특정 시나리오에서 SQL Server는 더 빠른 데이터 전달을 위해 여러 작업을 우회할 수 있도록 최적화를 수행할 수 있습니다. 이 문제는 SQL Server에서 위의 시나리오에 최적화를 적용할 수 없음을 인식 하지 못하기 때문에 발생 합니다.

추가 정보

이 SQL Server의 동작은 Char 및 Varchar 데이터를 사용 하 여다음 문서에 설명 된 동작을 따르지 않습니다.

Char NULL 열이 만들어질 때 ANSI_PADDING 켜져 있으면 char NULL이 아닌 열과 동일 하 게 작동 합니다. 값은 열 크기에 따라 오른쪽으로 채워집니다. Char NULL 열을 만들 때 ANSI_PADDING이 해제 되어 있는 경우, ANSI_PADDING 설정이 해제 된 varchar 열 처럼 작동 합니다. 후행 공백이 잘립니다.

인덱스 바이너리

This commit 인덱스 바이너리 does not belong to any branch on this repository, and may belong to a fork outside of the repository.

  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents

Copy raw contents

Copy raw contents

  • 문제 해석: root를 줄테니 inorder traversal 을 리턴해라
  • 알고리즘: DFS, Stack
  • 자료구조: Tree
  • 인덱스 바이너리
  • 문제 해결 과정: what is inorder traversal?
  1. Traverse the left subtree, i.e., call Inorder(left-subtree)
  2. Visit the root.
  3. Traverse the right subtree, i.e., call Inorder(right-subtree)

처음에 왜 왼쪽 - 중앙 - 오른쪽인데 루트부터 시작하는지 이해가 안갔었다. 3번부터 시작할 줄 알았는데 그렇진 인덱스 바이너리 않았다.

  1. 나를 기준으로 왼쪽에 노드가 있는가?
  2. 있으면 그 노드로 간다 2-1. 옮겨간 노드에 왼쪽에 노드가 있는가? 있으면 2번을 다시 실행(2와 2-1을 끝날 때 까지 반복, 끝나면 3을 실행)
  3. 없으면 나를 리턴한다. (answer배열에 값을 집어넣는다.)
  4. 다음 인덱스로 넘어간다.
  5. 인덱스값이 null이라면 다음 인덱스로 넘어간다.
  6. 1~5 단계를 반복

궁금증: 이 문제를 어떻게 재귀로 풀 지?

go to the next index 를 어떻게 하지? root[i+1] 로 체크하면 되려나.

if self.val 이 left를 가지고 있냐 를 어떻게 표현하지? c#에서 contains가 bool값을 리턴함. 하지만 파이선에서는 어떻게 표현할 지 모르겠음

go to the left node 를 어떻게 하지? "왼쪽으로 간다" 라는 말이 너무 자연어임. temporary를 만들어서(혹은 dp에서 cache) 현재 노드값을 저장하고 현재 노드값에다가는 left값을 저장하면 될 거 같은데 여기서 left는 어떻게 정의되는 거지.

Xcode: 비밀스러운 인덱스 저장소

익숙하게 만나는 Xcode 화면입니다. 나 인덱싱 중이니까 상태에 말 시키지마.

빌드Build와 인덱스Index

빌드와 인덱스는 거의 같은 동작하지만, 실제로는 조금 다릅니다. 빌드는 프로젝트에 설정되어 있는 타깃 단위로 제품을 만드는 과정입니다. 흔히 소스 파일을 컴파일해서 목적 파일을 만들고, 목적 파일들을 링크해서 바이너리를 생성합니다. 바이너리와 함께 앱 번들을 만들기 위해서 리소스도 빌드합니다.

인덱스는 빌드를 위한 게 아니라 빌드를 하는 과정에서 만들어지는 개발 도구에서 심볼을 빨리 접근하기 위한 말 그대로 색인Index입니다. Xcode를 위한 데이터인거죠.

LLVM clang은 C/C++/Objective-C 소스를 컴파일 하면서 부가적으로 Index를 만들어 냅니다. -index-store-path 옵션과 경로를 붙여서 원하는 경로를 지정할 수 있습니다. Swift 컴파일러 swiftc도 clang을 fork해서 만들었기 때문에 같은 옵션을 제공합니다.

빌드할 때 심볼에 대한 색인을 생성하는 것은 컴파일러가 3-5% 정도 더 일을 하게 된다고 인덱스 바이너리 인덱스 바이너리 합니다. clang과 swiftc는 3.2% 정도 오버헤드가 걸린다는 2017년 발표자료가 있습니다

Index 저장소

빌드 과정에서 만들어진 심볼 색인 — Index을 관리하는 것은 IndexStore DB라고 부릅니다. Index 저장소는 빌드 결과가 저장되는 DerivedData 경로 아래에 있습니다.

맨 처음이 빌드할 때마다 파일이 생성되는 Build 폴더이고, 안에는 Debug/Release Configuration에 따라 빌드된 타깃 Products가 있습니다. 예제코드는 HelloPlane 이라는 샘플 프로젝트의 DerivedData 경로입니다.

색인 저장소 경로 DataStore

두 번째 폴더가 바로 Index 저장소 경로이고, 그 아래에 DataStore 폴더가 인덱스가 저장되는 색인 저장소입니다. 거기에 있는 v5가 실제로 기록이 보관된 경로입니다.


0 개 댓글

답장을 남겨주세요