610 likes | 1.42k Views
자료구조. 제 2 장 배열. 2.1 추상 데이타 타입과 C++ 클래스. 2.1 추상 데이타 타입과 C++ 클래스. C++ 클래스 개요 C++ 은 클래스 기법을 사용하여 ADT 를 표현 C++ 클래스의 구성 클래스 이름 : ( 예 : Rectangle) 데이타 멤버 : 데이타 ( 예 : x1, y1, h, w) 멤버 함수 : 연산의 집합 ( 예 : GetHeight(), GetWidth()) 프로그램 접근 레벨 : 멤버 및 멤버함수
E N D
자료구조 제 2 장 배열
2.1 추상 데이타 타입과 C++ 클래스 • C++ 클래스 개요 • C++은 클래스 기법을 사용하여 ADT를 표현 • C++ 클래스의 구성 • 클래스 이름: (예: Rectangle) • 데이타 멤버: 데이타 (예: x1, y1, h, w) • 멤버 함수: 연산의 집합 (예: GetHeight(), GetWidth()) • 프로그램 접근 레벨: 멤버 및 멤버함수 • 공용(public): 프로그램 어디에서도 접근 • 보호(protected): 같은 클래스내, 서브클래스, friend에 의해서만 접근 • 전용(private): 같은 클래스내, friend로 선언된 함수나 클래스에 의해 접근
2.1 추상 데이타 타입과 C++ 클래스 ----------------------------------- #ifndef RECTANGLE_H #define RECTANGLE_H // Rectangle.h 헤더 화일속에 class Rectangle { public: // 다음 멤버들은 public이다. // 다음 4개의 멤버들은 멤버 함수들이다. Rectangle(); // 생성자 ~Rectangle(); // 파괴자 int GetHeight(); // 사각형의 높이를 제공 int GetWidth(); // 사각형의 길이를 제공 private: // 다음 멤버들은 전용이다. // 다음 4개의 멤버들은 데이타 멤버들이다. int x1, y1, h, w; // (x1, y1)은 사각형의 왼쪽 하단 모서리 좌표이다. // w는 사각형의 길이이고, h는 높이이다. }; #endif ----------------------------------- 프로그램 2.1 Rectangle 클래스의 C++ 정의
2.1 추상 데이타 타입과 C++ 클래스 • C++에서의 데이타 추상화와 캡슐화 • 데이타 멤버: • private(전용) 또는 protected(보호)로 선언 -> 데이타 캡슐화 • 멤버 함수: • 외부에서 기동될 것: public으로 선언 • 나머지: private나 protected로 선언 (다른 멤버 함수에서 호출) • 클래스 연산의 명세와 구현의 분리 • 명세 부분에서 함수 프로토타입(함수 이름, • 인자 타입, 결과 타입) 정의 -> 헤더 화일에 저장 (예) Rectangle.h 화일(프로그램 2.1의 내용) • 함수의 기능 기술: 주석(comment)을 이용 • 함수의 구현: 같은 이름의 소스 화일에 저장 (예) Rectangle.C 화일(프로그램 2.2의 내용) • note: 멤버 함수의 구현을 클래스 정의내에 함시킬 수도 있음 -> 인라인(inline) 함수로 취급 • int GetHeight() { return h };
2.1 추상 데이타 타입과 C++ 클래스 -------------------------------------- // 소스 화일 Rectangle.C 속에 #include "Rectangle.h" // 접두어로 사용되는 “Rectangle::"은 // GetHeight()와 GetWidth()가 // 클래스 Rectangle의 멤버 함수라는 것을 명시 한다. // 이 접두어가 필요한 이유는 멤버 함수들이 // 클래스 정의 밖에서 구현되기 때문이다 int Rectangle::GetHeight() { return h }; int Rectangle::GetWidth() { return w}; -------------------------------------- 프로그램 2.2 Rectangle에 대한 연산의 구현
2.1 추상 데이타 타입과 C++ 클래스 • 클래스 객체의 선언과 멤버 함수의 기동 - 클래스 객체: 변수와 똑같이 선언되고 생성됨 - 한 객체의 멤버: 점(.), 화살표(->)로 접근 ----------------------------------- // 소스 화일 main.C 속에 #include <iostream.h> #include "Rectangle.h" main(){ Rectangle r, s; // r과 s는 클래스 Rectangle의 객체들이다. Rectangle *t = &s; // t는 클래스 객체 s에 대한 포인터이다. ... (r, s의 값을 입력함) // 클래스 객체의 멤버를 직접 접근하기 위해서는 // 점(.)을 사용한다. // 포인터를 통해 클래스 객체의 멤버를 접근하기 // 위해서는 -> 를 사용한다. if (r.GetHeight()*r.GetWidth() > t->GetHeight() * t->GetWidth()) cout << " r "; else cout << " s "; cout << "has the greater area" << endl; } ----------------------------------- 프로그램 2.3: Rectangle 객체의 선언과 멤버 함수의 기동을 예시하는 C++ 코드 부분
2.1 추상 데이타 타입과 C++ 클래스 • 생성자(constructor)와 파괴자(destructor) - 생성자와 파괴자: 클래스의 특수한 멤버 함수 - 생성자: . 한 객체의 데이타 멤버들을 초기화 . 클래스 객체가 만들어질 때 자동적으로 실행 . 해당 클래스의 공용 멤버 함수로 선언 . 생성자 이름은 클래스의 이름과 동일 ----------------------------------- Rectangle::Rectangle(int x, int y, int height, int width) { x1=x; y1=y; h=height; w=width; } ----------------------------------- 프로그램 2.4: Rectangle을 위한 생성자 정의 - 생성자를 이용한 Rectangle 객체 초기화 Rectangle r(1,3,6,6); Rectangle *s = new Rectangle(0,0,3,4); Rectangle t; // 컴파일에러! -> 묵시생성자 필요
2.1 추상 데이타 타입과 C++ 클래스 ----------------------------------- Rectangle::Rectangle (int x=0, int y=0, // default value int height=0, int width=0) : x1 (x), y1(y), h(height), w(width) {} ----------------------------------- 프로그램 2.5: Rectangle 클래스에 대한 생성자 정의 - 프로그램 2.5의 생성자 . 데이타 멤버들이 멤버 초기화 리스트에 의해 초기화 : 보다 효율적 - 파괴자: . 객체가 없어지기 직전 데이타 멤버들을 삭제 . 클래스 객체가 범위를 벗어나거나 삭제될 때 자동적으로 기동 . 해당 클래스의 공용 멤버로 선언 . 이름: ~클래스 이름 . Rectangle r(1,3,6,6); - 삭제시 파괴자가 기동됨 Rectangle *s = new Rectangle(0,0,3,4); - 삭제시 파괴자가 기동되지 않음 . 삭제되는 객체의 한 데이타 멤버가 다른 객체에 대한 포인터인 경우 -> 지시되는 객체를 삭제하기 위해서는 명시적으로 delete를 사용해야 함
2.1 추상 데이타 타입과 C++ 클래스 • 연산자 다중화(operator overloading) • 하나의 연산자를 여러 의미로 사용 가능 (예) 연산자 “==“ • 두 실수(float) 데이타의 동등성 검사 • 두 정수(int) 데이타의 동등성 검사 • 두 사각형(Rectangle) 객체 동등성 비교 같은 객체 위치와 크기가 같은 사각형
2.1 추상 데이타 타입과 C++ 클래스 • 클래스 Rectangle을 위한 연산자 “==“의 다중화 ----------------------------------- int Rectangle::operator==(const Rectangle &s ) { ① if (this == &s) return 1; ② if ((x1 == s.x1) && (y1 == s.y1) && (h == s.h) && (w == s.w)) return 1; ③ else return 0; } ----------------------------------- 프로그램 2.6: 클래스 Rectangle을 위한 연산자 “==“의 다중화 . this: 멤버 함수를 기동시킨 특정 클래스 객체에 대한 포인터(self) . *this: 클래스 객체 자신 ① oid 비교: “this == &s" -> 두 객체가 같으면 결과는 1 (참) ② 값 비교: 데이타 멤버를 개별적으로 비교 -> 두 객체가 같으면 1 (참) ③ 아니면 0 (거짓)
2.1 추상 데이타 타입과 C++ 클래스 • Rectangle 객체를 cout으로 출력하기 위한 연산자 “<<“의 다중화 ----------------------------------- ostream& operator <<(ostream& os, Rectangle& r) { os<<"Position is: "<<r.x1 << " "; os<< r.y1 <endl; os<<"Height is: "<<r.h<<endl; os<<"Width is: “<<r.w<<endl; return os; } ----------------------------------- 프로그램 2.7: 클래스 Rectangle을 위한 연산자 “<<“의 다중화 . 연산자 << : Rectangle의 friend . cout << r; 의 출력 예 Position is: 1 3 Height is: 6 Width is: 6
2.1 추상 데이타 타입과 C++ 클래스 • 기타 내용 • C++의 struct • 묵시적 접근 레벨 = public (cf. class에서는 private) • C의 struct에 대한 일반화 (멤버함수도 정의 가능) • union • 제일 큰 멤버에 맞는 저장장소 할당 • 보다 효율적으로 기억장소 사용 • static(정적) 클래스 데이타 멤버 • 클래스에 대한 전역 변수: 오직 하나의 사본 모든 클래스 객체는 이를 공유 • 정적 데이타 멤버는 클래스 외부에서 정의 • ADT와 C++ 클래스 • ADT와의 차이: C++의 일부 연산자들은 해당 클래스의 멤버 함수로 존재하지 않음 • -> 보통의 C++ 함수로 외부에서 선언 (해당 클래스에서 friend 선언)
2.1 추상 데이타 타입과 C++ 클래스 • 예제 2.1 [추상 데이타 타입 NaturalNumber] ----------------------------------- class NaturalNumber{ // 컴퓨터에서 0부터 최대 정수(MAXINT)까지의 // 순서화된 정수의 부분 public: NaturalNumber Zero(); // 0을 반환 Boolean IsZero(); // *this가 0이면 TRUE, 아니면 FALSE를 반환 NaturalNumber Add(NaturalNumber y); // *this +y와 MAXINT에서 작은 값을 반환 Boolean Equal(NaturalNumber y); // *this==y이면 TRUE, 아니면 FALSE를 반환 NaturalNumber Successor(); // *this가 MAXINT이면 MAXINT, // 아니면 *this +1을 반환 NaturalNumber Substract(NaturalNumber y); // *this <y이면 0, 아니면 *this-y를 반환 }; ----------------------------------- ADT 1.2: NaturalNumber 추상 데이타 타입
2.2 추상 데이타 타입으로서의 배열 • 일련의 연속적인 메모리 위치: 구현 중심 • 배열 ADT • <인덱스,값>: <index,value>의 쌍 집합 index -> value : 대응, 사상(mapping) ↑ same data type • index: finite ordered set of j-dimensions (j≥1) • C++ 배열 • 인덱스 집합이 0부터 시작 • 배열의 인덱스 범위를 확인하지 않음 (예) float example[n]; i번째 원소값 -> example[i], *(example+i) • 함수 • GeneralArray(j-dimension, j-range list, initial value) • Retrieve(index) • Store(index, item)
------------------------------------- Class GeneralArray { // objects: IndexSet의 index 각 값에 대하여 float 타입의 // value가 존재하는 <index, value>쌍의 집합. // IndexSet는 일차원 또는 다차원의 유한 순서 집합 // 일차원의 경우 0,···,n-1와 이차원 배열 (0,0), (0,1), // (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2), 등이다. public: GeneralArray (int j, RangeList list, float initValue = defaultValue); // 생성자 GeneralArray는 j 차원의 실수 배열을 생성 // k번째 인덱스의 범위는 list의 k번째 원소에 의해 정해짐 // 인덱스 집합의 각 인덱스 i에 대해 <i, initValue>를 // 배열에 삽입 float Retrieve(index i); // if (i가 배열의 index 집합에 있는 경우) // return 배열 내 i와 연관된 실수값 // else 오류를 출력 void Store(index i, float x); // if (i가 배열의 index 집합에 있는 경우) // 배열 내에 있는 <i, y>의 쌍을 삭제하고 // 새로운 쌍인 <i, x>를 삽입 // else 오류를 출력 }; // GeneralArray의 끝 -------------------------------------- ADT 2.1 추상 데이타 타입 GeneralArray
2.3 다항식 추상 데이타 타입 • ordered list, linear list : 순서 리스트, 선형 리스트 • examples •한 주일의 요일: (일,월,화,수, .., 토) •카드 한 벌의 값: (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
2.3 다항식 추상 데이타 타입 • 순서 리스트의 일반적인 구현 •배열을 이용 •인덱스 i → ai, 0≤ i<n ... i, i+1, ... ... ai, ai+1, ... •순차적 사상(sequential mapping) •문제점: 삽입, 삭제시 오버헤드 • 다항식 (polynomial) - A(x) = ∑aixei : 항의 합 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))
2.3 다항식 추상 데이타 타입 • 다항식 추상 데이타 타입 ------------------------------------- Class Polynomial { // objects : P(x)=a0 xe0 + ··· +an xen ; <ei , ai>의 // 순서쌍으로 된 집합. // 여기서 ai∈Coefficient 이고, ei ∈ Exponent // Exponent는 ≥0인 정수로 가정 public: Polynomial(); // 다항식 p(x)=0를 반환 int operator!(); // *this가 0의 다항식이면 1을 아니면 0을 반환 Coefficient Coef(Exponent e); // *this에 있는 e의 계수를 반환 Exponent LeadExp(); // *this에서 가장 큰 지수를 반환 Polynomial Add(Polynomial poly); // *this와 poly의 합을 반환 Polynomial Mult(Polynomial poly); // *this와 poly의 곱을 반환 float Eval(float f); // *this에 f를 대입한 값을 계산하여 그 결과를 반환 } ; // polynomial 정의 끝 ------------------------------------- ADT 2.2 polynomial 추상 데이타 타입
2.3 다항식 추상 데이타 타입 • 다항식 표현 -가정: 서로 다른 지수들은 내림차순으로 정돈 [표현 1] 모든 차수에 대한 계수만 저장, 계수 배열 크기는 최대로 - Polynomial의 전용 데이타 멤버 선언 private: int degree; //degree ≤ MaxDegree float coef[MaxDegree + 1]; - a가 Polynomial 클래스 객체, n ≤ MaxDegree a.degree = n a.coef[i] = an-i, 0 ≤ i ≤ n - a.coef[i]는 xn-i의 계수, 각 계수는 지수의 내림차순으로 저장 - 장점: 다항식에 대한 연산 알고리즘이 간단 - 단점: 저장 공간 낭비 (a.degree가 MaxDegree보다 아주 작을 때)
2.3 다항식 추상 데이타 타입 • [표현 2] 모든 차수에 대한 계수만 저장, 계수 배열 크기는 실제 차수 크기로 - Polynomial의 전용 데이타 멤버 선언 private: int degree; float *coef; - 생성자를 Polynomial에 추가 Polynomial::Polynomial(int d) { degree=d; coef=new float[degree+1]; } - 단점: 희소 다항식에서 기억 공간 낭비 (예) 다항식 x1000+1 -> coef에서 999개의 항목은 0
2.3 다항식 추상 데이타 타입 • [표현 3]0이 아닌 계수-지수 쌍만 저장 - 다항식 A(x) = bm xem + bm-1 xem-1 + ··· + b0 xe0 bi : A의 0이 아닌 계수 ei : 지수, 내림차순. em > em-1 > ··· > e0 ≥ 0. A의 모든 계수가 0이 아니면: m=n, ei=i, 0 ≤ i ≤ n에 대해서 bi = ai an만이 0이 아니면, m=0,b0 = an 이고e0 = n - 모든 다항식은 배열 termArray를 이용해 표현 . termArray의 각 원소는 term 타입 . Polynomial의 정적 클래스 데이터 멤버로 선언 class Polynomial; class term { friend Polynomial; private: float coef; // 계수 int exp; // 지수 }; - Polynomial의 전용 데이타 멤버 선언 private: static term termArray[MaxTerms]; static int free; int Start, Finish;
2.3 다항식 추상 데이타 타입 • 클래스 정의 외부의 정적 클래스 멤버 정의 term Polynomial::termArray[MaxTerms]; int Polynomial::free = 0; //termArray에서 다음 자유공간의 위치 - 두개의 다항식 A(x)=2x1000+1, B(x)=x4+10x3+3x2+1 표현 그림 2.1 두 다항식의 배열 표현 - n개의 0이 아닌 항을 가진 다항식 A는 A.Finish = A.Start+n-1의 식을 만족하는 A.Start와 A.Finish를 가짐 -> A가 0이 아닌 항이 없을 경우 A.Finish = A.Start-1 - termArray에 저장될 수 있는 다항식수는 MaxTerms - 장점: 많은 항이 0인 경우 우수 - 단점: 모든 항이 0이 아닌 경우 표현 2보다 두배의 저장 장소 사용
2.3 다항식 추상 데이타 타입 • 다항식 덧셈 - C=A+B를 구하는 C++ 함수 함수 Add(프로그램 2.8): A(x)와 B(x)를 항별로 더하여 C(x)를 만드는 함수 - 다항식은 0이 아닌 항만 저장
2.3 다항식 추상 데이타 타입 Polynomial Polynomial::Add(Polynomial B){ // A(x)(*this의 값)와 B(x)를 더한 결과를 반환한다. Polynomial C; int a = Start; int b = B.Start; C.Start = free; float c; while ((a<=Finish) && (b<=B.Finish)) witch(compare(termArray[a].exp,termArray[b].exp)){ case '=': c = termArray[a].coef + termArray[b].coef; if (c) NewTerm(c, termArray[a].exp); a++; b++; break; case '<': NewTerm(termArray[b].coef, termArray[b].exp); b++; break; case '>': NewTerm(termArray[a].coef, termArray[a].exp); a++; } for (; a<=Finish; a++) // A(x)의 나머지 항들을 추가한다. NewTerm(termArray[a].coef, termArray[a].exp); for (; b<=B.Finish; b++) // B(x)의 나머지 항들을 추가한다. Newterm(termArray[b].coef, termArray[b].exp); C.Finish = free - 1; return C; }// Add의 끝 ------------------------------------ 프로그램 2.8: 두 다항식 덧셈
2.3 다항식 추상 데이타 타입 ------------------------------------ void Polynomial::NewTerm(float c, int e) { // C(x)에 새로운 항을 첨가 if (free>=MaxTerms) { cout << "Too many terms in polynomials" << endl; return; } termArray[free].coef = c; termArray[free].exp = e; free++; }// NewTerm의 끝 ------------------------------------- 프로그램 2.9: 새로운 항의 추가 • 알고리즘 Add 의 분석 : - m과 n: A와 B에서의 0이 아닌 항의 수 - 복잡도: O(n+m) • 다항식을 배열로 표현하는 경우의 문제점 - 공간의 비효율적 사용 - 실제 필요한 만큼의 저장공간 사용 → 동적 메모리 할당 (4장 리스트)
2.4 희소 행렬 • 개요 A[m][n] - m × n 행렬 A m : 행의 수 n : 열의 수 m × n : 원소의 수 - m = n : 정방 행렬 - 특정 원소 A[i][j] - 희소 행렬(sparse matrix) 0이 아닌 원소수 / 전체 원소수 << small -> 0이 아닌 원소만 저장할 필요 있음 - 행렬에 대한 연산 creation addition multiplication transpose
2.4 희소 행렬 • 밀집 행렬과 희소 행렬 예 그림 2.2: 두 행렬
희소 행렬 ADT ------------------------------------- Class SparseMatrix{ // objects: 3원소쌍 <행, 열, 값>의 집합이다. 여기서, 행과 // 열은 정수이고 이 조합은 유일하며, 값 또한 정수이다. public: SparseMatrix(int MaxRow, it MaxCol); // 이 생성자 함수는MaxItems=MaxRow x MaxCol을 // 저장할 수 있는 SparseMatrix를 생성한다. 여기서 최대 // 행의 수 는 max_row 이고 최대 열 수는 max_col이다. SparseMatrix Transpose(); // *this의 모든 3원소 쌍의 행과 열의 값을 서로 교환하여 // 얻은 행렬을 반환한다. SparseMatrix Add(SparseMatrix b); // if a(*this)와 b의 차원이 같으면 대응 항들 즉, 같은 행과 // 열의 값을 가진 항들을 더해서 만들어진 행렬을 반환 // 하고, else 오류를 발생한다. SparseMatrix Multiply(SparseMatrix b); // if a(*this)의 열의 수와 b의 행의 수가 같으면 다음 공식 // 에 따라 a와 b를 곱해서 생성된 행렬 d를 반환하고 // 행렬 d : d(i,j)= (a[i][k]·b[k][j]) //여기서 d(i,j)는 (i,j)번째 요소 // else 오류를 발생한다. }; ----------------------------------------- ADT 2.3 SparseMatrix 추상 데이타 타입
2.4 희소 행렬 • 효율적인 희소 행렬 표현 • 표현 방법 • <행, 열, 값> 3원소 쌍(triple) • 행의 수 • 열의 수 • non-zero 원소의 수 • 순서 (행우선 또는 열우선) : 행 또는 열의 오름순 • C++ 표현 class SparseMatrix; // 전방 선언 class MatrixTerm { friend class SparseMatrix private: int row, col, value; }; • 클래스 SparseMatrix 내부 정의 private: int Rows, Cols, Terms; MatrixTerm smArray[MaxTerms]; . Rows: 행의 수 . Cols: 열의 수 . Terms: 0이 아닌 항의 총 수
2.4 희소 행렬 • 그림 2.2(b)의 행렬의 smArray 표현 ------------------------------------- 행 열 값 smArray[0] 0 0 15 [1] 0 3 22 [2] 0 5 -15 [3] 1 1 11 [4] 1 2 3 [5] 2 3 6 [6] 4 0 91 [7] 5 2 28 (a) 행 열 값 smArray[0] 0 0 15 [1] 0 4 91 [2] 1 1 11 [3] 2 1 3 [4] 2 5 28 [5] 3 0 22 [6] 3 2 -6 [7] 5 0 -15 (b) ------------------------------------- 그림 2.3 3원소 쌍으로 저장된 희소 행렬과 전치행렬.
2.4 희소 행렬 • 행렬의 전치 - 원래의 행렬 각 행 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) . 문제점: 올바른 순서 유지위해 기존원소 이동 알고리즘 for (열 j에 있는 모든 원소에 대해) 원소<i, j, 값>을 원소<j, i, 값>에 저장
2.4 희소 행렬 ------------------------------------- SparseMatrix SparseMatrix::Transpose() { // a (*this)의 전치 행렬을 반환 SparseMatrix b; b.Rows = Cols; // b의 행 수 = a의 열 수 b.Cols = Rows; // b의 열 수 = a의 행 수 b.Terms = Terms; // b의 원소수 = a의 원소수 if (Terms > 0) { // 0이 아닌 행렬 int CurrentB = 0; for (int c = 0; c < Cols; c++) // 열별로 전치 for (int i = 0; i < Terms; i++) // 열 c로부터 원소를 찾는다. if (smArray[i].col ==c) { b.smArray[CurrentB].row = c; b.smArray[CurrentB].col = smArray[i].row; b.smArray[CurrentB].value = smArray[i].value; CurrentB++; } } // if (Terms >0)의 끝 return b; } // Transpose의 끝 ------------------------------------- 프로그램 2.10: 행렬의 전치
2.4 희소 행렬 - 복잡도: O(terms·columns), O(rows·columns2) -> 공간 절약을 위해 시간을 희생한 결과 [note] 단순 2차원 배열 표현시는 O(rows·columns) for (int j = 0; j < columns; j++) for (int i = 0; i < rows; i++) B[j][i] = A[i][j];
2.4 희소 행렬 - 메모리를 조금 더 사용한 개선 알고리즘: FastTranspose . 먼저 행렬 a의 각 열에 대한 원소 수를 구함 -> 즉,전치행렬 b의 각 행의 원소 수를 결정 . 이 정보에서 전치행렬 b의 각행의 시작위치 구함 . 원래 행렬 a에 있는 원소를 하나씩 전치 행렬 b의 올바른 위치로 옮김 ROW_SIZE ROW_START [0] 2 0 [1] 1 2 [2] 2 3 [3] 2 5 [4] 0 7 [5] 1 7 ↑ ↑ # of terms starting position in b's row (a's col) of b's row
------------------------------------------ SparseMatrix SparseMatrix::FastTranspose() { // a(*this)를 b로 전치하고 O(terms+columns) 연산 시간이 걸린다. int *RowSize = new int[Cols]; int *RowStart = new int[Cols]; SparseMatrix b; b.Rows = Cols; b.cols = Rows; b.Terms = Terms; if (Terms>0) { // 0이 아닌 행렬 // RowSize[i] = b의 행 i에 있는 항의 수를 계산 for (int i = 0; i<Cols; i++) RowSize[i] = 0; // 초기화 for (i = 0; i<Terms; i++) RowSize[smArray[i].col]++; RowStart[0] = 0; // RowStart[i] = b의 행 i의 시작점 for (i =1; i<Cols; i++) RowStart[i] = RowStart[i-1] + RowSize[i-1]; for (i = 0; i<Terms; i++) { // a를 b로 전치 int j = RowStart[smArray[i].col; b.smArray[j].row = smArray[i].col; b.smArray[j].col = smArray[i].row; b.smArray[j].value = smArray[i].value; RowStart[smArray[i].col]++; } } delete [] RowSize; delete [] RowStart; return b; } ------------------------------------- 프로그램 2.11 희소 행렬의 빠른 전치 - 복잡도: O(columns+terms)
2.4 희소 행렬 • 행렬 곱셈 - Result ← A × B Resultm×p ← Am×n × Bn×p resultij = , 0 I < m, 0 j < p ------------------------------------- ------------------------------------- - 순서 리스트로 표현된 두 희소 행렬의 곱셈 . Result의 원소를 행별로 계산 -> 이전 계산 원소를 이동하지 않고 저장 . 행렬 A에서 한 행을 선택하고 j = 0,1,..., B.Col-1에 대해 B의 j열에 있는 모든 원소를 찾음 . B를 전치 . A의 i행과 B의 j열의 원소들이 정해지면 다항식 덧셈과 유사한 합병연산 수행
2.4 희소 행렬 int SparseMatrix::StoreSum (int sum, int& LastInResult, int r, int c) { // sum≠0이면 행과 열의 위치와 함께 // result의 (LastInResult+1)번 째 항목에 저장된다. // LastInResult는 증가한다. // 새로운 항을 위한 가용 메모리가 없으면 1이, // 그렇지 않으면 0을 호출함수에 반환한다. if (sum != 0) { if (LastInResult < MaxTerms -1) { LastInResult++; smArray[LastInResult].row = r; smArray[LastInResult].col = c; smArray[LastInResult].value = sum; return 0; } else { cerr << "Number of terms in product exceeds MaxTerms" << endl; return 1; } } else return 0; } ------------------------------------ 프로그램 2.12: 행렬 항의 저장
2.4 희소 행렬 ---------------------------------------- SparseMatrix SparseMatrix::Multiply(SparseMatrix b) // 두 개의 희소 행렬 A(*this)와 B를 곱하여 Result를 생성 { if (Cols != b.Rows) { cout << "Incompatible matrices" << endl; return EmptyMatrix(); } if ((Terms == MaxTerms) || (b.Terms == MaxTerms)) { cout << "One additional space in a or b needed" << endl; return EmptyMatrix(); } SparseMatrix bXpose = b.FastTranspose(); SparseMatrix result; int currRowIndex = 0, LastInResult = -1, currRowBegin = 0, currRowA = smArray[0].row; // 경계 조건 설정 smArray[Terms].row = Rows; bXpose.smArray[b.Terms].row = b.Cols; bXpose.smArray[b.Terms].col = -1; int sum = 0;
while(currRowIndex<Terms) {// result의 행 currRowA를 생성 int currColB = bXpose.smArray[0].row; int currColIndex = 0; while (currColIndex <= b.Terms) { // a의 행 currRowA에 b의 열 currColB를 곱함 if (smArray[currRowIndex].row != currRowA) { // currRowA 행의 끝 if (result.StoreSum(sum, LastInResult, currRowA, currColB)) return EmptyMatrix(); // 에러 else sum = 0; // sum을 재설정 currRowIndex = currRowBegin; // 다음 열로 넘어감 while(bXpose.smArray[currColIndex].row == currColB) currColIndex++; currColB = bXpose.smArray[currColIndex].row; } else if (bXpose.smArray[currColIndex].row != currColB) { // b의 열 currColB의 끝 if (result.StoreSum(sum, LastInResult, currRowA, currColB)) return EmptyMatrix(); //에러 else sum = 0; // sum을 재설정 //currRowA 행을 다음 열과 곱하도록 설정 currRowIndex = currRowBegin; currColB = bXpose.smArray[currColIndex].row; }
else switch (compare(smArray[currRowIndex].col, bXpose.smArray[currColIndex].col)) { case '<' : //행의 다음 항으로 감 currRowIndex++; break; case '=' : // sum에 더함 sum += smArray[currRowIndex].value * bXpose.smArray[currColIndex].value; currRowIndex++; currColIndex++; break; case '>' : // 열 currColB의 다음 항으로 감 currColIndex++; } // switch의 끝 } // while(currColIndex <= b.Terms)의 끝 while (smArray[currRowIndex].row == currRowA) // 다음 행으로 감 currRowIndex++; currRowBegin = currRowIndex; currRowA = smArray[currRowIndex].row; } // while(currRowIndex < Terms)의 끝 result.Rows = Rows; result.Cols = b.Cols; result.Terms = LastInResult +1; return result; } // Multiply의 끝 --------------------------------------- 프로그램 2.13: 희소 행렬의 곱셈
2.4 희소 행렬 • 복잡도: O(∑r(B.Cols • tr+B.Terms)) = O(B.Cols • A.Terms +A.Rows • B.Terms) [note] 통상적인 곱셈 알고리즘 O(A.Rows • A.Cols • B.Cols) 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; }
2.5 배열의 표현 • 다차원 배열의 표현 - addressing formula: A[p1..q1][p2..q2], ··· ,[pn .. qn ] A의 총 원소수: - 특정 원소의 지정: A[i1 ] [i2 ] ,...,[in ] - 표현 순서: 행우선, 열우선 . 행우선(row major order) (예) A[4..5][2..4][1..2][3..4] 총 원소수: 2*3*2*2=24 저장 순서: A[4][2][1][3], A[4][2][1][4], A[4][2][2][3], A[4][2][2][4] A[4][3][1][3], A[4][3][1][4], A[4][3][2][3], A[4][3][2][4] · · A[5][4][1][3], A[5][4][1][4], A[5][4][2][3], A[5][4][2][4] 즉, 4213, 4214, ···, 5423, 5424 -> 사전순서(lexicographic order) 배열원소 A[i_1] [i_2] ,...,[i_n]의 1차원 배열 위치로의 변환 (예) A[4][2][1][3] -> 위치 0 A[4][2][1][4] -> 위치 1 A[5][4][2][4] -> 위치 23
2.5 배열의 표현 • 문제의 단순화를 위한 가정: 차원 i의 인덱스: 0에서 ui –1 (pi = 0, qi = ui– 1) • 1차원 배열 A[u1] . α : A[0]의 주소 . 임의의 원소 A[i]의 주소 : α + i 그림 2.5 A[u1]의 순차적인 표현
2.5 배열의 표현 • 2차원 배열 A[u1 ][u2 ] . α : A[0][0]의 주소 . A[i][0]의 주소: α +i * u2 . A[i][j]의 주소: α + i * u2 + j 그림 2.6: A[u1 ][u2 ]의 순차적 표현
2.5 배열의 표현 • 3차원 배열 A[u1 ][u2 ][u3 ] . α : A[0][0][0]의 주소 . A[i][0][0]의 주소: α +iu2u3 . A[i][j][k]의 주소: α +iu2 u3 + ju3 + k ---------------------------------------- (a) 3-dimensional array A[u1][u2][u3] regarded as u1 2-dimensional array (b) Sequential row major representation of a 3-demensional array. Each 2-dimensional array is represented as in Figure 2.6 ---------------------------------------- 그림 2.7 A[u1 ][u2 ][u3 ]의 순차적 표현