Trong các lĩnh vực lập trình là thời gian chạy thuật toán thực sự là một vấn đề quan trọng?


15

Đôi khi tôi nghe mọi người nói rằng vì tốc độ của bộ xử lý và dung lượng bộ nhớ khả dụng, hiệu quả thuật toán và thời gian chạy không thực sự là mối quan tâm lớn.

Nhưng tôi tưởng tượng vẫn còn những lĩnh vực mà những cân nhắc như vậy vẫn có tầm quan trọng tối cao. Hai điều xuất hiện trong giao dịch thuật toán, trong đó hàng ngàn giao dịch phải được thực hiện theo phân số của một giây và lập trình hệ thống nhúng, nơi bộ nhớ và sức mạnh thường khan hiếm. Tôi có đúng về những ví dụ này không? và những lĩnh vực khác cũng sẽ là ví dụ?


1
Kẻ gây rối LMAX có thể khiến bạn quan tâm: infoq.com/presentations/LMAX

"Giao dịch thuật toán" là một ví dụ tồi. Các thuật toán thường tầm thường; hiệu suất tổng thể có độ trễ thấp là vấn đề của các tài nguyên chuyên dụng, hơn là thiết kế thuật toán thông minh.
S.Lott

6
Độ phức tạp luôn quan trọng hơn tài nguyên phần cứng khi kích thước của dữ liệu tăng lên. Một O(n*log(n))thuật toán sẽ hoàn thành nhanh hơn trên một máy tính 30 tuổi so với một O(n!)hoặc O(n*n)trên phần cứng đắt nhất hiện nay nếu nđủ lớn.
vsz

1
Bạn có thể nghĩ về nó như O(c * f(n))Trường hợp hằng số cdựa trên sự kém hiệu quả của phần cứng. Bạn có thể có một hệ thống nhanh hơn 1000 lần, khi nđi đến vô tận, nó sẽ ngày càng ít đi. Tôi sẽ chọn một O(10000 * log(n))thay vì O(n)bất kỳ ngày nào nếu tôi nghi ngờ rằng nó ncó thể lớn.
vsz

Bạn có thể quan tâm tại sao vấn đề hiệu suất
Theraot

Câu trả lời:


14

Tốc độ luôn luôn là nhu cầu. Tôi đoán bạn đúng. Dưới đây là một số ví dụ về các thuật toán gọn gàng đang được yêu cầu:

  1. Mật mã

  2. Tìm kiếm cơ sở dữ liệu lớn

  3. Sắp xếp và hợp nhất

  4. Tìm kiếm văn bản (không được lập chỉ mục), bao gồm cả ký tự đại diện

  5. Các bài toán có tính toán chuyên sâu

  6. Mô phỏng

  7. Ứng dụng khai thác dữ liệu

  8. Hoạt hình

  9. AI

  10. Tầm nhìn máy tính


2
Tôi muốn thêm vào ứng dụng "quan trọng suốt đời" này như thiết bị y tế.
stuartmclark

@stuartmclark, bạn khá đúng. Tôi cũng quên đề cập đến Hệ thống điều khiển tự động và Hệ thống định vị!
NoChance

2
Tốc độ không liên quan khủng khiếp đến tiền điện tử trừ khi bạn đang cố bẻ khóa mật khẩu. Tôi sẽ đặt "cơ sở dữ liệu lớn" lên hàng đầu. Khối lượng thông tin có sẵn trên internet là đáng kinh ngạc. Một thuật toán dữ liệu lớn câm có thể giết chết một ý tưởng tốt bằng cách làm cho nó có vẻ không khả thi.
S.Lott

4
@ S.Lott, tốc độ cực kỳ phù hợp. Một trang web phục vụ hàng ngàn yêu cầu SSL mỗi giây sẽ bị nghẹt nếu thuật toán tiền điện tử không được tối ưu hóa đủ tốt. Một số thậm chí đang sử dụng tăng tốc phần cứng.
SK-logic

@ SK-logic: Mặc dù đúng, nó không phải là loại xem xét thuật toán giống như những cái khác. Hầu hết việc xử lý tiền điện tử có một thuật toán tương đối đơn giản với rất nhiều tối ưu hóa siêu thông minh để giảm "tính toán" cho việc tra cứu bảng và xử lý bit. Tôi cho rằng đây là "thuật toán", nhưng tiền điện tử luôn có vẻ như rất nhiều tối ưu hóa siêu thông minh hơn là thiết kế thuật toán. Đó là lý do tại sao tôi đề nghị rằng đó không phải là đầu tiên .
S.Lott

