HỌC VIỆN KỸ THUẬT QUÂN SỰ
KHOA CÔNG NGHỆ THÔNG TIN
BÁO CÁO MÔN HỌC
TRÍ TUỆ NHÂN TẠO
Giáo viên hướng dẫn: Ngô Hữu Phúc
HÀ NỘI 3/2010
I. Thuật toán di truyền.
Thuật toán di truyền (TTDT) là thuật toán bắt chước sự chọn lọc tự nhiên và di truyền. Trong tự
nhiên, các cá thể khỏe, có khả năng thích nghi tốt với môi trường sẽ được tái sinh và nhân bản ở
các thế hệ sau. Mỗi cá thể có cấu trúc gien đặc trưng cho phẩm chất của cá thể đó. Trong quá
trình sinh sản, các cá thể con có thể thừa hưởng các phẩm chất của cả cha và mẹ, cấu trúc gien
của nó mang một phần cấu trúc gien của cha và mẹ. Ngoài ra, trong quá trình tiến hóa, có thể xảy
ra hiện tượng đột biến, cấu trúc gien của cá thể con có thể chứa các gien mà cả cha và mẹ đều
không có.
Trong TTDT, mỗi cá thể được mã hóa bởi một cấu trúc dữ liệu mô tả cấu trúc gien của cá thể đó,
ta sẽ gọi nó là nhiễm sắc thể (chroniosome). Mỗi nhiễm sắc thể được tạo thành từ các đơn vị
được gọi là gien. Chẳng hạn, trong các TTDT cổ điển, các nhiễm sắc thể là các chuỗi nhị phân,
tức là mỗi cá thể được biểu diễn bởi một chuỗi nhị phân.
TTDT sẽ làm việc trên các quần thể gồm nhiều cá thể. Một quần thể ứng với một giai đoạn phát
triển sẽ được gọi là một thế hệ. Từ thế hệ ban đầu được tạo ra, TTDT bắt chước chọn lọc tự
nhiên và di truyền để biến đổi các thế hệ. TTDT sử dụng các toán tử cơ bản sau đây để biến đổi
các thế hệ.
• Toán tử tái sinh (reproduction) (còn được gọi là toán tử chọn lọc (selection)). Các cá thể
tốt được chọn lọc để đưa vào thế hệ sau. Sự lựa chọn này được thực hiện dựa vào độ thích nghi
với môi trường của mỗi cá thể. Ta sẽ gọi hàm ứng mỗi cá thể với độ thích nghi của nó là hàm
thích nghi (fitness function).
• Toán tử lai ghép (crossover). Hai cá thể cha và mẹ trao đổi các gien để tạo ra hai cá thể
con.
• Toán tử đột biến (mutation). Một cá thể thay đổi một số gien để tạo thành cá thể mới.
Tất cả các toán tử trên khi thực hiện đều mang tính ngẫu nhiên.
**Các bước cơ bản của giải thuật di truyền:
1. Khởi tạo 1 quần thể ban đầu gồm các chuỗi nhiễm sắc thể.
2. Xác định giá trị mục tiêu cho từng nhiễm sắc thể tương ứng.
3. Tạo các nhiễm sắc thể mới dựa trên các toán tử di truyền.
4. Loại bớt các nhiễm sắc thể có độ thích nghi thấp.
5. Xác định hàm mục tiêu cho các nhiễm sắc thể mới và đưa vào quần thể.
6. Kiểm tra thỏa mãn điều kiện dừng.Nếu điều kiện đúng, lấy ra nhiễm sắc thể tốt nhất, giải
thuật dừng lại; ngược lại, quay về bước 3.
**Cấu trúc cơ bản của TTDT là như sau:
Simple Genetic Algorithm{}
{
Initialize the Population;
Calculate Fitness Function;
While (Fitness != Optimal Value)
{
Selection
Crossover
Mutation
Calculate Fitness Function
}
}
Trong thủ tục trên, điều kiện kết thúc vòng lặp có thể là một số thế hệ đủ lớn nào đó, hoặc độ
thích nghi của các cá thể tốt nhất trong các thế hệ kế tiếp nhau khác nhau không đáng kể. Khi
thuật toán dừng, cá thể tốt nhất trong thế hệ cuối cùng được chọn làm nghiệm cần tìm.
Ta xét chi tiết cách mã hóa,toán tử chọn lọc và các toán tử di truyền (lai ghép, đột biến) trong các
TTDT:
1.Mã hóa:
Mã hóa nhị phân
Là dạng mã hóa phổ biến nhất và cách mã hóa chuỗi bit thay đổi tùy theo vấn đề.
Làm việc với integer tốt hơn floating point.
*Ưu điểm:
Dễ giao phối. (mating)
Biểu diễn nhiễm sắc thể với 1 số ít gen.
*Khuyết điểm:
Không thể giới hạn các thay đổi dễ dàng.
Tiến hành hiệu chỉnh sau khi mate.
VD: A: 0101101100010011
B: 1011010110110101
Mã hóa hoán vị
Nhiễm sắc thể là 1 chuỗi số thực, được biểu diễn theo thứ tự.
*Ưu điểm: hữu dụng cho các vấn đề thứ tự.
*Khuyết điểm: có thể phải hiệu chỉnh sau khi mate.
VD: A:8549102367
B: 9102438576
Mã hóa giá trị
Mỗi nhiễm sắc thể là chuỗi giá trị.
Giá trị là bất cứ thứ gì liên quan đến vấn đề như: số, ký tự, đối tượng…
VD:
A: [red], [black], [blue], [yellow], [red], [green]
B: 1.8765, 3.9821, 9.1283, 6.8344, 4.116, 2.192
C: ABCKDEIFGHNWLSWWEKPOIKNGVCI
Mã hóa cây
Được dùng chủ yếu để rút ra các biểu thức chương trình
*Ưu điểm: biểu diễn không gian tìm kiếm không giới hạn (open-ended)
*Khuyết điểm: xảy ra lỗi tiềm tàng,phát triển theo hướng không thể kiểm soát, khi phát
triển lớn thì rất khó hiểu và không để đơn giản hóa
2. Chọn lọc: Việc chọn lọc các cá thể từ một quần thể dựa trên độ thích nghi của mỗi cá thể. Các
cá thể có độ thích nghi cao có nhiều khả năng được chọn. Cần nhấn mạnh rằng, hàm thích nghi
chỉ cần là một hàm thực dương, nó có thể không tuyến tính, không liên tục, không khả vi. Quá
trình chọn lọc được thực hiện theo kỹ thuật quay bánh xe.
Giả sử thế hệ hiện thời P(t) gồm có n cá thể {x
1
, ,x
n
}. Số n được gọi là cỡ của quần thể. Với mỗi
cá thể x
i
, ta tính độ thích nghi của nó f(x
i
). Tính tổng các độ thích nghi của tất cả các cá thể trong
quần thể:
Mỗi lần chọn lọc, ta thực hiện hai bước sau:
• Sinh ra một số thực ngẫu nhiên q trong khoảng (0, F);
• x
k
là cá thể được chọn, nếu k là số nhỏ nhất sao cho
Việc chọn lọc theo hai bước trên có thể minh họa như sau: Ta có một bánh xe được chia thành n
phần, mỗi phần ứng với độ thích nghi của một cá thể (hình 3.5). Một mũi tên chỉ vào bánh xe.
Quay bánh xe, khi bánh xe dừng, mũi tên chỉ vào phần nào, cá thể ứng với phần đó được chọn.
Rõ ràng là với cách chọn này, các cá thể có thể có độ thích nghi càng cao càng có khả năng được
chọn. Các cá thể có độ thích nghi cao có thể có một hay nhiều bản sao, các cá thể có độ thích
nghi thấp có thể không có mặt ở thế hệ sau.
3. Lai ghép: Trên cá thể được chọn lọc, ta tíến hành toán tử lai ghép. Đầu tiên ta cần đưa ra xác
suất lai ghép p
c
. xác suất này cho ta hy vọng có p
c
.n cá thể được lai ghép (n là cỡ của quần thể).
Với mỗi cá thể ta thực hiện hai bước sau:
• Sinh ra số thực ngẫu nhiên r trong đoạn [0, 1];
• Nếu r < p
c
thì cá thể đó được chọn để lai ghép
Từ các cá thể được chọn để lai ghép, người ta cặp đôi chúng một cách ngẫu nhiên.Trong
trường hợp các nhiễm sắc thể là các chuỗi nhị phân có độ dài cố định m, ta có thể thực
hiện lai ghép như sau: với mỗi cặp,sinh ra một số nguyên ngẫu nhiên p trên đoạn [0,m-1],
p là vị trí điểm ghép. Cặp gồm 2 nhiễm sắc thể:
∑
=
=
n
1i
f(xi)F
∑
=
≥
k
i
xif
1
4)(
a = (a
1,
, a
p,
a
p+1,
, a
m
)
a = (b
1,
, b
p,
b
p+1,
, b
m
)
được thay bởi 2 con là:
a' = (a
1,
, a
p,
b
p+1,
, b
m
)
b' = (b
1,
, b
p,
a
p+1,
, a
m
)
4. Đột biến: Ta thực hiện toán tử đột biến trên các cá thể có được sau quá trình lai ghép. Đột biến
là thay đổi trạng thái một số gien nào đó trong nhiễm sắc thể. Mỗi gien chịu đột biến với xác suất
p
m
. Xác suất đột biến p
m
do ta xác định và là xác suất thấp. Sau đây là toán tử đột biến trên các
nhiễm sắc thể chuỗi nhị phân.
Với mỗi vị trí i trong nhiễm sắc thể:
a = (a
1,
, a
i,
, a
m
)
Ta sinh ra một số thực nghiệm ngẫu nhiên p
i
trong [0,1]. Qua đột biến a được biến thành a’ như
sau:
a' = (a'
1,
, a'
i,
, a'
m
)
Trong đó:
a'
i
= a
i
nếu p
i
≥ p
m
1 - a
i
nếu p
i
< p
m
Sau quá trình chọn lọc, lai ghép, đột biến, một thế hệ mới được sinh ra. Công việc còn lại của
thuật toán di truyền bây giờ chỉ là lặp lại các bước trên.
*** Đặc điểm hội tụ của thuật toán
Khi áp dụng thuật toán di truyền cho các vấn đề thực tế thường rất khó khăn.Nguyên
nhân:
+ Cách biểu diễn gene có thể tạo ra không gian tìm kiếm khác với không gian thực của bài toán.
+ Số bước lặp khi cài đặt thường không xác định trước.
+ Kích thước quần thể thường có giới hạn.
Trong một số trường hợp, thuật toán không thể tìm được lời giải tối ưu do hội tụ sớm về
lời giải tối ưu cục bộ. Hội tụ sớm là vấn đề của thuật toán di truyền cũng như các giải thuật tối
ưu khác. Nếu hội tụ xảy ra quá nhanh thì các thông tin đáng tin cậy đang phát triển trong quần
thể thường bị bỏ qua. Nguyên nhân của sự hội tụ sớm liên quan tới 2 vấn đề:
+ Quy mô và loại sai số do cơ chế tạo mẫu.
+ Bản chất của hàm mục tiêu.
**Điều kiện dừng của giải thuật:
Có hai loại điều kiện dừng cơ bản. Các điều kiện này dùng các đặc trưng tìm kiếm để
quyết định ngừng quá trình tìm kiếm.
Dựa trên cấu trúc nhiễm sắc thể: do sự hội tụ của quần thể bằng cách kiểm soát số
alen được hội tụ, ở đây alen được coi như hội tụ nếu 1 số phần trăm quần thể đã định
trước có cùng (hoặc tương đương đối với các biểu diễn không nhị phân) giá trị trong alen
này.Nếu số alen hội tụ vượt quá số phần trăm nào đó của tổng số alen, việc tìm kiếm sẽ
kết thúc.
Dựa trên ý nghĩa đặc biệt của 1 nhiễm sắc thể: đo tiến bộ của giải thuật trong một số
thế hệ cho trước.Nếu tiến bộ này nhỏ hơn một hằng số ɛ xác định,kết thúc tìm kiếm.
II Chương trình.
1. Class Knapsack.
*Properties:
+ knapsackMaxWeight: khối lượng tối đa của balô.
+ knapsackCurrentWeight: khối lượng hiện thời của balô.
+ knapsackValue: giá trị của balô.
+ fitness: chỉ số Fitness
*Method:
+ Overload: để kiểm tra xem khối lượng hiện tại của túi đã vượt quá khối lượng
max chưa.
+ ShowItem: in số lượng các đồ vật trong túi dưới dạng gene.
public class Knapsack
{
private int knapsackMaxWeight, knapsackCurrentWeight = 0, knapsackValue = 0, fitness;
private Item[] listItem = null;
public int[] listItemAmount;
public string strItems = "";
public int Fitness
{
get { return fitness; }
}
public int KnapsackCurrentWeight
{
get { return knapsackCurrentWeight; }
}
public int KnapsackValue
{
get { return knapsackValue; }
}
public Item[] ListItem
{
get { return listItem; }
set
{
listItem = value;
}
}
public Knapsack(int knapsackMaxWeight)
{
this.knapsackMaxWeight = knapsackMaxWeight;
}
public Knapsack(int knapsackMaxWeight, Item[] listItem)
{
this.knapsackMaxWeight = knapsackMaxWeight;
this.listItem = listItem;
listItemAmount = new int[ListItem.Length];
int i = 0;
foreach (Item item in listItem)
{
knapsackCurrentWeight += (item.ItemWeight * item.ItemAmount);
knapsackValue += (item.ItemValue * item.ItemAmount);
listItemAmount[i] = item.ItemAmount;
i++;
}
if (!overload()) fitness = knapsackValue;
else
{
fitness = knapsackValue - knapsackCurrentWeight;
}
}
public Boolean overload()
{
if (knapsackCurrentWeight > knapsackMaxWeight) return true;
else return false;
}
public string showItem()
{
string s = "";
int i;
for (i = 0; i < listItemAmount.Length - 1; i++)
s += listItemAmount[i] + ", ";
s += listItemAmount[i].ToString();
return s;
}
}
2.Class Item.
*Properties
itemValue: giá trị của đồ vật.
itemWeight: khối lượng của đồ vật.
itemAmount: số lượng đồ vật.
public class Item
{
private int itemValue, itemWeight, itemAmount;
public int ItemWeight
{
get { return itemWeight; }
set { itemWeight = value; }
}
public int ItemValue
{
get { return itemValue; }
set { itemValue = value; }
}
public int ItemAmount
{
get { return itemAmount; }
set { itemAmount = value; }
}
public Item(int itemValue, int itemWeight)
{
this.itemValue = itemValue;
this.itemWeight = itemWeight;
}
}
3.Class Gas.
public class GAs
{
// mỗi Knapsack trong ListKnapsack là 1 quần thể riêng biệt (trong 1 thế hệ)***//
//properties
private Random rand;
public int knapsackWeight,knapsackAmount;
private int maxValue = 0;
private int mutationProp;
private Item[] listItem;
public Knapsack[] listKnapsack;
public Knapsack ksBestCurrent;
//constructor
public GAs(int knapsackWeight, int knapsackAmount, Item[] listItem, int mutationProp, int
TotalGeneration)
{
this.knapsackWeight = knapsackWeight;
this.knapsackAmount = knapsackAmount;
this.mutationProp = mutationProp;
this.listItem = listItem;
rand = new Random();
initialize();
selectionForNewGeneration(listKnapsack);
}
//method
private void initialize()
{
// Thế hệ đầu tiên: tất cả các gene đều được chấp nhận
int i = 0;
listKnapsack = new Knapsack[knapsackAmount];
Knapsack knapsack;
while (i < knapsackAmount)
{
Item[] newList = listItem;
foreach (Item item in newList)
{
item.ItemAmount = rand.Next(0, Convert.ToInt32(knapsackWeight /
(item.ItemWeight)));
}
knapsack = new Knapsack(knapsackWeight, newList);
if ((!knapsack.overload()) && (knapsack.KnapsackValue > 0))
{
listKnapsack[i] = knapsack;
if (knapsack.KnapsackValue > maxValue)
{
maxValue = knapsack.KnapsackValue;
ksBestCurrent = knapsack;
}
i++;
}
}
for (i = 0; i < listKnapsack.Length; i++)
{
System.Windows.Forms.MessageBox.Show(listKnapsack[i].showItem());
}
}
public void selectionForNewGeneration(Knapsack[] oldListKnapsack)
{
for (int i = 0; i < oldListKnapsack.Length; i++)
{
if (oldListKnapsack[i] == null) break;
for (int j = i; j < oldListKnapsack.Length; j++)
{
if (oldListKnapsack[j] == null) break;
Knapsack ksTemp;
if (oldListKnapsack[i].Fitness > oldListKnapsack[j].Fitness)
{
ksTemp = oldListKnapsack[i];
oldListKnapsack[i] = oldListKnapsack[j];
oldListKnapsack[j] = ksTemp;
}
}
}
int count = 0;
while (count < knapsackAmount)
{
listKnapsack[count++] = oldListKnapsack[oldListKnapsack.Length - 1 -
knapsackAmount + count];
}
}
public Knapsack[] selectionForRecombination()
{
int fitnessTotal = 0, i = 0, times;
foreach (Knapsack knapsack in listKnapsack)
{
if (knapsack != null)
fitnessTotal += knapsack.Fitness;
}
times = rand.Next(knapsackAmount - 10, knapsackAmount + 10);
if (times < 0) times = knapsackAmount;
if (times % 2 != 0) times += 1;
Knapsack[] selectedKnapsacks = new Knapsack[times];
while (i < times)
{
int randNumber = rand.Next(1, fitnessTotal);
int currentTotal = 0, pos = 0;
while (currentTotal <= randNumber)
{
currentTotal += listKnapsack[pos++].Fitness;
}
if (i != 0 && selectedKnapsacks[i - 1].Fitness != listKnapsack[pos - 1].Fitness)
{
i++;
}
selectedKnapsacks[i] = listKnapsack[pos - 1];
}
return selectedKnapsacks;
}
public int[,] encoding(Knapsack[] selectedKnapsacks)
{
int[,] encodedListKnapsack = new int[selectedKnapsacks.Length, listItem.Length];
for (int i = 0; i < selectedKnapsacks.Length; i++)
{
if (selectedKnapsacks[i] == null) break;
for (int j = 0; j < selectedKnapsacks[i].ListItem.Length; j++)
{
if (selectedKnapsacks[i].ListItem[j] == null) break;
encodedListKnapsack[i, j] = selectedKnapsacks[i].listItemAmount[j];
}
}
return encodedListKnapsack;
}
public int[,] crossover(int[,] encodedListKnapsack)
{
int[,] encodedNewGeneration = new int[encodedListKnapsack.GetLength(0),
encodedListKnapsack.GetLength(1)];
int startPart2 = encodedListKnapsack.GetLength(1) / 4,
startPart3 = startPart2 * 3;
for (int i = 0; i < encodedListKnapsack.GetLength(0) - 1; i += 2)
{
for (int j = 0; j < encodedListKnapsack.GetLength(1); j++)
{
if (j < startPart2)
{
encodedNewGeneration[i, j] = encodedListKnapsack[i, j];
encodedNewGeneration[i + 1, j] = encodedListKnapsack[i + 1, j];
}
else if (j < startPart3)
{
encodedNewGeneration[i, j] = encodedListKnapsack[i + 1, j];
encodedNewGeneration[i + 1, j] = encodedListKnapsack[i, j];
}
else
{
encodedNewGeneration[i, j] = encodedListKnapsack[i, j];
encodedNewGeneration[i + 1, j] = encodedListKnapsack[i + 1, j];
}
}
}
return encodedNewGeneration;
}
public int[,] mutation(int[,] crossoverListKnapsack)
{
Random rand = new Random();
int times = mutationProp * crossoverListKnapsack.GetLength(0) / 100;
for (int i = 0; i < times; i++)
{
int chooseInvidiual = rand.Next(0, crossoverListKnapsack.GetLength(0) - 1);
int choosePosition = rand.Next(0, crossoverListKnapsack.GetLength(1) - 1);
crossoverListKnapsack[chooseInvidiual, choosePosition] = 6;
}
return crossoverListKnapsack;
}
public Knapsack[] evaluation(int[,] encodedListKnapsack)
{
Knapsack[] newListKnapsack = new Knapsack[knapsackAmount +
encodedListKnapsack.GetLength(0)];
int i, j;
for (i = 0; i < encodedListKnapsack.GetLength(0); i++)
{
Knapsack knapsack = new Knapsack(knapsackWeight);
knapsack.ListItem = listItem;
for (j = 0; j < encodedListKnapsack.GetLength(1); j++)
{
knapsack.ListItem[j].ItemAmount = encodedListKnapsack[i, j];
}
newListKnapsack[i] = knapsack;
if (!knapsack.overload())
{
if (knapsack.KnapsackValue > maxValue)
{
maxValue = knapsack.KnapsackValue;
ksBestCurrent = knapsack;
}
}
}
j = 0;
while (i < encodedListKnapsack.GetLength(0) + knapsackAmount)
{
newListKnapsack[i++] = listKnapsack[j++];
}
return newListKnapsack;
}
public void gene(Knapsack[] oldListKnapsack, int times)
{
while (times > 0)
{
selectionForNewGeneration(oldListKnapsack);
times ;
gene(evaluation(mutation(crossover(encoding(selectionForRecombination())))), times);
}
}
III.Kết quá thu được từ chương trình.
Đồ án này chỉ giới hạn trong việc minh họa cho thuật toán Gene với phương pháp chọn bánh xe
Roulette cho bài toán balo2, chứ chưa hướng tới việc áp dụng trong thực tế.
Để áp dụng cho thực tế thì sẽ cần nhiều thời gian và quá trình tìm hiểu sâu hơn, sau khi kết thúc
môn học này, em mong thầy có thể giúp đỡ em để hoàn thành chương trình 1 cách hoàn thiện
hơn.
Em xin cám ơn !
IV.Tài liệu tham khảo:
Slide và bài giảng của thầy Ngô Hữu Phúc