人工智能课程报告--分别用宽度优先、深度优先、贪婪算法和A_算法求解“罗马利亚度假问题”

人工智能课程报告--分别用宽度优先、深度优先、贪婪算法和A_算法求解“罗马利亚度假问题” 本文关键词:算法,优先,罗马,求解,人工智能

人工智能课程报告--分别用宽度优先、深度优先、贪婪算法和A_算法求解“罗马利亚度假问题” 本文简介:人工智能课程报告课程:人工智能实验报告班级:191121班学号:20121004362学生姓名:李华勇指导教师:赵曼2014年11月目录一、罗马利亚度假问题31.问题描述32.数据结构42.1广度优先算法42.2深度优先算法42.3贪婪算法42.4A*算法43.算法思想53.1广度优先搜索算法53.

人工智能课程报告--分别用宽度优先、深度优先、贪婪算法和A_算法求解“罗马利亚度假问题” 本文内容:

人工智能课程报告

程:

人工智能实验报告

级:

191121班

号:

20121004362

学生姓名:

指导教师:

2014年11月

目录

一、罗马利亚度假问题3

1.

问题描述3

2.

数据结构4

2.1

广度优先算法4

2.2

深度优先算法4

2.3

贪婪算法4

2.4

A*算法4

3.

算法思想5

3.1

广度优先搜索算法5

3.2

深度优先搜索算法5

3.3

贪婪算法6

3.4

A*算法6

4.

运行结果7

5.

比较讨论8

6.

主要代码8

二、N皇后问题13

1.问题描述13

2.数据结构13

2.1

回溯法(递归)13

2.2

GA算法13

2.3

CSP的最小冲突法13

3.算法思想14

3.1

回溯法(递归)14

3.2

CSP的最小冲突法14

3.3

GA算法15

4.运行结果16

5.比较讨论17

6.主要代码18

一、罗马利亚度假问题

题目:

分别用宽度优先、深度优先、贪婪算法和A*算法求解“罗马利亚度假问题”。

要求:

分别用文件存储地图和启发函数表,用生成节点数比较几种算法在问题求解时的效率,并列表给出结果。

1.

问题描述

从文件中读取图和启发函数,分别用广度优先、深度优先、贪婪算法、A*算法得到从起始点Arad到目标点Bucharest的一条路径,即为罗马尼亚问题的一个解。在求解的过程中记录生成扩展节点的个数(用于比较几种算法的优劣),用堆栈记录DepthFSearch和BroadFSearch的路径。

2.

数据结构

分别使用了图结构,顺序队列,顺序表以及堆栈。对于每一个图中的结点,定义了一个结构体HeuristicG,结构体中包含结点的名称以及对应的启发函数值。

typedef

struct{

char

G[20];

int

value;

}HeuristicG;

typedef

struct

//图结构:

typedef

struct

//链表

{

{

SeqList

Vertices;

string

list[20];

int

edge[20][20];

int

size;

int

numedge;

}SeqList;

}AdjMGraph;

typedef

struct

//队列

typedef

struct

//栈

{int

queue[20];

{

int

rear;

int

stack[20];

int

front;

int

top;

int

count;

}SeqStack;

}SeqCQueue;

2.1

广度优先算法

使用了数据结构中的图、队列和堆栈。

2.2

深度优先算法

使用了数据结构中的图和堆栈。

2.3

贪婪算法

使用了数据结构中的图。

2.4

A*算法

使用了数据结构中的图。

3.

算法思想

3.1

广度优先搜索算法

void

BF_Search(AdjMGraph

G,HeuristicG

data[],int

v0,int

vg,intExpand,int

count,int

visited[],int

BFS_path[])

v0---起始节点

vg---目标节点

Expand---返回扩展结点数

count---返回路径结点数

BFS_path[]---存储路径信息

G、data[]---用来读取图的各种信息

visited[]---用来存储节点是否已经被访问的信息

广度优先搜索是分层搜索的过程,广度优先搜索算法思想是用一个队列来保存访问过的顶点的顺序,以便按顺序访问这些顶点的邻接顶点,并把这些邻接顶点依次入栈。在函数中我还创建了两个栈,SeqStack