7

Có một số trường hợp trong đó thời gian chạy thuật toán có thể không phải là vấn đề lớn, bởi vì chúng ta đã đến mức bạn có thể chỉ cần vượt qua một thuật toán chạy dài hơn với phần cứng mạnh hơn. Nhưng chắc chắn có một số nơi mà việc tăng tốc là rất cần thiết.

Nói chung, bất cứ điều gì sử dụng bộ dữ liệu lớn sẽ là một vấn đề. Khi bạn có một cái gì đó quy mô kém với n, và sau đó bạn thực hiện số lượng rất lớn, bạn có một vấn đề. Tôi nghi ngờ nếu bạn đã đi đến trang web beta Khoa học tính toán và chọc ngoáy một chút, bạn có thể tìm thấy nhiều vấn đề cần các thuật toán tốt hơn, nhanh hơn. Một số lĩnh vực mà tôi đã chạy vào:

  • Đặc biệt phân tích thống kê phức tạp. Một sự kết hợp của các thuật toán không hiệu quả và các tập dữ liệu lớn có thể có nghĩa là sự chậm lại lớn. Đối với một số nghiên cứu, điều này có thể không quan trọng, nhưng nếu bạn đang cố gắng làm điều gì đó với tốc độ nhanh thì sao? "Nó sẽ ra khỏi máy chủ sau một tháng nữa" có lẽ là một điều tồi tệ khi bạn đang vận hành một hệ thống giám sát mối đe dọa hóa học / hạt nhân / sinh học.
  • Khai thác dữ liệu trên các tập dữ liệu lớn.
  • Mô phỏng liên quan đến nhiều biến số.

Nói chung, điện toán khoa học nói chung dường như là một lĩnh vực mà sự phức tạp của những gì được lập trình tạo ra cơ hội cho sự chậm chạp nghiêm trọng nếu thuật toán của bạn chậm chạp (nhiều trong số chúng bị ảnh hưởng rất lớn). Và, như bạn đã đề cập, có các ứng dụng tài chính. Khi mili giây có thể xác định bạn kiếm được hay mất tiền khi giao dịch, thuật toán "đủ tốt" sẽ không cắt giảm nếu có điều gì đó tốt hơn có thể được thực hiện.


4

Đôi khi tôi nghe mọi người nói rằng vì tốc độ của bộ xử lý và dung lượng bộ nhớ khả dụng, hiệu quả thuật toán và thời gian chạy không thực sự là mối quan tâm lớn.

Mang nó theo một hạt muối. Nhiều sức mạnh tính toán hơn về cơ bản chỉ có nghĩa là n của bạn có thể trở nên lớn hơn nhiều trước khi nó chậm lại đáng kể. Đối với hầu hết các vấn đề hàng ngày, n này hiện đủ lớn mà bạn không cần phải quan tâm. Tuy nhiên, bạn vẫn nên biết sự phức tạp của các thuật toán của bạn.

Với nhiều tài nguyên có sẵn hơn, nó có thể cần phải xử lý nhiều dữ liệu hơn sau này. Hôm nay bạn cần phân tích tệp nhật ký 10 MB với 100.000 dòng. Trong một năm, bạn có thể có tệp nhật ký 100 GB với 1.000.000.000 dòng. Nếu lượng dữ liệu tăng nhanh hơn sức mạnh tài nguyên, bạn sẽ gặp vấn đề sau này.

Với nhiều tài nguyên có sẵn hơn, nhiều lớp được xếp chồng lên nhau. Hệ điều hành, khung hệ điều hành, khung bên thứ 3, trình thông dịch ngôn ngữ và cuối cùng là công cụ của riêng bạn. Tất cả sự không hiệu quả không cần thiết trong tất cả các lớp khác nhau nhân lên. Ngày mai, công cụ của bạn có thể chạy trên một hệ điều hành mới có nhiều chuông và còi hơn, bản thân nó ăn nhiều chu kỳ và nhiều bộ nhớ hơn, để lại ít hơn cho bạn.

