1. Trang chủ
  2. » Giáo Dục - Đào Tạo

Xây dựng bộ công cụ hỗ trợ môn automata và trí tuệ nhân tạo

27 0 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Xây dựng công cụ hỗ trợ môn Automata Trí tuệ nhân tạo Mục lục I.Automata ngơn ngữ hình thức 1.Lý thuyết .3 1.1Lý thuyết Automata 1.2Phân loại Automata :có loại 1.3 Sự tương đương NFA DFA II.Ứng dụng 2.1 Bài toán 1:Lập sơ đồ chuyển trạng thái từ NFA sang DFA .5 2.2 Mô tả máy automata hữu hạn đoán nhận xâu: 16 2.3 Xây dựng Automata đẩy xuống đốn nhận ngơn ngữ 26 Nhóm 09-ĐHMHMT1K2 Xây dựng công cụ hỗ trợ môn Automata Trí tuệ nhân tạo C Lời nói đầu ơng nghệ ngày phát triển công nghệ thông tin ngày chuyên sâu vào lĩnh vực đời sống không lĩnh vực học tập nghiên cứu mà công tác giảng dạy Khi gặp giải thuật phức tạp lời giải hàn lâm buồn tẻ dễ gây nhàm chán nắm bắt tình hình nhóm chúng em muốn xây dựng công cụ hỗ trợ môn Automata ngơn ngữ hình thức mơn trí tuệ nhân tạo với ứng dụng trực tiếp công tác học tập chúng em mở rộng chúng em tin giúp giảng mơn Automata ngơn ngữ hình thức phong phú sinh động Hình thức làm chúng em mô tả cụ thể chi tiết vào phần chúng em sử dụng hoàn toàn ngơn ngữ lập trình C# Lúc nhận đề tài chúng em dự định mong muốn làm tất phần giáo trình đại cương mơn automata ngơn ngữ hình thức Nhưng thời gian kiến thức chúng em cịn có hạn nên chúng em chưa hồn thiện tất khơng thể tránh khỏi sai sót Chúng em mong đóng góp thầy bạn để tập lớn chúng em mở rộng sâu sắc đặc biệt ứng dụng học tập công tác giảng dạy Nhóm em xin chân thành cảm ơn Thầy: Trần Hùng Cường tận tình hướng dẫn góp ý kiến để chúng em hồn thành tốt tập Nhóm 09-ĐHMHMT1K2 Xây dựng cơng cụ hỗ trợ mơn Automata Trí tuệ nhân tạo I.Automata ngơn ngữ hình thức 1.Lý thuyết 1.1Lý thuyết Automata Có nhiều hệ thống xem thời điểm trạng thái tập hữu hạn trạng thái Mục đích trạng thái ghi nhớ phần lịch sử hệ thống Một Automata tập hợp trạng thái điều khiển dịch chuyển từ trạng thái sang trạng thái khác nhận liệu vào 1.2Phân loại Automata :có loại Automata đơn định (Deterministic Automata):  Mỗi bước di chuyển xác định cấu hình (hàm chuyển automata đơn trị) Automata không đơn định (Non-deterministic Automata):  Tại bước di chuyển, có vài khả để lựa chọn (hàm chuyển automata đa trị)  Nhóm 09-ĐHMHMT1K2 Xây dựng cơng cụ hỗ trợ mơn Automata Trí tuệ nhân tạo 1.3 Sự tương đương NFA DFA Chúng ta xây dựng DFA từ NFA Từ 1NFA N=(Qn,∑,€n,Fn) xây dựng DFA D=(Qd,∑,€d,Fn) bảng chữ vào NFA DFA giống nhau,trạng thái đầu DFA tập hợp {q0} gồm trạng thái đầu q0 NFA Các thành phần khác xây dựng theo công thức sau: Qd tập tập Qn,Qd thường có số trạng thái nhỏ 2n Fd tập tập S Qn cho S^Fn # Null Đối với tập T thuộc Qn ký hiệu a thuộc Z II.Ứng dụng 2.1 Bài toán 1:Lập sơ đồ chuyển trạng thái từ NFA sang DFA 2.1.1Vẽ DFA với liệu người dùng nhập vào  Mục đích chương trình: vẽ hình dạng DFA người dùng nhập liệu vào o Dữ liệu vào gồm:  Các biến vào  Số lượng trạng thái  Trạng thái cuối( ta mặc định trạng thái đầu là: Q0)  Chú ý: sau người dùng hoàn thành việc nhập số lượng trạng thái biến vào xong ta đưa hình dạng bảng chuyển Từ liệu nhập vào ta vẽ hình dạng DFA với quy tắc: If[Q0, a]==Q1 ta vẽ đường từ Q0 tới Q1 tương tự duyệt hết bảng chuyển Mô tả ngơn ngữ tự nhiên  Mục đích chương trình: vẽ hình dạng DFA người dùng nhập liệu vào o Dữ liệu vào gồm:  Các biến vào  Số lượng trạng thái  Trạng thái cuối( ta mặc định trạng thái đầu là: Q0)  Chú ý: sau người dùng hoàn thành việc nhập số lượng trạng thái biến vào xong ta đưa hình dạng bảng chuyển Từ liệu nhập vào ta vẽ hình dạng DFA với quy tắc: If[Q0, a]==Q1 ta vẽ đường từ Q0 tới Q1 tương tự duyệt hết bảng chuyển Viết ngôn ngữ tự nhiên: Nhóm 09-ĐHMHMT1K2 Xây dựng cơng cụ hỗ trợ mơn Automata Trí tuệ nhân tạo 2.1.2 Vẽ NFA với liệu người dùng nhập vào Mục đích chương trình: vẽ hình dạng DFA người dùng nhập liệu vào o Dữ liệu vào gồm:  Các biến vào  Số lượng trạng thái  Trạng thái cuối( ta mặc định trạng thái đầu là: Q0) Tương tự DFA sau người dùng nhập xong biến vào số lượng trạng thái ta đưa hình dạng bảng chuyển Từ liệu vào ta tiến hành duyệt Gặp kí tự khác “,” ta vẽ đường từ trạng thái thời tới Lần lượt duyệt tương tự hết Đây hình giao diện chương trình: Trên sổ gồm Form chính: Trí tuệ nhân tạo Automata ngơn ngữ hình thức Trợ giúp (phần chúng em hồn thiện) Sau click vào Automata click chuột vào nội dụng mà muốn thực VD:chúng ta chọn sơ đồ chuyển trạng thái NFA-DFA Nhóm 09-ĐHMHMT1K2 Xây dựng cơng cụ hỗ trợ mơn Automata Trí tuệ nhân tạo Sau chọn vào sơ đồ bạn cần vẽ sau nhập vào xích ma, nhập vào số trạng thái sau click vào vào tạo sơ đồ biểu đồ trạng thái Chọn trạng thái kết thúc Ví dụ: vẽ DFA tương ứng với trạng thái bắt đầu Q0, trạng thái kết thúc Q2 Nhóm 09-ĐHMHMT1K2 Xây dựng cơng cụ hỗ trợ mơn Automata Trí tuệ nhân tạo Và kết sau ta nhấn xem sơ đồ Code biểu diễn thuật toán trên: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Drawing.Drawing2D; namespace _DoAn.DFA_NFA { public partial class DrawingDFA : Form { public DrawingDFA() { InitializeComponent(); } public public public public DataTable dt_diagram = new DataTable(); DataTable dt_status = new DataTable(); string strXichMa = ""; int status_number = 0; private void DrawingDFA_Load(object sender, EventArgs e) { } private void panel1_Paint(object sender, PaintEventArgs e) { base.OnPaint(e); Graphics g = e.Graphics; float x = 30, y = 200, w = 50, h = 50, r = 120, a = 3, b = 15; Nhóm 09-ĐHMHMT1K2 Xây dựng công cụ hỗ trợ môn Automata Trí tuệ nhân tạo float yc = y + h / 2;//x,y center; Pen aPen = new Pen(Color.White, 1); Brush bru = new SolidBrush(Color.White); Font f = new Font("Tahoma", 13); //tạo bảng lưu vị trí trái, phải trạng thái DataTable dt_pos = new DataTable(); dt_pos.Columns.Add("xb"); dt_pos.Columns.Add("xe"); //vẽ trạng thái for (int i = 0; i < status_number; i++) { // lưu vị trí DataRow row = dt_pos.NewRow(); row["xb"] = x; row["xe"] = x + w; dt_pos.Rows.Add(row); //vẽ trạng thái g.DrawEllipse(aPen, x, y, w, h); if (Convert.ToBoolean(dt_status.Rows[0][i])) g.DrawEllipse(aPen, x + a, y + a, w - * a, h - * a); g.DrawString("q" + i, f, bru, x + b, y + b); x += r + w; } //vẽ đường aPen.Width = 3; aPen.EndCap = LineCap.ArrowAnchor; g.DrawLine(aPen, new Point(0, (int)yc), new Point(30, (int)yc));// vẽ đường tiến đến q0 f = new Font("Tahoma", 11); int sls = 6, sll = 30;//space_line_small : khoảng cách nhỏ đường đi, space_line_large: khoảng cách lớn đường //các ma trận để vẽ nhãn đường DataTable arr = new DataTable(), arr1 = new DataTable(), arr2 = new DataTable(); for (int i = 0; i < status_number; i++) arr.Columns.Add(i.ToString()); DataRow row_arr = arr.NewRow(); for (int i = 0; i < status_number; i++) row_arr[i.ToString()] = 0; arr.Rows.Add(row_arr); //ma trận khởi tạo : arr với số cột trạng thái dòng = tất for (int i = 0; i < status_number; i++)//vòng lặp trạng thái { DataRow row = dt_diagram.Rows[i]; int flag = 0; arr1 = arr.Copy(); arr2 = arr.Copy(); Nhóm 09-ĐHMHMT1K2 Xây dựng cơng cụ hỗ trợ mơn Automata Trí tuệ nhân tạo foreach (char c in strXichMa)//vịng lặp chuỗi Xích Ma { int q = Convert.ToInt16(row[c.ToString()]); if (i < q)//trạng thái i đứng trước trạng thái q, vẽ đường từ i đến q { Point pb = new Point(Convert.ToInt16(dt_pos.Rows[i] ["xe"]), Convert.ToInt16(yc) + (i - q) * sls); Point pe = new Point(Convert.ToInt16(dt_pos.Rows[q] ["xb"]), Convert.ToInt16(yc) + (i - q) * sls); Point pc = new Point((pb.X + pe.X) / 2, Convert.ToInt16(yc + (i - q) * sll)); int n = Convert.ToInt16(arr1.Rows[0][q.ToString()]); if (n == 0)//nếu trạng thái i đến trạng thái q chưa có { g.DrawCurve(aPen, new Point[] { pb, pc, pe });//vẽ đường g.DrawString(c.ToString(), f, bru, pc.X - 8, pc.Y + 5);//vẽ nhãn ko có dấu "," nen -8 } else { g.DrawString("," + c.ToString(), f, bru, pc.X - 10 + (10 * n), pc.Y + 6);//vẽ nhãn có dấu "," trừ 10 } arr1.Rows[0][q.ToString()] = n + 1;//cho biết trạng thái i -> trạng thái q có n + đường } else if (i == q)//trạng thái i quay lại { Point pb = new Point(Convert.ToInt16(dt_pos.Rows[i] ["xe"]) - 5, Convert.ToInt16(yc) - 17); Point pe = new Point(Convert.ToInt16(dt_pos.Rows[i] ["xb"]) + 5, Convert.ToInt16(yc) - 17); Point pc1 = new Point((pb.X + pe.X) / + 10, Convert.ToInt16(yc - 50)); Point pc2 = new Point((pb.X + pe.X) / - 10, Convert.ToInt16(yc - 50)); if (flag == 0) { g.DrawCurve(aPen, new Point[] { pb, pc1, pc2, pe }); g.DrawString(c.ToString(), f, bru, (pb.X + pe.X) / - 8, pc1.Y - 20);//do ko có dấu , nen -8 } else { g.DrawString("," + c.ToString(), f, bru, (pb.X + pe.X) / - 10 + 10 * flag, pc1.Y - 19);//có dấu , trừ 10 } flag++; } else//trạng thái i đứng sau trạng thái q, vẽ đường từ q đến i { Nhóm 09-ĐHMHMT1K2 Xây dựng cơng cụ hỗ trợ mơn Automata Trí tuệ nhân tạo Point pb = new Point(Convert.ToInt16(dt_pos.Rows[i] ["xb"]), Convert.ToInt16(yc) + (i - q) * sls); Point pe = new Point(Convert.ToInt16(dt_pos.Rows[q] ["xe"]), Convert.ToInt16(yc) + (i - q) * sls); Point pc = new Point((pb.X + pe.X) / 2, Convert.ToInt16(yc + (i - q) * sll)); int n = Convert.ToInt16(arr2.Rows[0][q.ToString()]); if (n == 0) { g.DrawCurve(aPen, new Point[] { pb, pc, pe }); g.DrawString(c.ToString(), f, bru, pc.X - 8, pc.Y - 15); } else { g.DrawString("," + c.ToString(), f, bru, pc.X - 10 + (10 * n), pc.Y - 14); } arr2.Rows[0][q.ToString()] = n + 1; } } } } /* * Vẽ trạng thái(hình trịn) trước, lưu trữ lại vị trí trái, phải trạng thái * vẽ mũi tên dựa sơ đồ chuyển dịch */ } } VD2: vẽ NFA tương ứng với trạng thái đầu Q0 trạng thái cuối q2 hoàn thành sơ đồ chuyển dịch trạng thái sau click vào xem sơ đồ ta có hiển thị hình vẽ Nhóm 09-ĐHMHMT1K2 10 Xây dựng công cụ hỗ trợ môn Automata Trí tuệ nhân tạo arr1 = arr.Copy(); arr2 = arr.Copy(); foreach (char c in strXichMa)//vịng lặp chuỗi Xích Ma { string s = row[c.ToString()].ToString(); for (int K = 0; K < s.Length; K++) { int q = Convert.ToInt16(s[K].ToString()); if (i < q)//trạng thái i đứng trước trạng thái q, vẽ đường từ i đến q { Point pb = new Point(Convert.ToInt16(dt_pos.Rows[i]["xe"]), Convert.ToInt16(yc) + (i - q) * sls); Point pe = new Point(Convert.ToInt16(dt_pos.Rows[q]["xb"]), Convert.ToInt16(yc) + (i - q) * sls); Point pc = new Point((pb.X + pe.X) / 2, Convert.ToInt16(yc + (i - q) * sll)); int n = Convert.ToInt16(arr1.Rows[0] [q.ToString()]); if (n == 0)//nếu trạng thái i đến trạng thái q chưa có { g.DrawCurve(aPen, new Point[] { pb, pc, pe });//vẽ đường g.DrawString(c.ToString(), f, bru, pc.X - 8, pc.Y + 5);//vẽ nhãn ko có dấu "," nen -8 } else { g.DrawString("," + c.ToString(), f, bru, pc.X - 10 + (10 * n), pc.Y + 6);//vẽ nhãn có dấu "," trừ 10 } arr1.Rows[0][q.ToString()] = n + 1;//cho biết trạng thái i -> trạng thái q có n + đường } else if (i == q)//trạng thái i quay lại { Point pb = new Point(Convert.ToInt16(dt_pos.Rows[i]["xe"]) - 5, Convert.ToInt16(yc) - 17); Point pe = new Point(Convert.ToInt16(dt_pos.Rows[i]["xb"]) + 5, Convert.ToInt16(yc) - 17); Point pc1 = new Point((pb.X + pe.X) / + 10, Convert.ToInt16(yc - 50)); Point pc2 = new Point((pb.X + pe.X) / - 10, Convert.ToInt16(yc - 50)); if (flag == 0) { g.DrawCurve(aPen, new Point[] { pb, pc1, pc2, pe }); g.DrawString(c.ToString(), f, bru, (pb.X + pe.X) / - 8, pc1.Y - 20);//do ko có dấu , nen -8 } else { Nhóm 09-ĐHMHMT1K2 13 Xây dựng công cụ hỗ trợ môn Automata Trí tuệ nhân tạo g.DrawString("," + c.ToString(), f, bru, (pb.X + pe.X) / - 10 + 10 * flag, pc1.Y - 19);//có dấu , trừ 10 } flag++; } else//trạng thái i đứng sau trạng thái q, vẽ đường từ q đến i { Point pb = new Point(Convert.ToInt16(dt_pos.Rows[i]["xb"]), Convert.ToInt16(yc) + (i - q) * sls); Point pe = new Point(Convert.ToInt16(dt_pos.Rows[q]["xe"]), Convert.ToInt16(yc) + (i - q) * sls); Point pc = new Point((pb.X + pe.X) / 2, Convert.ToInt16(yc + (i - q) * sll)); int n = Convert.ToInt16(arr2.Rows[0] [q.ToString()]); if (n == 0) { g.DrawCurve(aPen, new Point[] { pb, pc, pe }); g.DrawString(c.ToString(), f, bru, pc.X - 8, pc.Y - 15); } else { g.DrawString("," + c.ToString(), f, bru, pc.X - 10 + (10 * n), pc.Y - 14); } arr2.Rows[0][q.ToString()] = n + 1; } } } } } } } 2.2 Mô tả máy automata hữu hạn đoán nhận xâu: DFA xử lý xâu nào: giả sử ư=a0a1a2….an xâu vào DFA bắt đầu với trạng thái q0 thực dịch chuyển e(q0,a1) giả sử cho q1 DFA trạng thái q1 kí tự đọc a2 thực dịch chuyển e(q1,a2) giả sử cho q2.Nó tiếp tục đọc an chuyển sang trạng thái qn ,nếu qn thuộc tập F DFA thừa nhận xâu vào w ngược lại xâu vào khơng thừa nhận xâu vào w, ngược lại xâu vào khơng thừa nhận Cho biểu thức quy vẽ sơ đồ chuyển trạng thái Viết ngôn ngữ tự nhiên: Nhóm 09-ĐHMHMT1K2 14 Xây dựng cơng cụ hỗ trợ mơn Automata Trí tuệ nhân tạo Kết toán là: Từ đầu vào là:10*+1 Mục đích : xây dựng “máy” automata đốn nhận biểu thức quy, lên hình dạng NFA tương ứng với biểu thức quy người dùng nhập vào Cách xây dựng  Dữ liệu vào  Dữ liệu vào chuỗi người dùng nhập từ bàn phím  Thuật tốn  Bước 1: Đếm biến vào  Từ biểu thức( chuỗi) người dùng nhập vào ta đếm số biến vào( kí tự khác kí tự “*” “+”)  Bước 2:xây dựng bảng chuyển sở:  Ta xây dựng bảng chuyển sở tương ứng với biển vào( số lượng bảng chuyển số lượng biến vào đếm trên) Cấu trúc bảng chuyển Qstart Qfinish Biến vào ( biến vào 2) Qfinish null  Bước 3: xây dựng bảng chuyển cuối cùng: Duyệt chuỗi để tìm thực thứ tự ưu tiên Nhóm 09-ĐHMHMT1K2 15 Xây dựng cơng cụ hỗ trợ mơn Automata Trí tuệ nhân tạo  Thứ tự ưu tiên 1: ( việc tìm dấu “*) If có thực phép bao đóng( dấu “*) cách: o  Lấy kí tự trước kí tự “*”  Tham chiếu tới bảng chuyển tương ứng với kí tự Vì phép bao đóng “*”nên ta cần thêm vào bảng chuyển mới: trạng thái Và biến € Bảng chuyển phép bao đóng có dạng Qstart Null ……… Null € Qstart cũ, Qfinish Qstart cũ … Qfinish cũ Null Null Qfinish Null Null Qfinish mới, Qstart cũ null Đánh dấu bảng kí tự bất kì( sau coi biến) Tiếp tục lập bảng chuyển cho phép bao đóng khác chuỗi tới hêt -> finish thứ tự ưu tiên  Thứ tự ưu tiên Duyệt chuỗi kiểm tra o Nhóm 09-ĐHMHMT1K2 16 Xây dựng cơng cụ hỗ trợ mơn Automata Trí tuệ nhân tạo Kiểm tra có hai kí tự liền khác kí tự dấu “+” tham chiếu tới bảng chuyển hai kí tự để xây dựng lên bảng chuyển sau: VD: cần kết hợp bảng chuyển Các biến vào Qstart1 … Qfinish1 Null …… null Các biến vào Qstart2 … Qfinish2 Null …… null Kết hợp hai bảng chuyển ( phép nhân) Trong phép nhân: o o o o Số lượng biến=∑( biến vào 1+ biến vào 2)+ thêm € if chưa có Số lượng trạng thái =∑( trạng thái 1+ trạng thái 2) Phải xóa quyền làm trạng thái kết thúc Qfinish1 xóa quyền làm trạng thái khởi đầu trạng thái Qstart2 , lại quyền làm trạng thái khởi đầu trạng thái Qstart1 quyền làm trạng thái kết thúc Qfinish2 Tại ô [Qfinish1, €] ta đặt giá trị = Qstart2 Bảng chuyển ứng với phép nhân có dạng: Các biến vào Qstart1 … Qfinish1 Qstart2 … Nhóm 09-ĐHMHMT1K2 Các biến vào € Null Null Qstart2 Null Null 17 Xây dựng công cụ hỗ trợ môn Automata Trí tuệ nhân tạo Qfinish2 null Tương tự : sau bảng nhân xây dựng xong ta cần đánh dấu kí tự từ lúc ta coi kí tự biến Tiếp tục thực phép nhân biểu thức khơng cịn phép nhân xong ưu tiên Thứ tự ưu tiên Duyệt chuỗi sau bước hoàn thành Kiểm tra thấy chuỗi có dấu “+” lấy kí tự liền kề hai bên thực bước sau Ví Dụ là: Các biến vào Qstart1 … Qfinish1 Null …… null Các biến vào Qstart2 … Qfinish2 Null …… null Thực kết hợp bảng chuyển phép “+” Kiểm tra biến vào biến vào khơng có € thêm € vào Thực thêm vào trạng thái Qstart Qfinish vào bảng chuyển Bảng chuyển có dạng Kết thúc: từ bảng chuyển ta vẽ hình sơ đồ chuyển trạng thái Qstart Qstart1 … Qfinish1 Qstart2 … Qfinish2 Qfinish Các biến vào Null Các biến vào null Qfinish Nhóm 09-ĐHMHMT1K2 null € Qstart1, Qstart2 null Qfinish null 18 Xây dựng công cụ hỗ trợ môn Automata Trí tuệ nhân tạo Mơ tả “ máy ” otomata hữu hạn đơn định đoán nhận xâu Viết ngôn ngữ tự nhiên a b c d  Kết toán Q0 Q1 Q2 Q0 Q0 Q1 Q1 Q1 Q0 Q0 Q2 Q2 Q1 Q0 null  Mục đích: Để kiểm tra xâu (chuỗi) người dùng nhập vào có với DFA định nghĩa không? If (true) -> Đưa thông báo : chuỗi chấp nhận If( false) -> Đưa thông báo: không chấp nhận  Cách xây dựng Đầu vào:  Định nghĩa DFA(do người dùng nhập vào)(giống với việc định nghĩa để vẽ DFA tập lớn) gồm:  Slg trạng thái  Slg biến vào  Trạng thái bắt đầu  Trạng thái kết thúc…… QUÊN phần quan trọng là:  Bảng chuyển Ví dụ Nhập vào bảng chuyển trạng thái: Nhóm 09-ĐHMHMT1K2 19 Xây dựng công cụ hỗ trợ môn Automata Trí tuệ nhân tạo  Nhập xâu vào: W  Nhập xong Đầu Kết luận chuỗi có chấp nhận hay khơng?  Giải thuật  Bước 1: Khi người sử dụng nhập vào chuỗi W=” 010100011100011” Thì chuỗi đẩy vào băng chuyền trạng thái điều khiển Q0 , đầu đọc vị trí thứ băng chuyền( hình vẽ)( vị trí start)  Bước 2: kiểm tra: If( [q0,0] != Null) (giả sử giá trị q1) làm cơng việc sau:  Dịch chuyển đầu đọc điều khiển sang phải ô đồng thời  Do[q0,0]=q1 nên ô điều khiển ta thay hiển thị q0 thành q1 Bước 3: Tiếp tục lặp lại bước :  Hết xâu w  [q0,w]==Null Kết luận: TH1:( hết xâu w) :kiểm tra xem trạng thái điều khiển có trùng với trạng thái kết thúc không? If(true) -> đưa thông báo: xâu(chuỗi ) chấp nhận Else(flase) ->khơng chấp nhận Nhóm 09-ĐHMHMT1K2 20

Ngày đăng: 29/09/2023, 12:50

Xem thêm:

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w