650 likes | 1.21k Views
자료구조. 제 2 장 배열. 2.1 추상 데이타 타입과 C++ 클래스. 2.1 추상 데이타 타입과 C++ 클래스. ▶ C++ 클래스 개요 - C++ 은 클래스 기법을 사용하여 ADT 를 표현 - C++ 클래스의 구성 (1) 클래스 이름 : ( 예 : Rectangle) (2) 데이타 멤버 : 데이타 ( 예 : x1, y1, h, w) (3) 멤버 함수 : 연산의 집합 ( 예 : GetHeight(), GetWidth()). 2.1 추상 데이타 타입과 C++ 클래스.
E N D
자료구조 제 2 장 배열
2.1 추상 데이타 타입과 C++ 클래스 • ▶ C++ 클래스 개요 • - C++은 클래스 기법을 사용하여 ADT를 표현 • - C++ 클래스의 구성 • (1) 클래스 이름: (예: Rectangle) • (2) 데이타 멤버: 데이타 (예: x1, y1, h, w) • (3) 멤버 함수: 연산의 집합 • (예: GetHeight(), GetWidth())
2.1 추상 데이타 타입과 C++ 클래스 • (4) 프로그램 접근 레벨: 멤버 및 멤버함수 • 공용(public), 보호(protected), 전용(private) • public: 프로그램 어디에서도 접근 • private: 같은 클래스내, friend로 선언된 함수나 클래스 에 의해 접근 • protected: 같은 클래스내, 서브클래스, friend에 • 의해서만 접근
2.1 추상 데이타 타입과 C++ 클래스 • ----------------------------------- • #ifndefRECTANGLE_H • #define RECTANGLE_H • // Rectangle.h헤더 화일속에 • classRectangle { • public: // 다음 멤버들은 public이다. • // 다음 4개의 멤버들은 멤버 함수들이다. • Rectangle(); // 생성자 • ~Rectangle(); // 파괴자 • intGetHeight(); // 사각형의 높이를 제공 • intGetWidth(); // 사각형의 길이를 제공 • private: // 다음 멤버들은 전용이다. • // 다음 4개의 멤버들은 데이타 멤버들이다. • intx1, 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==(constRectangle &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: • NaturalNumberZero(); • // 0을 반환 • Boolean IsZero(); • // *this가 0이면 TRUE, 아니면 FALSE를 반환 • NaturalNumberAdd(NaturalNumbery); • // *this +y와 MAXINT에서 작은 값을 반환 • Boolean Equal(NaturalNumbery); • // *this==y이면 TRUE, 아니면 FALSE를 반환 • NaturalNumberSuccessor(); • // *this가 MAXINT이면 MAXINT, • // 아니면 *this +1을 반환 • NaturalNumberSubstract(NaturalNumbery); • // *this <y이면 0, 아니면 *this-y를 반환 • }; • ----------------------------------- • ADT1.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, RangeListlist, • 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의 끝 • -------------------------------------- • ADT2.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 다항식 추상 데이타 타입 • - 다항식 추상 데이타 타입 • ------------------------------------- • ClassPolynomial { • // 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 정의 끝 • ------------------------------------- • ADT2.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=MaxRowx MaxCol을 • //저장할 수 있는 SparseMatrix를 생성한다. 여기서 최대 • //행의 수 는 max_row 이고 최대 열 수는 max_col이다. • SparseMatrixTranspose(); • // *this의 모든 3원소 쌍의 행과 열의 값을 서로 교환하여 • //얻은 행렬을 반환한다. • SparseMatrixAdd(SparseMatrixb); • // if a(*this)와 b의 차원이 같으면대응 항들 즉, 같은 행과 • //열의 값을 가진 항들을 더해서 만들어진 행렬을 반환 • //하고, else 오류를 발생한다. • SparseMatrixMultiply(SparseMatrixb); • // if a(*this)의 열의 수와 b의 행의 수가 같으면다음 공식 • //에 따라 a와 b를 곱해서 생성된 행렬 d를반환하고 • // 행렬 d : d(i,j)=(a[i][k]·b[k][j]) • // 여기서 d(i,j)는 (i,j)번째 요소 • // else 오류를 발생한다. • }; • ----------------------------------------- • ADT2.3 SparseMatrix추상 데이타 타입
2.4 희소 행렬 • ▶효율적인 희소 행렬 표현 • - 표현 방법 • i. <행, 열, 값> 3원소 쌍(triple) • ii. 행의 수 • iii. 열의 수 • iv. non-zero 원소의 수 • v. 순서 (행우선 또는 열우선) : 행 또는 열의 오름순 • - C++ 표현 • class SparseMatrix; // 전방 선언 • class MatrixTerm { • friend class SparseMatrix • private: • int row, col, value; • }; • - 클래스 SparseMatrix내부 정의 • private: • int Rows, Cols, Terms; • MatrixTermsmArray[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 희소 행렬 • ------------------------------------- • SparseMatrixSparseMatrix::Transpose() { • // a (*this)의 전치 행렬을 반환 • SparseMatrixb; • 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 (intj = 0; j < columns; j++) • for (inti = 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
------------------------------------------ • SparseMatrixSparseMatrix::FastTranspose() { • // a(*this)를 b로 전치하고 O(terms+columns) 연산 시간이 • 걸린다. • int *RowSize= new int[Cols]; • int *RowStart= new int[Cols]; • SparseMatrixb; • 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, intr,intc) • { • // 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 • -> 사전순서(lexicographicorder) • 배열 원소 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 ]의 순차적 표현