Vì vậy, để trả lời câu hỏi của bạn, bạn vẫn cần quan tâm đến việc ngày càng có nhiều dữ liệu cần được xử lý (đủ ví dụ được đưa ra trong các câu trả lời khác) và nơi bạn không cung cấp công cụ cuối cùng, nhưng một lớp trừu tượng khác cho các công cụ khác.


4

Vài năm trước tôi đã phải viết một thuật toán sắp xếp các ống nghiệm được sắp xếp trên các ngiá đỡ thành hai phân vùng riêng biệt: tức là một tập hợp con của các ống được 'chọn' và phần còn lại là 'không được chọn' và kết quả cuối cùng sẽ là không có giá đỡ sẽ có cả ống 'được chọn' và 'không được chọn' trên đó (có một số yêu cầu bổ sung như nén). Mỗi giá chứa tối đa 100 ống.

Thuật toán được sử dụng để điều khiển robot phân loại ống trong phòng thí nghiệm dược phẩm.

Khi đặc điểm kỹ thuật ban đầu được trao cho tôi, tôi được phân bổ trong khoảng 1 phút thời gian tính toán để sắp xếp khoảng 2000 ống vì chúng tôi nghĩ rằng khả năng sử dụng khôn ngoan mà không quá đau đớn. Có một yêu cầu là số lần di chuyển là tối thiểu trên tất cả các kết hợp có thể vì bản thân robot chậm .

Giả định ngầm định là độ phức tạp sẽ theo cấp số nhân với số lượng ống. Tuy nhiên, trong khi làm việc với thiết kế thuật toán, tôi phát hiện ra rằng có một O(n)thuật toán nhanh , trong đó nsố lượng giá đỡ thực hiện phân vùng tối ưu của các ống. Kết quả của điều đó là thời gian sắp xếp thuật toán là tức thời nên màn hình sắp xếp sẽ được cập nhật theo thời gian thực khi người dùng định cấu hình hoạt động sắp xếp của họ.

Đối với tôi, sự khác biệt giữa người dùng ngồi trong một phút sau mỗi thay đổi và có GUI phản hồi tức thì là sự khác biệt giữa một phần mềm đủ chức năng và một phần mềm rất thích sử dụng.


Ví dụ hay! Âm thanh như bạn đã làm một cái gì đó giống như một loại cơ số?
Barry Brown

@BarryBrown - không chắc tên thuật toán tôi đã sử dụng là gì khi tôi tự tạo ra nó. Về cơ bản, nó là loại hai danh sách đồng thời có sự cạnh tranh. Vì vậy, mỗi giá có thể xuất hiện trong danh sách "được chọn" hoặc "không được chọn" và chi phí của nó trong danh sách đó là chi phí loại bỏ tất cả các ống bất hợp pháp.

3

Các lĩnh vực khác bao gồm nhiều loại xử lý tín hiệu thời gian thực, hệ thống kiểm soát phản hồi, giải mã thăm dò dầu, nén video, dò tia và kết xuất khung phim, hệ thống thực tế ảo, trò chơi có tốc độ khung hình cao có thể là lợi thế cạnh tranh đáng kể và điện thoại thông minh khác. các ứng dụng thiết bị di động, nơi có số lượng lớn chu kỳ CPU sẽ tiêu tốn thời lượng pin của người dùng nhanh hơn.

Tôi khá ngạc nhiên khi câu hỏi này thậm chí sẽ được hỏi, vì đối với bất kỳ siêu máy tính Top 500 nào từng được chế tạo, có khả năng sẽ có một danh sách chờ đợi của các nhà nghiên cứu có thể tối đa hóa nó và mong muốn các cường độ tính toán mạnh hơn hoặc các thuật toán tốt hơn để giải quyết một số vấn đề (gấp một số protein để giải mã ung thư, v.v.) trước khi họ nghỉ hưu.


1
Vấn đề về tuổi thọ pin (hoặc chỉ sử dụng năng lượng nói chung) rất quan trọng trong những ngày này (6 năm sau khi câu trả lời này được đăng), rằng công ty của tôi có các số liệu năng lượng cụ thể mà chúng tôi dự kiến ​​sẽ đạt được trong các ứng dụng của chúng tôi ngoài các số liệu về thời gian. Trong quá trình phát triển, chúng tôi đã có các ứng dụng khiến thiết bị quá nóng và chuyển sang chế độ chậm hơn, hoạt động kém hơn. Các thuật toán tốt hơn, hiệu quả hơn làm giảm bớt điều này!
dùng1118321

