Saturday, December 2, 2017

Collections

Collections. Từ thuở xa xửa xa xưa có một con tàu vượt xuyên biển đại dương băng qua làn sương mù màu xám. Trên con thuyền chứa rất nhiều hàng hóa quý giá, trong đó có vàng, kim cương, ngọc đỏ. Bỗng dưng không khí lạnh dần và bắt đầu mưa.

(Ta coi con thuyền như là ngôn ngữ)


Class. là một khuôn mẫu. Các thể hiện của một lớp là các thể hiện của mẫu đó. Có rất nhiều thứ có thể được tạo ra với các Class Dưới đây là một số Class hàng đầu.

Buffer
Console
DataRow
DataSet
DataTable
DataView
Debug
Directory
Environment
Exception
File
FileInfo
HttpClient
IComparable
IEnumerable
IEqualityComparer
Object
Path
Process
Random
Regex
SqlClient
SqlConnection
SqlParameter
Stopwatch
StreamReader
StreamWriter
String
StringBuilder
StringReader
StringWriter
System
Task
Thread
Timer
TimeSpan
Type
XElement
XmlReader
XmlWriter


Collections ta coi như một kho báu, trong kho báu ấy bao gồm rất nhiều những đồng tiền vàng và trang sức đắt tiền. Ta có thể coi những trang sức vàng bạc đó là:

ArrayList
BitArray
ConcurrentBag
ConcurrentDictionary
ConcurrentQueue
ConcurrentStack
Dictionary
DictionaryEntry
HashSet
Hashtable
HybridDictionary
KeyValuePair
Lazy
LinkedList
List
ListDictionary
NameValueCollection
Queue
ReadOnlyCollection
SortedDictionary
SortedList
SortedSet
Stack
StringDictionary

Tuple


Classes, concepts Hầu hết mọi thứ trong C # liên quan đến các đối tượng. Một chuỗi là một đối tượng. Với một lớp, chúng ta tạo ra một kiểu mà khi khởi tạo trở thành một đối tượng.

Class
Class: Constructor
Class: Inheritance
Class: Nested
Class: Variable Initializer




Thursday, November 16, 2017

Thuật Toán Quay Lui Backtracking

Thuật Toán Quay Lui Backtracking

Quy lui là một kĩ thuật thiết kế giải thuật dựa trên đệ quy. Ý tưởng của quay lui là tìm lời giải từng bước, mỗi bước chọn một trong số các lựa chọn khả dĩ và đệ quy. Ok. Có vẻ vẫn chưa được rõ ràng lắm. Giờ chúng ta xem xét một vài ví dụ và khái quát hóa phương pháp quay lui.
Bài toán n quân hậu (n Queens)
Bài toán như sau:
Problem 1: Cho một bàn cờ hình vuông kích thước n×n và n quân hậu. Hãy tìm cách đặt n quân hậu trên bàn cờ sao cho không có 2 quân hậu nào có thể ăn được nhau.

Mã hóa lời giải: ta thấy để các quân hâu không ăn được nhau, ta phải xếp nquân hậu trên n hàng của bàn cờ. Ta dùng mảng Q[1,2,,n], trong đó Q[i]=j nếu quân hậu ở hàng thứ i được đặt ở cột j.
Ý tưởng chính của thuật toán: giả sử chúng ta đã đặt được r1 quân hậu, 1r<n, trên r1 hàng đầu tiên sao cho không có 2 quân hậu nào ăn được nhau. Cụ thể các phần tử Q[1,2,,r1] sẽ khác 0 và các phần tử Q[r,r+1.,n] đều bằng 0. Chúng ta tìm cách đặt một quân hậu trên hàng thứ r. Ta sẽ thử lần lượt đặt vào cột thứ 1,2,,n. Nếu đặt vào cột thứ j mà bị một trong r quân hậu đã đặt trước đó ăn, ta sẽ thử cột thứ j+1. Nếu ta tìm được một ví trí đặt khả dĩ (a feasible position), ta sẽ đặt vào đó và gọi đệ quy để đặt hàng thứ r+1. Giả mã như sau:
RecursiveQueen(Q[1,2,,n],r): 
    if(r=n+1)
        print Q
   else
        for j1 to n
            legal True
            for i1 to r1               check conflict 
                if (Q[i]=j) or (Q[i]=j+ri) or (Q[i]=jr+i)
                    legal False
            if legal= True
                Q[r]j
                RecursiveQueen(Q[1,2,,n],r+1)

Code của giả mã bằng C. Code đầy đủ được cho ở cuối bài.
Cây đệ quy là một cách nhìn khác của thuật toán. Tưởng tượng ta có một cây với gốc tượng trưng cho cấu hình ban đầu của bàn cờ (mảng Q[1,2,,n] gồm toàn 0). Nút con của gốc, gọi là nút level 1, là các cách đặt một quân hậu vào hàng thứ 1. Nút con của một nút level 1, gọi là nút level 2, là các cách đặt khả dĩ một quân hậu vào hàng thứ 2. Cứ như thế đến nút level n (tại sao chỉ có nlevel) ta sẽ thu được một cây gọi là cây đệu quy. Có thể thấy rằng thuật toán quay lui ở trên thực ra chính là một cách duyệt cây theo chiều sâu cho đến khi tìm được một nút ở level n. Hình minh họa sau với n=4 được lấy từ [1]

