人工智能导论实验导航
实验一:斑马问题 https://blog.csdn.net/weixin_46291251/article/details/122246347
实验二:图像恢复 https://blog.csdn.net/weixin_46291251/article/details/122561220
实验三:花卉识别 https://blog.csdn.net/weixin_46291251/article/details/122561505
实验四:手写体生成 https://blog.csdn.net/weixin_46291251/article/details/122576478
实验源码: xxx
1.1实验介绍
1.1.1实验背景
演绎推理(Deductive Reasoning)是由一般到特殊的推理方法。与“归纳法”相对。推论前提与结论之间的联系是必然的,是一种确实性推理。
运用此法研究问题,首先要正确掌握作为指导思想或依据的一般原理、原则;其次要全面了解所要研究的课题、问题的实际情况和特殊性;然后才能推导出一般原理用于特定事物的结论。
演绎推理的形式有三段论、假言推理和选言推理等。在教育工作中, 依据一定的科学原理设计和进行教育与教学实验等,均离不开此法。
1.1.2实验目的
本章实验的主要目的是掌握逻辑与推理相关基础知识点,了解算法推理相关基础知识,掌握归纳法和演绎推理的主要步骤,熟悉python编程。
通过实验,基本掌握逻辑编程的思想,了解逻辑编程与命令式编程的区别。能够依据给定的事实以及规则编写代码,解决逻辑约束问题(CLP)。
1.1.3实验简介
5个不同国家且工作各不相同的人分别住在一条街上的5所房子里,每所房子的颜色不同,每个人都有自己养的不同宠物,喜欢喝不同的饮料。
根据以下提示,你能告诉我哪所房子里的人养斑马,哪所房子里的人喜欢喝矿泉水吗?
存在的条件汇总:
- 国籍:英国、西班牙、日本、意大利、挪威
- 房子颜色:红色、绿色、白色、蓝色、黄色
- 工作:油漆工、摄影师、外交官、小提琴家、医师
- 宠物:狗、蜗牛、狐狸、马、斑马
- 饮料:茶、牛奶、咖啡、橘子汁、矿泉水
已知条件如下:
- 英国人住在红色的房子里
- 西班牙人养了一条狗
- 日本人是一个油漆工
- 意大利人喜欢喝茶
- 挪威人住在左边的第一个房子里
- 绿房子在白房子的右边
- 摄影师养了一只蜗牛
- 外交官住在黄房子里
- 中间那个房子的人喜欢喝牛奶
- 喜欢喝咖啡的人住在绿房子里
- 挪威人住在蓝色的房子旁边
- 小提琴家喜欢喝橘子汁
- 养狐狸的人所住的房子与医生的房子相邻
- 养马的人所住的房子与外交官的房子相邻
1.2概要设计
1.2.1手动求解
-
1.罗列初始条件。
-
2.首先由“英国人住在红色的房子里,绿房子在白房子的右边,外交官住在黄房子里”以及上述条件可知:
a:红白绿房子必在右边三个房子。
b:挪威人必住在黄房子且他是外交官
c:红房子要么是第三间,要么是第五间。 -
3.然后假设在红房子在第三间外交官,可得:
a.由表格知挪威人不喝咖啡和牛奶,又意大利喝茶,小提琴家喝桔子汁(挪威人是外交官),所以挪威人喝矿泉水。
b.由小提琴家和桔子汁知,小提琴家不是英国人(喝牛奶),挪威人(矿泉水),意大利人(茶),日本人(油漆工),所以小提琴家是西班牙人。 -
4.结合上述分析可得
a.由“养狐狸的人所住的房子与医师的房子相邻”知,医师不可能在绿色和红色房子(相邻宠物不为狐狸),也不可能在黄色房子(外交官),所以医师是蓝色房子。
b.由外交官,小提琴,油漆工的国籍已定,且医师不是英国人(蓝色房子),知摄影师是英国人,养了蜗牛。所以挪威人养了狐狸(养狐狸的人所住的房子与医师的房子相邻)。
c.狐狸,马,蜗牛,狗的主人身份已确定,知斑马是油漆工,即日本人的宠物。
d.由矿泉水,牛奶,桔子汁,咖啡主人已确定,且意大利人喝茶,知意大利人所住房子为蓝色。 -
5.综合以上分析可得下面的表格:
1.2.2 python求解
通过上述分析,结合python求解逻辑推理问题的常见库,得到以下思路:
解法1:穷举法
将所有可能的组合通过全排列的形式穷举出来
然后根据已有的条件依次匹配
若全部通过则为正确答案
否则为错误方案
解法2:
利用google or-tools 。
OR-Tools是一个用于优化的开源软件套件,用于解决车辆路径、流程、整数和线性规划以及约束编程等世界上最棘手的问题。
当前ortools提供的优化器包括: – 约束规划 – 线性与混合整数规划 – 路径规划 – 调度规划 – 网络规划 – 装箱。
并且在其GitHub的源代码中有解决斑马问题的示例代码,地址如下:
https://github.com/google/or-tools/blob/master/examples/python/zebra_sat.py
解法3:
使用python逻辑编程kanren库,将题目中的条件形式化为一个个的表达式,加入约束到kanren的一个集合中,然后利用kanren内置的run求解即可。
综上:
通过上述对三种方法的分析可知,方法1过于简单粗暴,能够简单的解决问题,但是算法的复杂度太高,不可取。
方法二和方法三都是通过调用python对应的逻辑分析库完成,但鉴于方法2的google or-tools 官方已有较为完善的示例代码,与独立完成实验的要求不符,故采用方法3。下面只给出方法3的解法设计与分析。
1.3详细设计
首先导入实验所需的包
from kanren import *
from kanren.core import lall
下面根据已知条件的性质将条件分为几组,首先有着确定关系的实体和其属性的条件,以及同属于一个实体的属性间的关系,有如下几个:
- 英国人住在红房子里
- 西班牙人养了一条狗
- 日本人是一个油漆工
- 意大利人喝茶。
- 摄影师养了一只蜗牛
- 外交官住在黄房子里
- 外交官住在黄房子里
- 喜欢喝咖啡的人住在绿房子里
- 小提琴家喜欢喝橘子汁
然后介绍kanren中加入一个逻辑表达式的方法:
首先用houses = var() 创建一个逻辑变量house。
然后利用membero函数表示一个约束, membero(item, coll) 表示 item 是 coll集合中的一个成员。如:
(membero, ('Britain', var(), var(), var(), 'red'), houses), # 英国人住在红房子里
代码中的var表示任意变量。
5个var()分别代表 国籍、工作、饮料、宠物、屋子颜色
(membero, ('Britain', var(), var(), var(), 'red'), houses), # 英国人住在红房子里
(membero, ('Spain', var(), var(), 'dog', var()), houses), # 西班牙人养了一条狗
(membero, ('Japan', 'painter', var(), var(), var()), houses), # 日本人是一个油漆工
(membero, ('Italy', var(), 'tea', var(), var()), houses), # 意大利人喝茶。
(membero, (var(), 'Photographer', var(), 'snail', var()), houses), # 摄影师养了一只蜗牛
(membero, (var(), 'diplomat', var(), var(), 'yellow'), houses), # 外交官住在黄房子里
(membero, (var(), var(), 'coffee', var(), 'green'), houses), # 喜欢喝咖啡的人住在绿房子里
(membero, (var(), 'Violinist', 'juice', var(), var()), houses), # 小提琴家喜欢喝橘子汁
然后是表示实体在整体中的确切属性的条件,有以下两个:
- 挪威人住在左边的第一个房子里
- 中间房子的人喜欢喝牛奶
添加方法和第一种一样,不一样的是这里用的是eq函数,eq(a,b ) 表示ab相等。
(eq, (('Norwegian', var(), var(), var(), var())
, var(), var(), var(), var()), houses), # 挪威人住在左边的第一个房子里
(eq, (var(), var(),
(var(), var(), 'milk', var(), var()),
var(), var()), houses), # 中间那个房子的人喜欢喝牛奶
然后是描述两个实体间的不确定的(模糊的)关系的表达式,有以下几个:
- 绿房子在白房子的右边
- 挪威人住在蓝房子旁边。
- 养狐狸的人所住的房子与医生的房子相邻
- 养马的人所住的房子与外交官的房子相邻
但是在kanren中没有能够直接描述以上关系的函数,所以定义了以下两个函数,用来实现上述要求:
其中利用到的kanren内的函数:
- zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
- CONDE:一个用于逻辑和/或OR的目标构造函数。
实现的函数:
Left(a,b,x)表示在关系集合x中a在b的右侧
Next(a,b,x)表示在关系集合x中a和b相邻
其中next是借助left表示出来的,即:两个变量a,b相邻只有两种情况,a在b左边和b在a左边。
def left(q, p, list):
return membero((q, p), zip(list, list[1:]))
def next(q, p, list): # p q 相邻意味着要么p在q左边,要么q在p左边
return conde([left(q, p, list)], [left(p, q, list)])
然后借助以上两个函数就可以将对应的四个逻辑表达式表达出来了:
(left, # 绿房子在白房子的右边
(var(), var(), var(), var(), 'green'),
(var(), var(), var(), var(), 'white'),
houses),
(next, ('Norwegian', var(), var(), var(), var()),
(var(), var(), var(), var(), 'blue'), houses), # 挪威人住在蓝房子旁边。
(next, (var(), 'physician', var(), var(), var()), # 养狐狸的人所住的房子与医生的房子相邻
(var(), var(), var(), 'fox', var()), houses),
(next, (var(), 'diplomat', var(), var(), var()), # 养马的人所住的房子与外交官的房子相邻
(var(), var(), var(), 'horse', var()), houses),
到这里所有的表达式都已经表达完毕,已经可以利用run运行了,但是此时的表达式中还没有斑马和矿泉水这两个变量,程序处理到他们时会用符号代替,下面添加这两个变量进入表达式:
(membero, (var(), var(), var(), 'zebra', var()), houses), # 有人养斑马
(membero, (var(), var(), 'water', var(), var()), houses) # 有人喝水
现在完成了所有表达式和变量的表达。
接下来开始运算。
solutions = run(0, houses, rules_zebraproblem)
根据运算的结果,判断谁是斑马的主人以及谁爱喝矿泉水。
判断是否有解
if len(solutions):
zebra_owner = ""
water_drinker = ""
for i in solutions[0]:
if "zebra" in i:
zebra_owner = i[0] # 找到斑马的主人
if "water" in i:
water_drinker = i[0] # 找到喝矿泉水的人
print(i)
print('\nzebra_owner:\t\t' + zebra_owner) # 打印结果
print('water_drinker:\t' + water_drinker)
else:
print("no answer")
程序设计完毕,下面进行运行测试。
1.4运行测试
运行程序,输出结果
分析程序输出的结果可知,日本人的宠物是斑马,挪威人爱喝矿泉水。
结合前面手动计算出的结果,可知两个结果完全一致,程序正确运行。
穷举法
# -*- coding: GBK -*-
import time
import itertools
"""
本题的思路:
将所有可能的组合通过全排列的形式穷举出来
然后根据已有的条件依次匹配
若全部通过则为正确答案
否则为错误方案
"""
# **********************匹配函数******************************
def handle(data):
# 英国人住红色房子
index = data[0].index('红色')
if data[1][index] != '英国':
return -1
# 瑞典人养狗
index = data[4].index('狗')
if data[1][index] != '瑞典':
return -2
# 丹麦人喝茶
index = data[2].index('茶')
if data[1][index] != '丹麦':
return -3
# 抽Pall Mall香烟的人养鸟
index = data[3].index('Pall Mall')
if data[4][index] != '鸟':
return -6
# 黄色房子主人抽Dunhill香烟
index = data[3].index('Dunhill')
if data[0][index] != '黄色':
return -7
# 抽Blue Master的人喝啤酒
index = data[3].index('BlueMaster')
if data[2][index] != '啤酒':
return -12
# 德国人抽Prince香烟
index = data[3].index('Prince')
if data[1][index] != '德国':
return -13
# 绿色房子在白色房子左面
index = data[0].index('绿色')
if index == 4: # 绿色房子在最后
return -4
if data[0][index + 1] != '白色':
return -4
# 绿色房子主人喝咖啡
if data[2][index] != '咖啡':
return -5
# 抽Blends香烟的人住在养猫的人隔壁
index = data[3].index('Blends')
cat_index = data[4].index('猫')
if cat_index - index != 1 and cat_index - index != -1:
return -10
# 养马的人住抽Dunhill香烟的人隔壁
index = data[3].index('Dunhill')
horse_index = data[4].index('马')
if horse_index - index != 1 and horse_index - index != -1:
return -11
# 抽Blends香烟的人有一个喝水的邻居
index = data[3].index('Blends')
water_index = data[2].index('水')
if water_index - index != 1 and water_index - index != -1:
return -15
print('找到答案:')
for d_item in data:
print(d_item)
return 0
# **********************全排列列表******************************
# itertools.permutations 返回可迭代对象的所有数学全排列方式
# 以下是所有元素的全排列构成的列表
colour_list_all = list(itertools.permutations(['红色', '黄色', '蓝色', '白色', '绿色'], 5))
country_list_all = list(itertools.permutations(['英国', '丹麦', '挪威', '德国', '瑞典'], 5))
drinks_list_all = list(itertools.permutations(['茶', '水', '咖啡', '啤酒', '牛奶'], 5))
smoke_list_all = list(itertools.permutations(['Pall Mall', 'Dunhill', 'BlueMaster', 'Blends', 'Prince'], 5))
pet_list_all = list(itertools.permutations(['猫', '马', '鱼', '鸟', '狗'], 5))
# **********************过滤后的全排列列表******************************
# 全排列数量过大以下是过滤后出的列表
colour_list = []
country_list = []
drinks_list = []
smoke_list = []
pet_list = []
for colour in colour_list_all:
colour = list(colour)
if colour[1] == '蓝色':
colour_list.append(colour)
for country in country_list_all:
country = list(country)
if country[0] == '挪威':
country_list.append(country)
for drinks in drinks_list_all:
drinks = list(drinks)
if drinks[2] == '牛奶':
drinks_list.append(drinks)
# 5! = 120
for i in range(len(smoke_list_all)):
smoke_list.append(list(smoke_list_all[i]))
for i in range(len(pet_list_all)):
pet_list.append(list(pet_list_all[i]))
# **********************遍历所有可能的方案并进行匹配******************************
# 由于程序采用穷举法,效率较低,故记录时间。
start = time.time()
for country in country_list:
for drinks in drinks_list:
for smoke in smoke_list:
for pet in pet_list:
for colour in colour_list:
data_list = [colour, country, drinks, smoke, pet]
# ~ num += 1
if handle(data_list) == 0:
# ~ print("总运行次数 %d" % num)
exit()
end = time.time()
print("\r计算用时:%0.4f秒" % (end - start), end="")
其他解法
利用google or-tools 求解
https://github.com/google/or-tools/blob/master/examples/python/zebra_sat.py
prolog求解
https://blog.csdn.net/weixin_45841983/article/details/109813808
文章出处登录后可见!