1

Tôi nghĩ các công cụ tìm kiếm như GoogleBing là một trong những lĩnh vực lớn nhất sử dụng thuật toán phức tạp và chúng đóng vai trò chính trong việc tăng tốc kết quả với mức độ phù hợp (xếp hạng trang) mang lại nhiều tiện ích hơn cho người dùng.


1

Hiệu quả của thuật toán không phải là mối quan tâm chính hiện nay vì chúng tôi đang sử dụng các thuật toán hiệu quả. Nếu bạn đã sử dụng thuật toán O (n!), Nó sẽ chậm trên mọi loại phần cứng.


Đó là một quan điểm thú vị. "Đó không phải là một vấn đề, bởi vì nó không nên nói" hơn là "đó là một vấn đề, nhưng không phải là một vấn đề quan trọng".
rẽ trái

1

Độ phức tạp của thuật toán ngày càng trở nên quan trọng hơn khi lượng dữ liệu tăng lên. May mắn thay, các giải pháp chung hiệu quả cho các vấn đề lập trình phổ biến (chủ yếu là tìm kiếm và sắp xếp) được bao gồm trong hầu hết mọi thư viện chuẩn của ngôn ngữ lập trình hiện đại, do đó, thông thường, một lập trình viên không phải lo lắng về những điều này nhiều. Nhược điểm là nhiều lập trình viên không biết gì về những gì đang diễn ra dưới mui xe và đặc điểm của các thuật toán họ sử dụng là gì.

Điều này trở nên đặc biệt khó khăn khi nhiều ứng dụng không được kiểm tra căng thẳng đúng cách: Mọi người viết mã hoạt động tốt cho các tập dữ liệu thử nghiệm nhỏ, nhưng khi phải đối mặt với dữ liệu nhiều hơn vài nghìn lần, mã sẽ bị dừng lại. Một cái gì đó hoạt động tốt cho mười bản ghi nhanh chóng phát nổ khi tập dữ liệu phát triển. Ví dụ trong thế giới thực: một đoạn mã được cho là để xóa các mục không được liên kết với bất kỳ danh mục nào nữa đã sử dụng vòng lặp lồng nhau ba cấp, đó là O (n ^ 3). Chỉ với 10 bản ghi trong cơ sở dữ liệu kiểm tra, điều này có nghĩa là 1000 kiểm tra - hoàn toàn có thể thực hiện được và không gây ra sự chậm trễ đáng chú ý nào. Tuy nhiên, cơ sở dữ liệu sản xuất nhanh chóng chứa khoảng 1000 hàng và đột nhiên mã này thực hiện một tỷ kiểm tra mỗi lần.

Vì vậy: Không, bạn không cần biết và thực hiện tất cả các loại thuật toán gọn gàng và bạn không cần phải có khả năng tự phát minh, nhưng bạn cần một số kiến ​​thức cơ bản về các thuật toán phổ biến, những gì chúng là Điểm mạnh và điểm yếu là khi nào và khi nào không sử dụng chúng và bạn cần nhận thức được tác động có thể có của độ phức tạp thuật toán, để bạn có thể quyết định mức độ phức tạp nào được chấp nhận.


0

Đây không phải là câu hỏi về những miền ứng dụng nào nhạy cảm với thời gian chạy. Bất kỳ chương trình nào, ở bất cứ đâu, đều có hiệu suất tối thiểu dưới mức mà nó thực sự vô giá trị. Điểm phức tạp của thuật toán là cách nó thay đổi khi tăng kích thước đầu vào. Nói cách khác, các lĩnh vực mà tốc độ đặc biệt quan trọng là những khu vực mà bạn mong đợi phải mở rộng ra không chỉ quy mô vấn đề hiện tại của bạn, mà cả thứ tự cường độkích thước vấn đề hiện tại của bạn. Nếu bạn xử lý các đơn xin thuế của công dân Pháp, nhiệm vụ có thể lớn, nhưng không có khả năng quy mô dân số hoặc mức độ phức tạp của việc xử lý một bản ghi sẽ tăng gấp mười hoặc trăm lần, vì vậy bất cứ điều gì hiệu quả bạn bây giờ, có thể sẽ tiếp tục làm việc. Nhưng nếu bạn cố gắng tạo ra thứ gì đó sẽ giảm ở khối lượng internet, thì độ phức tạp của thuật toán là chính: mọi thứ phụ thuộc nhiều hơn tuyến tính hoặc tuyến tính vào kích thước đầu vào sẽ trở nên đắt hơn rất nhanh và cuối cùng tốc độ của bộ xử lý không thể theo kịp sự tăng trưởng.