SaveQ,LineSave,SaveQ将出队列的节点全部进栈,LineSave用于保存路径。广度优先搜索算法如下:

1)

访问顶点v0并标记v0已被访问,把v0输出到屏幕。

2)

顶点v0入队列。

3)

若队列非空,则继续执行,否则算法结束。

4)

出队列取得队头顶点u,并把u入SaveQ栈。

5)

查找顶点u的第一个邻接顶点w。

6)

如果w

=

vg,即找到目标节点,算法结束。

7)

若顶点u的邻接顶点w不存在,则转到步骤3),否则循环执行:

(a)若顶点w尚未被访问,则访问顶点w并标记w为已访问;

(b)顶点w入队列,Expand++;

(c)查找顶点u的w邻接顶点后的下一个邻接顶点w,转到步骤7)。

广度优先搜索起始节点到目标节点的路径的算法是:

1)

把顶点vg以及vg的父节点u入栈。

2)

把SaveQ栈顶元素出栈到u,当SaveQ非空是执行以下步骤:

(a)把SaveQ栈顶元素出栈到u

(b)取LineSave栈顶元素给y。

(c)如果u和y没有相同的父亲,没被访问过,并且之间有边则保存路

径,把u压入LineSave栈。

3.2

深度优先搜索算法

void

DF_Search(AdjMGraph

G,SeqStack

S,HeuristicG

data[],int

Expand,int

v0,int

vg,int

visited[])

v0---起始节点

vg---目标节点

Expand---返回扩展结点数

SeqStack

S---用堆栈存储路径信息

visited[]---存储路径是否被访问的信息

G、data[]---用来读取图的各种信息

深度优先搜索的算法思想是用栈来保存已经访问过的节点,递归找该节点的第一个邻接顶点并把把顶点入栈,直到找不到顶点的邻接顶点为止,然后回溯,找该顶点父顶点的下一个邻接顶点。

使用深度优先搜索算法,每次都在访问完当前顶点后首先访问当前顶点的第一个邻接顶点。深度优先搜索算法如下:

1)

访问顶点v并标记顶点v为以访问,把v0压入栈S,并在屏幕上输出v。

2)

如果v0!=

-1,查找顶点v的第一个邻接顶点w

3)

若顶点v的邻接顶点w存在且为被访问,则继续执行,否则算法结束。

4)

若果w

=

vg,即找到目标节点,算法结束。

5)

弹出S栈顶元素。

6)

查找顶点v的w邻接顶点的下一个邻接顶点w,转到步骤3)。

3.3

贪婪算法

void

Greedy_Search(AdjMGraph

G,HeuristicG

data[],int

v0,int

vg,intExpand,int

visited[])

v0---起始节点

vg---目标节点

Expand---返回扩展结点数

G、data[]---用来读取图的各种信息

贪心算法思想是找到当前顶点的所有邻接顶点中h(x)值最小的邻接顶点,并把该顶点设置为下一个起始节点,递归直到找到目标节点。算法如下:

1)

访问v0,并将v0设为以访问,把v0输出到屏幕。

2)

如果v0

=

vg,即找到目标节点,算法结束。

3)

找到v0的所有邻接顶点,并比较邻接顶点的h(x)值,把h(x)最小

的邻接顶点w,把w设置为起始顶点v0,转到步骤1)。

3.4

A*算法

void

A_Search(AdjMGraph

G,HeuristicG

data[],int

v0,int

vg,int

distance,intExpand,int

visited[])

v0---起始节点

vg---目标节点

distance---用来保存已经过路径值

Expand---返回扩展结点数

G、data[]---用来读取图的各种信息

A*算法思想是找到起始节点v0的所有邻接顶点,比较所有邻接顶点的fx值(fx

=

到v0已经经过路径值+v0到邻接顶点w的边的路径值distance+邻接顶点w的hx值),找到fx最小的邻接顶点w作为下一个起始顶点v0,同时更新距离diatance

=

diatance

+

v0到w边的路径值,直到找到目标节点。算法如下:

1)访问v0,并将v0设为以访问,把v0输出到屏幕。

2)如果v0

=

vg,即找到目标节点,算法结束。

