560 likes | 982 Views
Chapter 2 배열과 구조 (Arrays and Structures) 2009. 3. 23. 포인터. 포인터 C 언어에서는 모든 타입에 대해 그것의 포인터 타입이 존재 포인터 변수의 값은 메모리 상의 주소 (address) 포인터 관련 연산 주소 / 참조 (address/reference) 연산자 : & 역참조 / 간접참조 (dereferencing/indirection) 연산자 : * 예 int i, *pi
E N D
포인터 • 포인터 • C언어에서는 모든 타입에 대해 그것의 포인터 타입이 존재 • 포인터 변수의 값은 메모리 상의 주소(address) • 포인터 관련 연산 • 주소/참조 (address/reference) 연산자: & • 역참조/간접참조 (dereferencing/indirection) 연산자: * • 예 int i, *pi pi = &i; // pi는 i의 주소값을 가짐. 즉, i를 가리킴 // i에 10을 저장하기 i = 10; *pi = 10;
포인터 • 동적 메모리 할당(dynamic storage allocation) • 프로그램을 작성 시 얼마나 많은 메모리 공간이 필요한지 알 수 없을 때 • 실행-시간 메모리 할당을 위해 heap 이용 • 새로운 메모리 공간이 필요할 때마다 함수 malloc을 호출해서 필요한 양의공간을 할당 받음 int i, *pi; float f, *pf; pi = (int *)malloc(sizeof(int)); pf = (float *)malloc(sizeof(float)); *pi = 1024; *pf = 3.14; printf(“an integer=%d, a float=%f\n”,*pi,*pf); free(pi); free(pf);
ADT로서의 배열 • 배열(array) • 일련의 연속적인 메모리 위치 • 구현 중심의 이해 • 추상 데이터 타입 • <index, value> 쌍의 집합 • 값들은 동일한 타입 • 인덱스와 값 사이의 대응 또는 사상(mappings) • array : i → ai • 수행 가능한 연산 정의 • Array Create(j-dimension, j-range list) • Item Retrieve(array, index) • Array Store(array, index, item)
ADT로서의 배열 Structure Array Objects : index의 각 값에 대하여 집합 item에 속한 한 값이 존재하는 <index, value>쌍의 집합. index는 일차원 또는 다차원의 유한 순서 집합이다. 예를 들면, 일차원의 경우 {0,‥, n-1}과 이차원 배열 {(0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2)}, 등이다. Functions : 모든 A ∈ Array, i ∈ index, x ∈ item, j, size ∈ integer Array Create(j, list) ::= return j차원의 배열. 여기서 list는 j-tuple(i번째 원소는 i번째 차원의 크기를 나타냄)이며, item들은 정의되지 않았음. Item Retrieve(A, i) ::= if (i ∈ index) return 배열 A의 인덱스 i값과 관련된 항목. else return 에러. Array Store(A, i, x) ::= if (i ∈ index) return 새로운 쌍<i, x>가 삽입된 배열 A. else return 에러. end Array
C언어에서의 구현 • C의 일차원 배열 int list[5], *plist[5]; • 정수의 집합 : { list[0], list[1], list[2], list[3], list[4] } • 정수 포인터의 집합 : { plist[0], plist[1], plist[2], plist[3], plist[4] } • 배열 이름 = 포인터 상수(첫번째 원소의 주소) int list2[5]; • list2: list2[0]를 가리키는 포인터 (즉, list2[0]의 주소) • list2+i: list2[i]를 가리키는 포인터 (즉, list2[i]의 주소) • (list2+i) == &list2[i], *(list2+i) == list2[i]
C언어에서의 구현 • 배열 프로그램의 예 #define MAX_SIZE 100 float sum(float [], int); float input[MAX_SIZE], answer; int i; void main(void) { for (i = 0; i < MAX_SIZE; i++) input[i] = i; answer = sum(input, MAX_SIZE); printf("The sum is: %f\n", answer); } float sum(float list[], int n) // float list[] == float *list { int i; float tempsum = 0; for (i = 0; i < n; i++) tempsum += list[i]; // list[i] == *(list+i) return tempsum; }
C언어에서의 구현 • 호출시 input(=&input[0])은 형식 매개 변수 list에 복사됨 • 즉, list[i] == input[i] • 역참조(dereference) • list[i]가 “=”기호 우측 (list + i)가 가리키는 값 반환 • list[i]가 “=”기호 좌측 값을 (list + i) 위치에 저장 ※ C언어의 매개변수 전달방식 : call-by-value 실 매개변수가 배열 이름인의 경우 배열의 주소를 형식매개변수에 복사함 배열 자체는 복사되지 않음
C언어에서의 구현 • 예제[일차원 배열의 주소 계산] short one[] = {0, 1, 2, 3, 4}; printf1(&one[0], 5) // == print1(one, 5) void print1 (short *ptr, int rows) { /* print out an one-dimensional array using a pointer */ int i; printf (“Address Contents\n”); for (i=0; i<rows; i++) printf(“%8u%5d\n”, ptr + i, *(ptr + i)); // &ptr[i], ptr[i]와 동일 printf(“\n”); }
동적 할당 배열 #define MALLOC(p, s)\ if(!((p) = malloc(s))) {\ fprintf(stderr, “Insufficient memory”):\ exit(EXIT_FAILURE):\ } #define CALLOC(p, n, s)\ if(!((p) = calloc(n, s))) {\ 메모리를 할당하고 0으로 초기화함 fprintf(stderr, “Insufficient memory”):\ exit(EXIT_FAILURE):\ } #define REALLOC(p, s)\ if(!((p) = realloc(n, s))) {\ 메모리를 재할당 (크기 변경) fprintf(stderr, “Insufficient memory”):\ exit(EXIT_FAILURE):\ }
동적 할당 배열 • 1차원 배열 동적 생성 예 int i, n, *list; printf(“Enter the number of number to generate: ”); scanf(“%d”, &n”); if (n < 1) { fprintf(stderr, “Improper value of n \n”); exit (EXIT_FAILURE); } MALLOC(list, n * sizeof(int)); ※ n < 1이거나 정렬할 수의 리스트를 저장할 메모리가 충분치 않을 때 실패
동적 할당 배열 • 2차원 배열 동적 생성 예 • int x[3][5] [1] [4] [0] [2] [3] x x[0] x[1] x[2] int ** make2dArray(int rows, int cols) { /* create a two dimensional rows cols array */ int **x, i; /* get memory for row pointers */ MALLOC (x, rows * sizeof(int*));; /* get memory for each row */ for (i = 0; i < rows; i++) MALLOC (x[i], cols * sizeof(int)); return x; }
동적 할당 배열 • 사용 예 • 510 2차원 정수 배열에 대한 메모리 할당 • 이 배열의 [2][4]원소에 정수 6을 지정 int **myArray; myArray = make2dArray(5, 10); myArray[2][4] = 6
구조체(Structure) 및 공용체(Union) • 구조(structure) : struct • 타입이 다른 데이터들을 그룹화 함 • 데이터 항목들의 집단 - 각 항목은 타입과 이름으로 식별 • 멤버(member) 연산자 . • strcpy(person.name, "james"); • person.age =10; • person.salary = 35000; struct { char name[10]; int age; float salary; } person;
구조체(Structure) 및 공용체(Union) • typedef 문장을 사용한 구조체 데이터 타입 생성 • 전체 구조의 동등성 검사 : if (person1 == person2) • 구조 치환 : person1 = person2 (ANSI C) • strcpy(person1.name, person2.name); • person1.age = person2.age; • person1.salary = person2.salary; typedef struct human_being { char name[10]; int age; float salary; }; /* 변수 선언 */ human_being person1, person2; typedef struct { char name[10]; int age; float salary; } human_being; /* 변수 선언 */ human_being person1, person2;
구조체(Structure) 및 공용체(Union) • 구조체 속에 또 다른 구조체 정의 • 예 typedef struct { int month; int day; int year; } date ; typedef struct human_being { char name[10]; int age; float salary; date dob; } ; person1.dob.month = 2; person1.dob.day = 11; person1.dob.year = 1944;
구조체(Structure) 및 공용체(Union) • 공용체(Union) • 공용체에 속한 각 필드들은 메모리 공간을 공유함 • 어느 한 시점에 한 필드만 활동적이 되어 사용 가능 • C에서는 공용체 필드의 올바른 사용 여부를 검사하지 않음 typedef struct gender_type { enum tag_field {female, male} gender; union { int children; int beard; } u ; }; typedef struct human_being { char name[10]; int age; float salary; date dob; gender_type g_info; }; human_being person1, person2; person1.g_info.gender = male; person1.g_info.u.beard = FALSE; person2.g_info.gender = female; person2.g_info.u.children = 4;
구조체(Structure) 및 공용체(Union) • 자체참조 구조체 (self-referential structure) • 구성요소중 자신을 가리키는 포인터가 존재하는 구조체 typedef struct list { char data; list *link; }; list item1, item2, item3; item1.data = 'a'; item2.data = 'b'; item3.data = 'c'; item1.link = item2.link = item3.link = NULL; item1.link = &item2;구조들을 서로 연결 item2.link = &item3;(item1→ item2→ item3)
순서 리스트 • 순서 리스트, 선형 리스트(Ordered list, Linear list) • 순서가 있는 원소들의 집합 • 예 • 한 주일의 요일: (일,월,화,수, .., 토) • 카드 한 벌의 값: (Ace, 2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, King) • 건물 층: (지하, 로비, 1층, 2층) • 미국의 WWII 참전년도: (1941, 1942, 1943, 1944, 1945) • 스위스의 2차 세계대전 참전 년도: () • 리스트 형태: (a0, a1, ... , an-1) • 공백 리스트: () -> 포함된 항목이 없음
순서 리스트 • 리스트에 대한 연산 • 리스트 길이 n의 계산 • 리스트의 항목을 왼쪽에서 오른쪽 (오른쪽에서 왼쪽) 으로 읽기 • i 번째 항목을 검색, 0 i < n • i 번째 항목을 대체, 0 i < n • i 번째 위치에 새 항목 삽입, 0 i <n • i 번째 항목을 삭제, 0 i < n • 순서 리스트의 일반적인 구현 (기억장소 표현) • 순차 사상 (sequential mapping) • 배열 이용 • 물리적 인접성 이용 • 장점: 상수 시간내에 항목 검색/변경 가능 • 문제점: 삽입, 삭제시 오버헤드 • 비순차 사상(non-sequential mapping) • 비연속적 기억장소 위치 • 연결 리스트로 표현 : pointer(즉, link)를 가지는 노드들의 집합
다항식 추상 데이터 타입 • 다항식 (polynomial) • A(x) = aiXei = anxn+an-1xn-1+...+a1x1+a0x0 ai : 계수(coefficient), ai 0 ei : 지수(exponent), unique, 0 x : 변수(variable) • 예 A(x) = 3x20 + 2x5 + 4 , B(x) = x4 + 10x3 + 3x2 + 1 • 차수(degree): 가장 큰 지수 • 다항식의 합과 곱 • A(x) = aixi, B(x) = bixi 일 때, A(x) + B(x) = (ai + bi) xi A(x) · B(x) = (aixi · (bjxj))
polynomial 추상 데이터 타입 Structure Polynomial Objects : P(x) = +・・・+ : <ei, ai>의 순서쌍 집합. 여기서 ai는 Coefficient, ei는 Exponents. ei (≥ 0)는 정수. Function : 모든poly, poly1, poly2 ∈ polynomial, coef ∈ Coefficients, expon ∈ Exponents에 대해 Polynomial Zero() ::= return 다항식, p(x) = 0 Boolean IsZero(poly) ::= if (poly) return FALSE else return TRUE Coefficient Coef(poly, expon) ::= if (expon ∈ poly) return 계수 else return 0 Exponent Lead_Exp(poly) ::= return poly에서 제일 큰 지수 Polynomial Attach(poly, coef, expon) ::= if (expon ∈ poly) return 오류 else return <coef, exp>항이 삽입된 다항식poly Polynomial Remove(poly, expon) ::= if (expon ∈ poly) return 지수가 expon인 항이 삭제된 다항식 poly else return 오류 Polynomial SingleMult(poly, coef, expon) ::= return 다항식 poly‧coef‧xexpon Polynomial Add(poly1, poly2) ::= return 다항식 poly1 + poly2 Polynomial Mult(poly1, poly2) ::= return 다항식 poly1‧poly2 end polynomial
Add 연산 /* d = a + b, 여기서 a, b, d는 다항식이다. */ d = Zero(); while (! IsZero(a) && ! IsZero(b)) do { switch COMPARE(Lead_Exp(a), Lead_Exp(b)) { case -1: d = Attach (d, Coef(b, Lead_Exp(b)), Lead_Exp(b)); b = Remove(b, Lead_Exp(b)); break; case 0: sum = Coef(a, Lead_Exp(a)) + Coef(b, Lead_Exp(b)); if (sum) { Attach(d, sum, Lead_Exp(a)); a = Remove(a, Lead_Exp(a)); b = Remove(b, Lead_Exp(b)); } break; case 1: d = Attach(d, Coef(a, Lead_Exp(a)), Lead_Exp(a)); a = Remove(a, Lead_Exp(a)); } } a 또는 b의 나머지 항을 d에 삽입한다.
다항식의 표현 1 • 가정: 서로 다른 지수들을 내림차순으로 정렬 • [표현 1] 모든 차수에 대한 계수 저장 • 계수 배열 크기는 기정의된 값(최대값)으로 • a: polynomial, n < MAX_DEGREE • a.degree = n • a.coef[i] = an-i, 0 ≤ i ≤ n • 예 • A(x) = x4+10x3+3x2+1 : n = 4 • A = (4, 1, 10, 3, 0, 1) • 장점: 다항식에 대한 연산 알고리즘이 간단 • 단점: 저장 공간 낭비 • a.degree << MaxDegree 또는 Sparse polynomial의 경우 #define MAX_DEGREE 101 /* 다항식의 최대 차수 + 1 */ typedef struct int degree; float coef[MAX_DEGREE]; } polynomial;
다항식의 표현 2 • [표현 2] 0이 아닌 계수-지수 쌍 만 저장 • 희소 다항식의 경우 a.coef[MAX_DEGREE]의 대부분이 필요없음 • A(x) = x1000 + 1 : n = 1000 • A = (1000, 1, 0,..., 0, 1) : 1002 elements ↳ 999 • A(x) = bm-1xe_m-1 + bm-2xe_m-2 + ... + boxe_0 where bi ≠ 0 (0≤i≤m-1), em-1>em-2>...>eo≥0 A = (m, em-1, bm-1, em-2, bm-2, ... , e0, b0) • 예 • A(x) = x4+10x3+3x2+1 : A = (4, 4, 1, 3, 10, 2, 3, 0, 1) • A(x) = x1000 +1 : A = (2, 1000, 1, 0, 1)
다항식의 표현 2 • 여러 개의 다항식 표현: 모든 다항식을 하나의 전역 배열에 저장 A(x)=2x1000+1 B(x)=x4+10x3+3x2+1 starta finisha startb finishb avail coef exp A(x) : <starta, finisha> finisha = starta + n - 1 • 장점: 많은 항이 0인 경우 우수 • 단점: 모든 항이 0이 아닌 경우 표현 1보다 두 배의 저장 장소 필요 MAX_TERMS 100 /* 항 배열의 크기 */ typedef struct { float coef; int expon; } Polynomial; Polynomial terms[MAX_TERMS]; int avail = 0;
다항식의 표현 2: Add 연산의 구현 void padd(int starta, int finisha, int startb, int finishb,int *startd, int *finishd); { /* A(x) 와 B(x)를 더하여 D(x)를 생성한다 */ float coefficient; *startd = avail; while (starta <= finisha && startb <= finishb) switch (COMPARE(terms[starta].expon, terms[startb].expon)) { case -1: /* a의 expon이 b의 expon보다 작은 경우 */ attach(terms[startb].coef, terms[startb].expon); startb++; break; case 0: /* 지수가 같은 경우 */ coefficient = terms[starta].coef + terms[startb].coef; if (coefficient) attach(coefficient, terms[starta].expon); starta++; startb++; break; case 1: /* a의 expon이 b의 expon보다 큰 경우 */ attach(terms[starta].coef, terms[starta].expon); starta++; } for( ; starta <= finisha; starta++ ) /* A(x)의 나머지 항들을 첨가한다 */ attach(terms[starta].coef, terms[starta].expon); for( ; startb <= finishb; startb++ ) /* B(x)의 나머지 항들을 첨가한다 */ attach(terms[startb].coef, terms[startb].expon); *finishd = avail-1; }
다항식의 표현 2: Add 연산의 구현 void attach(float coefficient, int exponent) {/* 새 항을 다항식에 첨가한다. */ if (avail >= MAX_TERMS) { fprintf(stderr, "다항식에 항이 너무 많다."); exit(1); } terms[avail].coef = coefficient; terms[avail++].expon = exponent; }
다항식의 표현 2: Add 연산의 구현 • 알고리즘 분석 • m, n (>0) : 각각 A와 B의 0이 아닌 항의 수 • while 루프 : O(n+m) • 각 반복마다 starta나 startb 또는 둘 다 값이 증가 • 반복 종료 조건: starta > finisha || startb > finishb (즉, 어느 한쪽이 모두 검사될 때까지 반복) • 반복 횟수 ≤ m+n-1 -> O(n+m) • m+n-1인 경우: A(x) = x2i , B(x) = x2i+1일 때 • for 루프 : O(m), O(n) ∴ 총 연산시간 = O(n+m) • 문제점 • avail = MAX_TERMS일 때 • 불필요한 다항식 제거 후 배열 끝에 연속적인 가용공간 생성 필요 • 데이터 이동 및 각 다항식의 starti, finishi변경 필요
희소 행렬(Sparse Matrix) • A[m][n]: m n 행렬 A • m : 행의 수, n : 열의 수, m n : 원소의 수 • m = n : 정방 행렬(square matrix) • 각 원소 A[i][j]에 대한 접근: O(1) • 2차원 배열로 구현 시 공간 복잡도: O(m·n) • 희소 행렬(sparse matrix) • 0이 아닌 원소 개수 / 전체 원소 개수 << small • 메모리를 효율적으로 사용하기 위해서는 0이 아닌 원소만 저장해야 함 • 행렬에 대한 연산 • creation • addition • multiplication • transpose
밀집 행렬과 희소 행렬의 예 3 0 1 2 4 5 0 1 2 0 0 0 0 1 1 2 2 3 3 4 4 5 밀집 행렬 희소 행렬
희소 행렬 추상 데이터 타입(ADT) Structure SparseMatrix Objects : 3 원소쌍 <행, 열, 값>의 집합. 행, 열, 값은 정수이고, 행과 열의 각 조합은 유일 Function : SparseMatrix Create(int MaxRow, int MaxCol); // MaxRow x MaxCol (=MaxItems)을 저장할 수 있는 SparseMatrix를 생성함 SparseMatrix Transpose(SparseMatrix a); // a의 모든 3원소 쌍의 행과 열의 값을 서로 교환하여 얻은 행렬을 반환함 SparseMatrix Add(SparseMatrix a, SparseMatrix b); // if a와 b의 차원이 같으면 대응 항들, 즉, 같은 행과 열의 값을 // 가진 항들을 더해서 만들어진 행렬을 반환 // else 오류를 발생 SparseMatrix Multiply(SparseMatrix a, SparseMatrix b); // if a의 열의 수와 b의 행의 수가 같으면 d(i,j)= (a[i][k] b[k][j])인 // 행렬 d를 반환 // else 오류를 발생 end SparseMatrix
희소 행렬의 효율적인 표현 • 표현 방법 (필요한 정보들) • i. 0이 아닌 값을 갖는 <행, 열, 값> 3원소 쌍(triple) • ii. 행의 수 • iii. 열의 수 • iv. non-zero 원소의 수 • v. 순서: 행우선 순서 또는 열우선 순서 • #define MAX_TERMS 101 • typedef struct { • int row; • int col; • int value; • } term; • term a[MAX_TERMS]; • - a[0].row: the number of rows • - a[0].col: the number of columns • - a[0].value: the total number of • non-zeros • - We choose row-major order • => term들의 순서 리스트로 표현
희소 행렬의 효율적인 표현 행 열 값 [0] 6 6 8 a 3 0 1 2 4 5 0 [1] 0 0 15 [2] 0 3 22 1 [3] 0 5 -15 2 [4] 1 1 11 3 [5] 1 2 3 4 [6] 2 3 -6 5 [7] 4 0 91 [8] 5 2 28 예제 희소 행렬 순서 리스트 표현
3 0 1 2 4 5 0 1 2 3 4 5 A의 전치 행렬 AT 예제 희소 행렬 및 전치 행렬 3 0 1 2 4 5 0 1 2 3 4 5 희소 행렬 A
행 열 값 [0] a 6 6 8 [1] 0 0 15 [2] 0 4 91 [3] 1 1 11 [4] 2 1 3 [5] 2 5 28 [6] 3 0 22 [7] 3 2 -6 [8] 5 0 -15 A의 전치 행렬 AT 순서 리스트 표현 행 열 값 [0] 6 6 8 a [1] 0 0 15 [2] 0 3 22 [3] 0 5 -15 [4] 1 1 11 [5] 1 2 3 [6] 2 3 -6 [7] 4 0 91 [8] 5 2 28 희소 행렬 A
희소 행렬의 전치(transpose) • 단순 알고리즘 for (각 행 i 에 대해) 원소 <i, j, 값>을 가져와서 전치행렬의 원소 <j, i, 값>으로 저장 • (예) (0, 0, 15) → (0, 0, 15) (0, 3, 22) → (3, 0, 22) (0, 5, -15) → (5, 0, -15) (1, 1, 11) → (1, 1, 11) • 문제점: 올바른 순서 유지 위해 기존 원소를 이동해야 함 • 알고리즘 1 for (각 열 j에 대해) 열 j 에 있는 모든 원소 <i, j, 값>을 원소 <j, i, 값>에 저장
희소 행렬의 전치 알고리즘 1 void transpose(term a[], term b[]) /* a를 전치시켜 b를 생성 */ { int n, i, j, currentb; n = a[0].value;/* a 행렬에 포함된 0이 아닌 원소의 개수 */ b[0].row = a[0].col; /* b의 행 수 = a의 열 수 */ b[0].col = a[0].row; /* b의 열 수 = a의 행 수 */ b[0].value = n;/* b의 원소 수 = a의 원소 수 */ if (n > 0) { /* 0이 아닌 행렬 */ currentb = 1; for (i = 0; i < a[0].col; i++) /* a의 각 열에 대해 반복 */ for (j = 1; j <= n; j++) /* 모든 원소들을 검사하여*/ if (a[j].col == i) { /* i 열에 속한 원소들을 찾음 */ /* 발견된 원소를 b에 추가 */ b[currentb].row = a[j].col; b[currentb].col = a[j].row; b[currentb].value = a[j].value; currentb++; } } }
희소 행렬의 전치 알고리즘 1 • 시간 복잡도: O(columns elements) = O(rows columns2) • [Note] 희소 행렬을 단순한 2차원 배열로 표현할 경우 전치 방법 for (int j = 0; j < columns; j++) for (int i = 0; i < rows; i++) B[j][i] = A[i][j]; O(rows columns) • 공간 절약을 위해 시간을 희생한 결과
희소 행렬의 전치 알고리즘 2 • 메모리 공간을 조금 더 사용한 알고리즘: FastTranspose • 먼저, 행렬 a의 각 열에 대한 원소 수를 구함 • 즉, 전치 행렬 b의 각 행의 원소 수를 결정 • 이 정보로부터 전치 행렬 b의 각 행의 시작 위치를 구함 • 행렬 a에 있는 원소를 하나씩 전치 행렬 b의 올바른 위치로 옮김 • 구현 방법: 부가적인 배열 이용 • row_terms[i]: the number of elements in the row i of the result matrix b • starting_pos[i]: the starting point of the elements in the row i in the sparse matrix representation of b 결과 행렬에 row = i 인 원소를 저장할 때 마다 1 증가시켜, 다음 원소의 저장 위치를 가리키도록 함
희소 행렬의 전치 알고리즘 2 행 열 값 행 열 값 [0] 6 6 8 [0] b 6 6 8 a [1] 0 0 15 [1] 0 0 15 [2] 0 3 22 [2] 0 4 91 [3] 0 5 -15 [3] 1 1 11 [4] 1 1 11 [4] 2 1 3 [5] 1 2 3 [5] 2 5 28 [6] 2 3 -6 [6] 3 0 22 [7] 4 0 91 [7] 3 2 -6 [8] 5 2 28 [8] 5 0 -15 0, 1, 2, 3, 4, 5 행: row_terms[] = {2, 1, 2, 2, 0, 1} starting_pos[] = {1, 3, 4, 6, 8, 8}
희소 행렬의 전치 알고리즘 2 void fast_transpose(term a[], term b[]) /* a를 전치시켜 b에 저장 */ { int row_terms[MAX_COL], starting_pos[MAX_COL]; int i, j, num_col = a[0].col, num_terms = a[0].value; b[0].row = a[0].col; b[0].col = a[0].row; b[0].value = a[0].value; if (num_terms > 0) { /* 0이 아닌 행렬 */ for(i = 0; i < num_cols; i++)row_terms[i] = 0; for(i = 1; i <= num_terms; i++) row_terms[a[i].col]++; starting_pos[0] = 1; for(i = 1; i < num_cols; i++) starting_pos[i] = starting_pos[i-1] + row_terms[i-1]; for(i = 1; i <= num_terms; i++) { j = starting_pos[a[i].col]++; b[j].row = a[i].col; b[j].col = a[i].row; b[j].value = a[i].value; } } }
희소 행렬의 전치 알고리즘 2 • 시간 복잡도: O(columns + elements) = O(rows columns) 그러나, 희소 행렬에서 elements는 일반적으로 rows columns 보다 훨씬 작으므로 2차원 배열 알고리즘 보다 빠름 (즉, 시간과 공간을 동시에 절약하는 효과를 거둘 수 있음)
행렬의 곱셈 • Result A B • Result(mp) A(mn) B(np) • [note] 통상적인 곱셈 알고리즘 for (int i = 0; i < A.Rows; i++) for (int j = 0; j < B.Cols; j++) { sum = 0; for (int k = 0; k < A.Cols; k++) sum += (a[i][k] * b[k][j]); c[i][j] = sum; } O(A.Rows • A.Cols • B.Cols)
행렬의 곱셈 • 순서 리스트로 표현된 두 희소 행렬의 곱셈 • 결과 행렬(순서 리스트 표현)의 원소들을 행-우선 순서대로 계산 • 계산 및 저장된 원소들을 이동할 필요 없음 • 행렬 A에서 한 행을 선택하고, j = 0,1,..., cols_b-1에 대해 B의 j열에 있는 모든 원소를 찾아서 벡터 내적을 반복 계산해야 함 • 효율적인 계산을 위해 우선 행렬 B에 대한 전치 행렬을 구함 • B의 j열의 원소들을 순서대로 찾을 수 있음 (B의 j열 = BT의 j행) • A의 i행과 BT의 j행(= B의 j열)의 원소들이 정해지면 다항식 덧셈과 유사한 합병 연산 수행 • 알고리즘 및 시간 복잡도 계산 • 교재 참조 • O(B.Cols • A.Terms + A.Rows • B.Terms) => 최악의 경우 O(A.Rows • A.Cols • B.Cols) 이나, A, B가 희소 행렬이면 일반적인 알고리즘보다 훨씬 빠름
다차원 배열의 표현 • 배열의 원소 수 : A[upper0][upper1]...[uppern-1] = • 예: A[10][10][10] → 10・10・10 = 1000 개 • 행 우선 순서(row major order) • A[upper0][upper1] A[0][0], A[0][1], ⋯ , A[0][upper1-1] ⋯ row0 row1 row2 row_upper0-1 • 열 우선 순서(column major order) A[0][0], A[1][0], ⋯, A[upper0-1][0] ⋯ col0 col1 col2 col_upper1-1
다차원 배열의 표현 • 원소의 메모리 주소 계산 (2차원 배열 – 행우선 표현) • α = A[0][0]의 주소 A[i][0]의 주소 = α + i·upper1 A[i][j]의 주소 = α + i·upper1 + j • 주소 계산 (3차원 배열 – 행우선 표현) • A[upper0][upper1][upper2] • 2차원 배열upper1× upper2 → upper0개 A[i][0][0] 주소 = α + i·upper1·upper2 A[i][j][k] 주소 = α + i·upper1·upper2 + j ·upper2 + k
다차원 배열의 표현 • 주소 계산 (n차원 배열): A[upper0][upper1]...[uppern-1] • α = A[0][0]...[0]의 주소 A[i0][0]...[0] 주소 = α + i0upper1upper2...uppern-1 A[i0][i1][0]...[0] 주소= α + i0upper1upper2...uppern-1 + i1upper2upper3...uppern-1 A[i0][i1]...[in-1] 주소 = α + i0upper1upper2...uppern-1 + i1upper2upper3...uppern-1 + i2upper3upper4...uppern-1 ⋮ + in-2uppern-1 + in-1 = α +
문자열 추상 데이타 타입 • 문자열(string): S = s0, s1, …, sn-1의 형태, • si : 문자 집합의 원소 • n = 0 : 공백 또는 널 문자열 • 연산 • 생성, 문자열의 읽기 또는 출력 • 두 문자열 접합(concatenation), 문자열 복사 • 문자열 비교 • 문자열에 일부 문자열 삽입 • 문자열로부터 일부 문자열 삭제 • 문자열에서 특정 패턴 식별
문자열 추상 데이타 타입 structure String objects : 0개 이상의 문자들의 유한 순서 집합 functions : String Null(int m); // 최대 길이가 m인 문자열 (Null로 초기화) int Compare(String s, String t); // if (s와 t가 동등하면) return 0 // else if (s가 t에 선행하면) return -1 else return 1 Boolean IsNull(String s); // if s가 Null 문자열이면 return TRUE // else return FALSE int Length(String s); // s의 문자수를 반환 String Concat(String s, String t); // s뒤에 t를 붙인 문자열을 반환 String Substr(String s, int i, int j); // s에서 i,i+1,...,i+j-1의 위치에 있는 j개의 문자열을 반환 // 이들이 유효한 위치가 아니면 공백 문자열을 반환 int nfind (String s, String pat); // s의 i에서 시작하는 부분문자열이 pat과 일치하는 경우 인덱스 i를 반환 // pat이 공백이거나 s의 부분문자열이 아닌 경우에는 -1을 반환