6. Cấu trúc của luận văn
1.7. Bảo toàn 3NF bảo toàn phụ thuộc hàm
Algorithm 3NF
Function: Chuẩn hóa 3NF không tổn thất và bảo toàn PTH. Input: LĐQH p = (U,F)
Output: Các LĐQH 3NF (U1,K1),(U2,K2),...,(Us,Ks) thoả
RREL(U): R[U1]R[U2]...R[Us] = R K1, K2,..., Ks - khoá của các lược đồ tương ứng
F {F+[Ui] | i = 1,2,...,s} Method
1. Tìm một phủ tối thiểu của F:
G = {K1A1, K2A2,..., KmAm}
2. Ghép các PTH có cùng vế trái trong G để thu được phủ
G = {K1X1, K2X2,..., KsXs}. 3. /* Xét phép tách = (K1X1,...,KsXs).
Nếu chứa một siêu khóa nào đó của p
thì return {(K1X1,K1),...,(KsXs,Ks)}
nếu không
return {(K1X1,K1),...,(KsXs,Ks),(K,K)}
với K là một khóa của p. */
construct = (K1X1,K2X2,...,KsXs); for each component V = KiXi in do if V+ = U then // V = KiXi là siêu khóa return {(K1X1,K1),...,(KsXs,Ks)}; endif; endfor; K = Key(U,F); return {(K1X1,K1),...,(KsXs,Ks),(K,K)}; End 3NF
CHƯƠNG 2 - CÁC THUẬT TOÁN VỀ CHUẨN HÓA DỮ LIỆU QUAN HỆ
Trong chương này tôi sẽ trình bày các thuật toán của đại số quan hệ, thuật toán quản lý phụ thuộc hàm, các thuật toán tìm bao đóng và các thuật toán tìm khóa gồm các thuật toán sau:
2.1. Các thuật toán của đại số quan hệ
Các thuật toán của đại số quan hệ gồm:
2.1.1 Phép chọn
Algorithm Selection
Function: Thực hiện phép Chọn
Format: P = R(e) Input: - Quan hệ R(U)
- Biểu thức chọn e trên U.
Output: - Quan hệ P(U) = R(e) = {tR | Sat(t,e)} Method
// Tạo lập quan hệ P tương thích với R
Create(P,Attr(R)); for each tuple t in R with Sat(t,e) do
add t to P ; endfor;
return P; end Selection.
Độ phức tạp tính toán: Thuật toán duyệt r bộ, phép kiểm tra Sat(t, e) kiểm tra trên n thuộc tính, tổng cộng cần O(n.r) phép kiểm tra. [1,2]
2.1.2 Phép chiếu
Algorithm Projection
Function: Thực hiện phép Chiếu
Format: P = R[X]
Input: - Quan hệ R(U)
Output: - Quan hệ R[X]={t.X | tR} Method
Create(P,X);
for each tuple t in R with t.X not_in P do add t.X to P ; endfor; return P; end Projection.
Độ phức tạp tính toán: Giả sử mỗi lần thuật toán chỉ nạp được 1 bộ vào quan hệ kết quả P. Để kiểm tra bộ t.X có trong P ta cần tối đa r phép so sánh theo n thuộc tính. Vậy độ phức tạp tính toán là O(n.r2). [1,2]
2.1.3 Kết nối tự nhiên
Algorithm Join
Function: Thực hiện phép Kết nối tự nhiên hai quan hệ.
Format: P = RS
Input: - Quan hệ R(U)
- Quan hệ S(V)
Output: - Quan hệ RS={uv | uR,vS,u.M = v.M, M = U
v} Method
X = Attr(R) Attr(S); M = Attr(R) Attr(S); Create(P,X);
for each tuple u in R do
for each tuple v in S with (u.M = v.M) do add uv to P ; endfor; endfor; return P; end Join.
Độ phức tạp tính toán: Giả sử hai quan hệ R và S có k thuộc tính chung, khi đó độ phức tạp tính toán sẽ là O(r.s.k) thao tác trên bộ. [1,2]
2.1.4 Phép hợp
Algorithm Union
Function: Thực hiện phép Hợp hai quan hệ
Format: P = R+S Input: - Quan hệ R(U)
- Quan hệ S(U)
Output: - Quan hệ R+S = {t | tR tS} Method
P := R;
for each tuple v in S with (v not_in R) do add v to P ; endfor;
return P; end Union.
Độ phức tạp tính toán: Phép kiểm tra v thuộc R đòi hỏi r lần duyệt và so sánh trên n thuộc tính. Vậy độ phức tạp tính toán sẽ là O(s.r.n) thao tác trên bộ. [1,2]
2.1.5 Phép giao
Algorithm Intersection
Function: Thực hiện phép giao hai quan hệ
Format: P = R & S Input: - Quan hệ R(U)
- Quan hệ S(U)
Output: - Quan hệ R & S = { t | t R t S } Method
Create(P,Attr(R)); for each tuple u in R with (u in S) do add u to P; endfor; return P; end Intersection. Độ phức tạp tính toán: O(s.r.n). [1,2]
2.1.6 Phép trừ
Algorithm Substraction
Function: Thực hiện phép Trừ hai quan hệ
Format: P = R-S Input: - Quan hệ R(U)
- Quan hệ S(U)
Output: - Quan hệ R - S = {t | t R t S} Method
Create(P,Attr(R)); for each tuple u in R
with (u not_in S) do add u to P; endfor; return P; end Substraction. Độ phức tạp tính toán: O(s.r.n). [1,2] 2.1.7 Phép chia Algorithm Division
Function: Thực hiện phép Chia hai quan hệ
Format: P = R:S Input: - Quan hệ R(U)
- Quan hệ S(V)
Output: Quan hệ R:S = {t.M | tR,(t.M)S R, M = U-V}
Method
M = Attr(R)-Attr(S);
c := Card(S);//số bộ của S
Create(P,M);
for each tuple t in R with (t.M not_in P) do
d := 0;//khởitạo biến đếm
for each tuple v in S if (t.M)v in R then d := d + 1 else breakfor; endif; endfor; if d = c then add t.M to P;
endif; endfor; return P; end Division.
Độ phức tạp tính toán: Thuật toán duyệt r bộ của quan hệ R. Phép kiểm tra t.M thuộc P đòi hỏi r lần duyệt và so sánh trên n thuộc tính. Tiếp đến là duyệt s lần các bộ v của quan hệ S và kiểm tra (t.M)*v có trong R. Vậy độ phức tạp tính toán sẽ là O(r2.s.n) thao tác trên bộ. [1,2]
2.2. Các thuật toán quản lý tập phụ thuộc hàm.
Các thuật toán quản lý phụ thuộc hàm gồm:
2.2.1 Thuật toán tìm phủ thu gọn tự nhiên của tập PTH F.
Định nghĩa: Cho hai tập PTH F và G trên cùng một tập thuộc tính U. G là phủ
thu gọn tựnhiên của F nếu
1. G là một phủ của F, và
2. G có dạng thu gọn tự nhiên theo nghĩa sau:
2a. Hai vế trái và phải của mọi PTH trong G rời nhau (không giao nhau.) f G: LS(f) RS(f) =
2b. Các vế trái của mọi PTH trong G khác nhau đôi một. f, g G: f g LS(f) LS(g)
Thuật toán
Algorithm Natural_Reduced
Function: Tính phủ thu gọn tự nhiên của tập PTH
Format: Natural_Reduced(F) Input: - Tập PTH F
Output: - Một phủ thu gọn tự nhiên G của F
(i) G F (ii) LR G: L R = (iii) LiRi,LjRjG: ij LiLj Method G := ; for each FD L R in F do Z := R - L;
if Z then if there is an FD L Y in G then replace L Y in G by L YZ else add L Z to G; endif; endif; endfor; return G; end Natural_Reduced.
Độ phức tạp của thuật toán: Trong [2] độ phức tạp của thuật toán là O(mn) với
m là số lượng PTH trong tập F và n là số lượng thuộc tính trong U. [1.2]
2.2.2 Thuật toán tìm phủ không dư của tập PTH F.
Định nghĩa: Cho hai tập PTH F và G trên tập thuộc tính U. G được gọi là phủ
không dưcủa F nếu
(i) G là một phủ của F, và
(ii) G có dạng không dư theo nghĩa sau: gG: G \{g} ≢G
Thuật toán
Algorithm Nonredundant
Function: Tính phủ không dư
Format: Nonredundant(F) Input: - Tập PTH F
Output: - Một phủ không dư G của F
(i) G F
(ii) g G: G-{g} ! G Method
U := Attr(F); // Tập thuộc tính của F
G := F; for each FD g: L R in F do if R Closure(U,G-{g},L) then G := G – {g}; endif; endfor; return G;
end Nonredundant.
2.2.3 Thuật toán tìm phủ thu gọn trái của tập PTH.
Định nghĩa: Cho hai tập PTH F và G trên tập thuộc tính U. G được gọi là phủ
thu gọn tráicủa F nếu (i) G là một phủ của F, và
(ii) (LRG,AL): G\{LR}{L\AR} ≢G
Thuật toán
Để ý rằng AL ta có L\A L, nên
g: LRG,AL): G\{g}{L\AR}╞ LR, do đó ta chỉ cần kiểm tra G ╞ L\AR ? [1,2]
Algorithm Left_Reduced
Function: Tính phủ thu gọn trái của tập PTH
Format: Left_Reduced(F) Input: - Tập PTH F
Output: - Một phủ thu gọn trái G của F
(i) G F
(ii) g:LR G,AL: G-{g}{L-{A}R}!G Method
U := Attr(F);// Tap thuoc tinh cua F G := F;
for each FD g:L R in F do X := L;
for each attribute A in X do if R Closure(U,G,L-{A}) then delete A from L in G; endif; endfor; endfor; return G; end Left_Reduced.
Định nghĩa: Cho hai tập PTH F và G trên tập thuộc tính U. G được gọi là phủ
thu gọn phảicủa F nếu (i) G là một phủ của F, và
(ii) (LRG, AR): G\{LR}{LR\A} ≢G
Thuật toán
Algorithm Right_Reduced
Function: Tính phủ thu gọn phải của tập PTH
Format: Right_Reduced(F) Input: - Tập PTH F
Output: - Một phủ thu gọn phải G của F
(i) G F
(ii) g:LRG,AR:G-{g}{LR-{A}}!G Method
U := Attr(F);// Tập thuộc tính của F
G := F;
for each FD g:L R in F do X := R;
for each attribute A in X do
if A in Closure(U,G-{g}{LR-{A}},L) then delete A from R in G;
endif; endfor; if R = then delete L R from G; endif; endfor; return G; Right_Reduced.
2.2.5 Thuật toán tìm phủ thu gọn của tập PTH F.
Định nghĩa: Cho hai tập PTH F và G trên tập thuộc tính U. G được gọi là phủ
thu gọn của F nếu G đồng thời là phủ thu gọn trái và thu gọn phải của F. [1,2]
Function: Tính phủ thu gọn của tập PTH
Format: Reduced(F) Input: - Tập PTH F
Output: - Một phủ thu gọn của F
Method
return Right_Reduced(Left_Reduced(F)); end Reduced.
2.3. Các thuật toán tính bao đóng.
Định nghĩa: Cho tập PTH F trên tập thuộc tính U và một tập con các thuộc
tính X trong U. Bao đóng của tập thuộc tính X, ký hiệu X+ là tập thuộc tính
X+ = {A U | X AF+}
Thuật toán
Algorithm Closure
Function: Tính bao đóng của tập thuộc tính.
Format: Y = Closure(U,F,X) Input: - LĐQH p =(U,F) - Tập thuộc tính X U Output: - Y = X+ = { A U | XA F+ } Method Y := X; repeat Z := Y; for each FD L R in F do if L Y then Y := Y R; endif; endfor; until Y=Z; return Y; end Closure.
Độ phức tạp tính toán Giả sử mỗi lần lặp trong repeat ta chỉ thêm được 1 thuộc tính. Khi đó repeat sẽ được lặp tối đa n lần. Mỗi lần lặp ta phải duyệt toàn bộ m
phụ thuộc hàm để thực hiện các thao tác trên các tập chứa tối đa n thuộc tính.
Vậy độ phức tạp tính toán sẽ là O(n2m). [1,2]
2.4. Các thuật toán khóa
Tư tưởng: Xuất phát từ một siêu khóa K tùy ý của LĐQH, duyệt lần lượt các thuộc tính A của K, nếu bất biến (K{A})+= U được bảo toàn thì loại A khỏi K.
Có thể thay kiểm tra (K{A})+= U bằng kiểm tra A (K{A})+. [2]
Algorithm Key
Function: Tìm một khóa của LĐQH
Format: Key(U,F) Input: - Tập thuộc tính U - Tập thuộc PTH F Output: - Khóa K U thỏa (i) K+ = U (ii) AK: (K-{A})+ U Method K := U;
for each attribute A in U do if A (K-{A})+ then K := K-{A} endif; endfor; return K; end Key.
Độ phức tạp tính toán Trong thuật toán duyệt n thuộc tính, với mỗi thuộc tính thực hiện phép lấy bao đóng với độ phức tạp n2m. Tổng hợp lại, độ phức tạp
tính toán của thuật toán là O(n3m). [1,2]
Trong chương này tôi sẽ trình bày cài đặt và ứng dụng cho các thuật toán theo hướng đối tượng cho thiết kế lớp tập hợp Set và lớp lược đồ quan hệ RSC gồm:
3.1. Tiếp cận hướng đối tượng cho thiết kế
Theo tiếp cận hướng đối tượng ta có thể thiết kế và cài đặt một kiểu dữ liệu mới. Luận văn sẽ tập trung thiết kế ba kiểu dữ liệu phục vụ cho các chương trình xử lý các tập là:
Lớp tập hợp Set
Lớp lược đồ quan hệ RSC
3.2. Thiết kế lớp tập hợp Set 3.2.1 Các thuộc tính của lớp Set 3.2.1 Các thuộc tính của lớp Set
Ta định nghĩa kiểu dữ liệu BS như sau:
const int __SETSIZE = 26; typedef bitset<__SETSIZE> BS;
Trong lớp Set thuộc tính Data được định nghĩa là:
BS Data;
Như vậy, Data là trường dữ liệu chứa 0-25 bit
3.2.2 Các phương thức của lớp Set
Tạo tử (constructor)
Cú pháp
inline Set() { Data.reset();
}
Ý nghĩa: Khởi tạo tập rỗng
inline Set(const string s) {
Data.reset();
for (int i = 0; i < s.length(); ++i)
if (IsAlpha(s[i]))
Data.set(Code(s[i]));
}
Ý nghĩa: Khởi tạo Set nhận giá trị từ chuỗi s Ví dụ: Set x(“ABC”);
Khi đó x chứa 2 phần từ A, B, C
Cú pháp
inline Set(const Set &x) {
Data = x.Data; }
Ý nghĩa: Khởi tạo Set nhận giá trị của tập x cho trước Ví dụ:
Set x(“ABC”);
Set y(x);
Khởi tạo x chứa 3 phần từ A, B, C
Set y(x) khởi tạo tập y nhận các giá trị của tập x là 3 phần từ A, B, C Cú pháp
inline void Reset() {
Data.reset();
}
Ý nghĩa: Reset một tập thành tập rỗng
Toán từ gán
inline void operator =(const string s) { Data.reset();
for (int i = 0; i < s.length(); ++i) if (IsAlpha(s[i]))
Data.set(Code(s[i])); }
Ý nghĩa : Gán một chuỗi từ tập x
Lấy giá trị của tập hợp
Cú pháp
inline char operator[](int i) const {
if (i >= 0 && i < __SETSIZE) {
if (Data[i])
return (Data[i]) ? ToAlpha(i) : NULL; }
return NULL; }
Ý nghĩa: Lấy phần từ thứ i cuả tập hợp.
Kiểm tra tập hợp có phải là tập rỗng hay không
Cú pháp
inline bool Empty() const {return Data.none();}
Ý nghĩa:
Kiểm tra một phần tử có thuộc tập hợp hay không
Cú pháp
inline bool IsIn(char e) {
if (IsAlpha(e)) return Data[Code(e)]; return 0; } Lực lượng của tập hợp Cú pháp
inline int Card() const {return Data.count();
Ý nghĩa: trả về số của tập hợp
Thêm phần tử vào tập hợp
inline void Ins(char e) { if (IsAlpha(e))
Data.set(Code(e)); }
Ý nghĩa : Thêm phần từ e vào tập x
Xóa phần từ
Cú pháp
inline void Del(char e) { if (IsAlpha(e)) Data.reset(Code(e)); } Ý nghĩa : Xóa phần tử e từ tập hợp Thêm phần tử Cú pháp
inline void Add(char e) {Ins(e);
Ý nghĩa: Thêm phần tử e vào tập hợp
3.2.3 Các phép toán tập hợp
Hợp của hai tập hợp
Cú pháp
Set operator +(const Set &y) const { Set z(*this);
z.Data |= y.Data; return z;
}
inline void operator +=(const Set &y) { *this = *this + y;
}
Ý nghĩa: Trả về hợp của 2 tập hợp
Giao của hai tập hợp
Cú pháp
Set operator *(const Set &y) const { Set z(*this);
z.Data &= y.Data; return z;
}
inline void operator *=(const Set &y) { *this = *this * y;
}
Ý nghĩa: Trả về giao của 2 tập hợp
Hiệu củahai tập hợp
Cú pháp
Set operator -(const Set &y) const { Set z;
for (int i = 0; i < __SETSIZE; ++i) if (Data[i] && (!y.Data[i]))
z.Data[i] = 1; return z;
}
inline void operator -=(const Set &y) {
*this = *this - y;
}
Ý nghĩa: Trả về hiệu của 2 tập hợp
So sánh hai phần tử So sánh bằng
Cú pháp
inline bool operator ==(const Set & y) const {
return Data == y.Data;
}
Ý nghĩa: So sánh hai tập hợp có bằng nhau hay không
So sánh khác
Cú pháp
inline bool operator !=(const Set & y) const {
return Data != y.Data;
}
Ý nghĩa: So sánh hai tập hợp có khác nhau hay không
So sánh nhỏ hơn hoặc bằng
Cú pháp
inline friend bool operator <=(const Set & x, const Set & y){ for (int i = 0; i < __SETSIZE; ++i)
if (x.Data[i] && (!y.Data[i])) return false;
return true; }
Ý nghĩa: So sánh hai tập hợp có nhỏ hơn hoặc bằng nhau không.
So sánh lớn hơn hoặc bằng
Cú pháp
inline friend bool operator >=(const Set & x, const Set & y){
return y <= x;
}
Ý nghĩa: So sánh hai tập hợp có lớn hơn hoặc bằng nhau không.
So sánh nhỏ hơn
Cú pháp
friend bool operator <(const Set & x, const Set & y) { return((x <= y) && (x != y));
}
Ý nghĩa: So sánh hai tập hợp có nhỏ hơn không
So sánh lớn hơn
Cú pháp
inline friend bool operator >(const Set & x, const Set & y) {
return y < x;
}
Ý nghĩa: So sánh hai tập hợp có lớn hơn không
Hiển thị
Cú pháp
friend ostream & operator <<(ostream & os, const Set & s) { if (s.Empty()) {
os << "{}"; return os; }
for (int i = 0; i < __SETSIZE; ++i) { if (s.Data[i]) os << ToAlpha(i); } return os; } Ý nghĩa: Hiển thị các phần tử ra màn hình Cú pháp
cout << msg; if (Empty()) {
cout << "{}"; return; }
for (int i = 0; i < __SETSIZE; ++i) { if (Data[i])
cout << ToAlpha(i); }
} }
Ý nghĩa: Hiển thị các phần tử ra màn hình và kèm theo thông báo msg
Ví dụ: Chương trình Demoset.cpp thực hiện các thao tác như sau: 1. Khai báo tập x, y
2. So sánh tập x có lớn hơn hoặc bằng tập y hay không 3. Hiển thị ra màn hình tập x, y
Như vậy ta thu được
/****************************************** Demoset.cpp *************************************************/ #include <iostream> #include <fstream> #include <bitset> using namespace std;
const int __SETSIZE = 26; const int __UNIT = 16;
typedef bitset<__SETSIZE> BS; class Set { protected: BS Data; public: inline Set() { Data.reset(); }
inline Set(const string s) { Data.reset();
for (int i = 0; i < s.length(); ++i) if (IsAlpha(s[i]))
Data.set(Code(s[i])); }
inline Set(const Set &x) { Data = x.Data;
}
inline void Reset() {
Data.reset();
}
inline void operator =(const Set & y) { Data = y.Data;
}
inline void operator =(const string s) { Data.reset();
for (int i = 0; i < s.length(); ++i) if (IsAlpha(s[i]))
Data.set(Code(s[i])); }
inline char operator[](int i) const { if (i >= 0 && i < __SETSIZE) {
if (Data[i])
return (Data[i]) ? ToAlpha(i) :
NULL;
}
return NULL; // (char)0; }
inline bool Empty() const {return Data.none();}
inline bool IsIn(char e) {
if (IsAlpha(e))
return Data[Code(e)];
return 0;
}
inline bool Has(char e) {return IsIn(e);}