3)找到v0的所有邻接顶点w,并比较所有邻接顶点的fx值,

fx=ditance+v0到w的距离+w的启发函数值,找到fx最小的邻接顶点w

令v0

=

w,更新distance

=

distance

+

edge[v0][w],转到步骤1)。

4.

运行结果

深度优先搜索

宽度优先搜索

A*算法

贪婪算法

扩展节点数

12

11

5

4

路径节点数

5

4

5

4

5.

比较讨论

从运行结果中可以看出,在搜索的过程中:

DFS算法扩展结点数为12

BFS算法扩展结点数为11

A*算法扩展结点数为5

贪婪算法扩展结点数为4

所以在求解该问题时,贪婪算法的效率最高,其次是A*算法,然后是BFS算法,最后是DFS算法。但是贪婪算法和A*算法生成的节点数依赖于启发函数的值,因此虽然对于本题来说贪婪算法和A*算法的效率很高,但是不能说在所有搜索问题中贪婪算法和A*算法的效率都是最高的。

6.

主要代码

1)深度优先搜索

//

v0---起始节点

vg---目标节点

//

//

Expand---返回扩展结点数

//

//

SeqStack

S---用堆栈存储路径信息

//

//

visited[]---存储路径访问信息

//

//

G、data[]---用来读取图的各种信

//

void

DF_Search(AdjMGraph

G,SeqStack

S,HeuristicG

data[],int

Expand,int

v0,int

vg,int

visited[])

{

int

t,w;

//用于寻找目标节点

static

int

flag

=

0;

static

int

DFS_flag

=

0;

//标志位---找到目标节点后退出递归

StackPush(S,v0);

//首先将起始节点入栈

flag++;

printf(“%s-->

“,data[v0].G);

visited[v0]=1;

if(v0

!=

-1)

w=GetFirstVex(G,v0,visited);

//获取第一个临接点

while(!DFS_flagExpand

=

flag;

break;

}

if(!

visited[w]

if(DFS_flag)

break;

StackPop(S,w

=

GetNextVex(G,v0,w,visited);

}

}

2)宽度优先搜索

//

v0---起始节点

vg---目标节点

//

//

Expand---返回扩展结点数

//

//

count---返回路径结点数

//

//

BFS_path[]---存储路径信息

//

//

G、data[]---用来读取图的各种信息

//

void

BF_Search(AdjMGraph

G,HeuristicG

data[],int

v0,int

vg,intExpand,int

count,int

visited[],int

BFS_path[])