0

Trong lĩnh vực của tôi (VFX, bao gồm những thứ như theo dõi đường dẫn, hoạt hình máy tính, mô phỏng hạt, động lực học chất lỏng, xử lý hình ảnh, v.v.), độ phức tạp thuật toán là cơ bản. Không có cách nào hoạt động kém hơn thời gian tuyến tính có thể hy vọng hoàn thành trong bất kỳ thời gian hợp lý nào trên các đầu vào thường đạt tới hàng triệu đỉnh, đa giác, voxels, hạt, texels, đặc biệt là khi nhiều thứ trong số này cần hoàn thành nhiều lần trong một giây thời gian thực, phản hồi tương tác.

Như đã nói, không có sự nhấn mạnh mạnh mẽ vào sự phức tạp thuật toán trong các cuộc thảo luận điển hình giữa các đồng nghiệp, có lẽ bởi vì nó phần nào được coi là hiển nhiên và khá "thô sơ". Thông thường, giả sử nếu bạn đang viết một trình theo dõi đường dẫn rằng nó sẽ hoạt động trong thời gian logarit hoặc tốt hơn và các cấu trúc dữ liệu như phân cấp âm lượng giới hạn là quen thuộc và tương đối tầm thường để thực hiện cho người đọc. Tôi thậm chí đã có một đồng nghiệp lành nghề, người luôn nói rằng đa luồng và SIMD quan trọng hơn thuật toán, và tôi không nghĩ rằng anh ta có nghĩa là bạn có thể mong đợi nhận được nhiều từ việc song song hóa một loại bong bóng. Tôi nghĩ rằng anh ấy đã nói rằng vì anh ấy đã chấp nhận rằng chúng tôi sẽ áp dụng các thuật toán hợp lý,

Ngày nay, rất nhiều sự tập trung vào việc sử dụng nhiều thuật toán quen thuộc này và làm cho chúng khai thác tốt hơn các đặc điểm cơ bản của phần cứng như bộ đệm CPU, các thanh ghi và hướng dẫn SIMD, GPU và nhiều lõi. Ví dụ, Intel đã đưa ra một cách mới để lấy BVH cũ quen thuộc và đưa ra khái niệm "gói tia", về cơ bản thử nghiệm nhiều tia kết hợp tại một thời điểm với một loại đi qua cây đệ quy (nghe có vẻ giống như nó 'đi kèm với sự phức tạp và chi phí chung của nó, ngoại trừ nó được tạo ra bởi thực tế là các tia đó hiện có thể được kiểm tra đồng thời đối với các tia / AABB và các giao điểm tia / tam giác thông qua các hướng dẫn và thanh ghi SIMD).

Điều tương tự với phân khu như catmull-clark, đó là thứ rất thô sơ trong đồ họa máy tính. Nhưng ngày nay, những gì cạnh tranh và hấp dẫn và siêu hiệu quả là các triển khai GPU xấp xỉ phân khu CC bằng cách sử dụng các bản vá lỗi của Gregory, được phổ biến bởi Charles Loop và sau đó được Pixar áp dụng. Việc triển khai CPU đơn giản hơn bây giờ khá lỗi thời, không nhất thiết là vì nó được thay thế về độ phức tạp thuật toán, nhưng vì nó được thay thế bởi thứ gì đó chơi tốt với GPU.

Và đó thường là rất nhiều thách thức ngày nay không đến với thuật toán tốt nhất theo cách tương đối độc lập với các đặc tính cơ bản của phần cứng. Tôi thực sự đã có được chân trong ngành bằng cách đưa ra một cấu trúc tăng tốc mới giúp tăng tốc đáng kể việc phát hiện va chạm để tạo hiệu ứng cho các nhân vật và các cơ thể mềm khác trong thập niên 90 bằng cách sử dụng phương pháp phân cấp theo thứ bậc trái ngược với chỉ số không gian, khiến tôi rất nhiều cung cấp công việc, nhưng ngày nay nó không còn ấn tượng nữa vì tôi đã xuất bản nó rất lâu trước khi chúng tôi có bộ nhớ CPU ấn tượng và nhiều lõi và GPU lập trình được và không, và hiện tại tôi sử dụng một cách tiếp cận hoàn toàn khác do kết quả của những thay đổi đáng kể đối với phần cứng cơ bản.


0

Tôi đã từng gặp phải một vấn đề trong đó một thuật toán thường chạy trong O (n), nhưng trong những trường hợp hiếm hoi và cực kỳ khó xảy ra sẽ cần thời gian O (n ^ 3) - trường hợp "hiếm" là một thư mục chứa các tệp có tên hợp lệ trong một hệ điều hành nhưng không phải trong một hệ điều hành khác.

Không ai từng gặp vấn đề. Sau đó, một khách hàng đã sử dụng chiến lược để đặt tên cho các tệp sẽ chạy một cách có hệ thống vào trường hợp O (n ^ 3) và với một vài 100 tệp có hệ thống đi vào bế tắc ảo. Kết quả là thuật toán đã được thay đổi.


0

Ba điều nữa chưa được đề cập:

1) Nhiều trò chơi chiến lược thời gian thực. Nhìn vào những người có đơn vị không thể chia sẻ một vị trí. Quan sát những gì xảy ra với đường dẫn khi một nhóm lớn các đơn vị di chuyển qua địa hình hạn chế. Tôi vẫn chưa gặp phải một trò chơi mà không có vấn đề gì đáng kể với điều này vì đơn giản là không có đủ năng lượng CPU.