Có thể thấy thuật toán trên có thời gian cỡ O(n!). Tuy nhiên nếu bạn cần một lời giải bất kì, bài toán này có công thức giải và bạn chỉ mất thời gian để in lời giải ra (O(n))[3].
Quay Lui

Sudoku
Sudoku là một trò chơi khá phổ biến và chắc ai (readers of this bog) cũng biết. Trò chơi như sau: có một hình vuông được chia thành 9x9 ô vuông con. Mỗi ô vuông con có giá trị trong khoảng từ 1 đến 9. Ban đầu hình vuông có một số ô vuông con cho trước (có điền sẵn số) và còn lại là trống. Hãy điền các số từ 1-9 vào các ô con lại sao cho: hàng ngang là các số khác nhau từ 1 đến 9, hàng dọc là các số khác nhau từ 1 đến 9, và mỗi khối 3x3 chính là các số khác nhau từ 1 đến 9. Ví dụ một câu đố và lời gải tương ứng như sau (hình được lấy từ wikipedia):

Sudoku

Trong bài này mình sẽ giới thiệu cách giải sudoku bằng thuật toán quay lui. Ý tưởng của thuật toán cũng giống bài toán n quân hậu. Mỗi bước tìm tập các giá trị khả dĩ để điền vào ô trống, và sau đó đệ quy để điền ô tiếp theo. Giả mã của thuật toán (ở đây chú ý mảng chỉ có kích thước 9×9). Thủ tục Feasible(S,x,y,k) kiểm trả xem giá trị k có khả dĩ với ô S[x][y] không.
Sudoku(S[1,2,,9][1,2,,9],x,y): 
    if y=10
        if x=9
            print S
       else
            Sudoku(S[1,2,,9][1,2,,9],x+1,1)
    else if S[x,y]=
        for k1 to 9
            if Feasible(S,x,y,k)
                S[x,y]k
                Sudoku(S[1,2,,9][1,2,,9],x,y+1)
                S[x,y]         for next branching 
    else                                    S[x,y] is given 
        Sudoku(S[1,2,,9][1,2,,9],x,y+1)

Code của giả mã trên bằng C:
Giả mã của thủ tục Feasible(S,x,y,k) như sau:
Feasible(S[1,2,,9][1,2,,9],x,y,k): 
    for i1 to 9
        if S[x,i]=k
            return False
    for i1 to 9
        if S[i,y]=k
            return False
    a(x1)/3,b(y1)/3
    for i3a+1 to 3a+3
        for j3b+1 to 3b+3
           if S[i,j]=k
                return False
    return True

Code bằng C của giả mã (code đầy đủ được cho ở cuối bài):
Subset Sum
Subset Sum là một trong những bài toán NP-hard. Trong bài này chúng ta sẽ thiết kế thuật toán quay lui để giải bài toán Subset Sum.
Problem 2: Cho một mảng n phần tử X[1,2,,n] và một số T. Có tồn tại một tập con các phần tử của mảng X sao cho tổng của chúng bằng T.

Ví dụ: X={8,6,7,5,3,10,9} và T=12. Lời giải là true vì tập con {7,5} của X có tổng bằng 12.
Ý tưởng của thuật toán quay lui dựa trên quan sát sau: xét một phần tử xX, tồn tại một dãy con có tổng bằng T nếu một trong hai điều kiện sau là đúng:
  1. Tồn tại một tập con của X{x} có tổng bằng Tx
  2. Tồn tại một tập con của X{x} có tổng bằng T
Giả mã như sau:
SubsetSum(X[1,2,,n],r,T): 
    if T=0
        return True
    else if T > 0 or n==0
        return False
    return SubsetSum(X[1,2,,n1],T) Or
                SubsetSum(X[1,2,,n1],TX[n])

Code của giả mã bằng C:
Phân tích thời gian: Ở mỗi bước của thuật toán quay lui, ta gọi đệ quy hai lần trên mảng con của X với kích thước nhỏ hơn 1. Ta có:
T(n)=2T(n1)+O(1)=O(2n)

Tài liệu tham khảo
[1] J. Erickson. Algorithm Lecture Notes, UIUC.
[2] n queen on stackexchange: http://cstheory.stackexchange.com/questions/12682/is-the-n-queens-problem-np-hard
[3] B. Bernhardsson, Explicit Solutions to the N-queens Problem for All N, SIGART Bull. 2(2)(1991)7.
Bài Tập
Bài 1 (Longest Increasing Subsequence) Cho một mảng n phần tử A[1,2,,n]. Tìm một dãy dài nhất (k lớn nhất) các chỉ số 1i1 < i2<<ikn sao cho A[i1]A[i2]A[ik] bằng phương pháp quay lui. (Gợi ý: xét A[1], nếu A[1] nằm trong dãy con tăng, đệ quy trên A[2,3,] tìm dãy con tăng dài nhất mà mọi phần tử đều lớn hoưn A[1]. Nếu A[1] không nằm trong dãy con tăng, đệ quy trên A[2,3,,n]).
Bài 2 (Dãy gia tốc) Một dãy X[1,2,,n] gọi là gia tốc nếu 2X[i] > X[i1]+X[i+1]. Cho một dãy A[1,2,,n], tìm dãy con gia tốc dài nhất của A. Tìm công thức đệ quy cho bài toán, dựa vào đó thiết kế giải thuật quay lui.
Bài 3 Hãy sửa đổi thuật toán quay lui ở trên của bài toán Subset Sum để in ra ít nhất một dãy con có tổng bằng T của X.