{

int

u,w,y,SumExpand=1,i=0;

SeqCQueue

Q;

SeqStack

SaveQ,LineSave;

//SaveQ将出队列的节点全部进栈,LineSave用于保存路径

StackInitiate(

StackInitiate(

printf(“%s-->

“,data[v0].G);

visited[v0]=1;

QueueInitiate(

QueueAppend(

//首先将起始节点入队列

while(QueenNotEmpty(Q))

{

QueueDelete(

StackPush(

//将每一个出队列的结点进行保存

w

=

GetFirstVex(G,u,visited);

if(w

==

vg)

{

SumExpand++;Expand

=

SumExpand;

break;

}

while(w

!=

-1)

{

if(

!visited[w])

{

printf(“%s-->

“,data[w].G);

visited[w]

=

1;

QueueAppend(

SumExpand++;

}

w

=

GetNextVex(G,u,w,visited);

}

}

StackPush(//此时w为目标节点

StackPush(

//此时u为w的父节点

StackPop(

while(StackNotEmpty(SaveQ))

{

StackPop(

StackTop(LineSave,if

(

Edge(G,u,y)==1

}

while(StackNotEmpty(LineSave))

{

StackPop(

BFS_path[i++]=u;

count

=

i;

}

}

3)A*

搜索

//

v0---起始节点

vg---目标节点

//

//

distance---用来保存已经过路径值

//

//

Expand---返回扩展结点数

//

//

G、data[]---用来读取图的各种信息

//

void

A_Search(AdjMGraph

G,HeuristicG

data[],int

v0,int

vg,int

distance,intExpand,int

visited[])

{

int

i,u,temp=10000;

static

int

path_num

=

0;

static

int

A_Search_flag

=

0;

//标志位---找到目标节点后退出递归

static

int

fx

=

0;

if(v0

==

2)

printf(“%s“,data[v0].G);

else

printf(“%s-->“,data[v0].G);

visited[v0]

=

1;

path_num++;

if(v0

==

vg)

{

A_Search_flag

=

1;Expand

=

path_num;

return;

}

for(i=0;i“,data[v0].G);

visited[v0]

=

1;

path_num++;

if(v0

==

vg)

{

G_Search_flag

=

1;Expand

=

path_num;

return;

}

for(i=0;i30时,回溯法已经很难找到解,运行时超过了20s。

当N>50时,GA算法运行时间开始逐渐变长,当N>90时运行时间已经超过了60s。

当N=200时,CSP的最小冲突法时间才仅为7.699s。

对比可知当N较大时,CSP的最小冲突法的效率最高,GA算法次之,回溯法在求解大的皇后数是效率最低。

对于回溯法的时间复杂度,最好情况是O(n^2),最坏情况是O(n!)。

对于CSP的最小冲突法的时间复杂度,最好的情况是O(n^3),最坏的情况是O(m*n^3)。

对于GA算法的时间复杂度,最好情况是O(n^3),最坏的情况是O(p*n^3)。

6.主要代码

1、回溯法:

void

backtrack(int

column)

//以列作为判断点

{

int

row,i,j;

double

secs,ms;

BK_stop

=clock();

ms

=

(double)(BK_stop

-

BK_start);

if(ms>20000)

//回溯法运行超过20s就退出

{

printf(“BackT_Queen

Calculations

took

more

than

20

seconds

!/n“);

exit(0);

}

if

(

column==BK_Q_num)

{

BK_end

=

clock

()

;

secs

=

(double)(BK_end

-

BK_start)

/

CLOCKS_PER_SEC

;

printf(“Back_Queen

Calculations

took

%.3lf

second%s./n“,secs,(secs

eachFitness[i]

eachFitness[worst])

worst

=

i

;

if(p->eachFitness[worst]

==

GA_Q_num-1)

return;

baby

=p

;

for

(i

=

0

;

i

p->unitFitness

||

(double)rand()

/

RAND_MAX

slice)

{

selection

=

i;

break;

}

}

return

selection;

}

//

杂交

father,mother,

产生的子代保存在

baby中

void

CrossOverFM

(Population

father,Population

mother,Populationbaby)

{

int

flag[MAX_QUEENS]

;

int

pos1,pos2,tmp

;

int

i,j

;

//

随机产生两个基因断点

do

{

pos1

=

rand()

%

GA_Q_num

;

pos2

=

rand()

%

GA_Q_num

;

}

while

(pos1

==

pos2)

;

if

(pos1

>

pos2)

{

tmp

=

pos1

;

pos1

=

pos2

;

pos2

=

tmp;

}

for

(j

=

0

;

j

pos2)

{

while

(flag[mother.queen[j]])

j++

;

//保证每个皇后都不在同一行

baby->queen[i]

=

mother.queen[j]

;

j

++

;

}

else

baby->queen[i]

=

father.queen[i]

;

}

UpdateFitnessScore

(baby)

;

}

//

杂交产生子代

void

CrossOver

()

{

int

i

;

int

father,mother

;

Population

p[30

+

MAX_QUEENS

/

10]

;

int

count

;

//

计算总共的

Fitness,

用于轮盘赌局规则时候的选择

m_totFitness

=

0

;

for

(i

=

0

;

i

<

m_size

;

i++)

m_totFitness

+=

m_population[i].unitFitness

;

for

(count

=

0

;

count

<

m_size

-

2

;

count++)

{

father

=

RouletteWheelSelection

()

;

mother

=

RouletteWheelSelection

()

;

CrossOverFM

(m_population[father],m_population[mother],//

杂交,后代保存

}

//

将结果覆盖回原种群,加上原种群中适应度最高的两个个体组成新的进化后的种群

for

(count

=

0

;

count

<

m_size

-

2

;

count++)

m_population[count+2]

=

p[count]

;

}

31