2) Nhiều vấn đề tối ưu hóa. . theo thói quen đó, sau đó tôi nhận ra đó là 2 ^ n. Bây giờ nó là 2 ^ mặc dù điều đó đôi khi có thể tạo ra một kết quả không tối ưu.)

3) Những thứ phải hoạt động trên một lượng lớn dữ liệu trong thời gian thực. Xem xét DVD: Bạn thường nhận được 2 giờ video trong 4.7gb. Xem xét một tệp video điển hình ở cùng độ phân giải: 2 giờ video đó thường sẽ có dung lượng dưới 1gb. Lý do cho điều này là khi thông số DVD được đặt xuống, bạn không thể tạo một đầu phát DVD giá hợp lý có thể giải mã các định dạng hiện đại hơn đủ nhanh.


0

Chà, bất kỳ ứng dụng nào thường chạy trên siêu máy tính ( danh sách các máy lớn nhất ) đều đủ điều kiện. Chúng rất đa dạng, nhưng một lớp con lớn trong số chúng là mô phỏng vật lý:

  • Mô phỏng vật lý:
    • Dự báo thời tiết
    • Mô phỏng khí hậu
    • Mô phỏng các ngôi sao nổ tung, v.v.
    • Mô phỏng hạt nhân nổ
    • Mô phỏng khí động học của ô tô / máy bay / tàu hỏa, v.v.
    • ...
  • Tính toán hình ảnh từ dữ liệu kính viễn vọng vô tuyến
  • Ứng dụng sinh học:
    • Thứ với chuỗi DNA (Tôi không thực sự thích chúng)
    • Công cụ sinh hóa như gấp protein
    • Mô phỏng cách thức các tế bào thần kinh phối hợp với nhau để xử lý thông tin
    • Mô phỏng các tương tác phức tạp khác như hệ sinh thái
    • ...
  • ...

Đây chỉ là những chủ đề hàng đầu của tôi, nhưng chỉ cần đọc danh sách các siêu máy tính khác nhau và nhận ra rằng mỗi một trong số chúng đều được chế tạo để cho phép một số loại tính toán không thể có nếu không có những cỗ máy khổng lồ như vậy.

Và, một khi bạn thấy rằng chúng tôi thực sự cần những máy này, hãy nhận ra có thể tiết kiệm được bao nhiêu chi phí, chỉ bằng cách tăng tốc 10% cho ứng dụng này . Bất kỳ tối ưu hóa các mã này trực tiếp làm tăng số lượng kết quả mà chúng tôi có thể thoát khỏi các máy này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.