人工智能 倒啤酒问题 python解法


系列文章



补充发两篇文章记录一下之前用到的宽度优先算法

问题描述

分啤酒问题:现有8升、5升、3升的容器各一个,均无任何度量标记,其中8升的容器装满啤酒,其他两个为空。要求用上述容器倒来倒去,分成两份4升的啤酒。

倒啤酒问题

主要来介绍一下A*算法与该题目如何结合使用,并且使用python语言来实现它

宽度优先搜索

  1. 把起始点放入queue;
  2. 重复下述2步骤,直到queue为空为止:
    1 . 从queue中取出队列头的点;
    2 . 找出与此点邻接的且尚未遍历的点,进行标记,然后全部放入queue中。

可以看看这篇文章,类似关于算法的介绍网上很多

状态空间

(x,y,z) 分别代表8,5,3升容器的啤酒量,
x∈(0,1,2,3,4,5,6,7,8),
y∈(0,1,2,3,4,5),
z∈(0,1,2,3),
初始状态为(8,0,0)
目标状态为(4,4,0)

操作

  1. x倒入y
  2. x倒入z
  3. y倒入x
  4. y倒入z
  5. z倒入x
  6. z倒入y

思路

将各个属性放到一个类中,初始化状态空间,置为全0,仅仅(0,0,0)点为1,作为是否访问过的依据,再初始化一个队列来存放需要访问的节点,在类中编写操作相关的6个函数,接着使用宽度优先搜索算法进行循环,为了能够获取出正确并且多组路径,在每个节点的内容中添加了其父节点的信息。最后根据这些信息还原出所有的有效路径,这里稍微做出的改进是当遇到目标值后就不再对当前节点继续深入。

这里因为题目的信息可以简化部分倒水的过程具体分析在代码注释里给出了解析,最终仅仅y倒入z与z倒入y需要进行讨论

因为一共就8升水,8升的杯子是无论如何都到不满的,所以比如5升杯子倒入8升杯子就不用讨论,直接 8升杯子+=5升杯子 5升杯子=0 这样既可

代码

在这里插入图片描述

# -*- coding: utf-8 -*-
# @Time    : 2020/10/15 16:26
# @Author  : Tong Tianyu
# @File    : chapter1_2.py
import queue
import numpy as np


# 定义啤酒类对象
class Beer:
    def __init__(self):
        self.x = 8  # 八升的水壶
        self.y = 0  # 五升的水壶
        self.z = 0  # 三升的水壶
        self.state_space = np.zeros((9, 6, 4))  # 定义状态空间
        self.q_tmp = queue.Queue()  # 定义队列
        self.final = []  # 定义结果
        self.final_only_data = [[8, 0, 0]]  # 便于操作的结果

    def BFS(self):
        # con = 1
        self.q_tmp.put([8, 0, 0])
        self.final.append([[8, 0, 0], 'none', [0]])
        self.state_space[8][0][0] = 1
        while len(self.q_tmp.queue) != 0:
            a, b, c = self.q_tmp.get()
            if a == 4 and b == 4:
                continue
            pre = [a, b, c]
            # print('----------------', con, '------------------')
            self.x, self.y, self.z = a, b, c
            self.Pour_X_To_Y()
            self.update(pre, a, b, c, 'Pour_X_To_Y')

            self.Pour_X_To_Z()
            self.update(pre, a, b, c, 'Pour_X_To_Z')

            self.Pour_Y_To_X()
            self.update(pre, a, b, c, 'Pour_Y_To_X')

            self.Pour_Y_To_Z()
            self.update(pre, a, b, c, 'Pour_Y_To_Z')

            self.Pour_Z_To_X()
            self.update(pre, a, b, c, 'Pour_Z_To_X')

            self.Pour_Z_To_Y()
            self.update(pre, a, b, c, 'Pour_Z_To_Y')

            # con += 1
        return self.final, self.final_only_data

    def update(self, pre, a, b, c, name):
        if self.state_space[self.x][self.y][self.z] == 0:
            self.state_space[self.x][self.y][self.z] = 1
            self.q_tmp.put([self.x, self.y, self.z])
            self.final_only_data.append([self.x, self.y, self.z])
            self.final.append([[self.x, self.y, self.z], name, pre])
        self.x, self.y, self.z = a, b, c

    def Pour_X_To_Y(self):
        '''
            将8升杯x的水倒入5升杯y中
            因为总共8升的水,就算3升的杯子装满了,剩下的水也还有5升
            所以 x + y >= 5
        '''
        self.x -= 5 - self.y
        self.y = 5
        return True

    def Pour_X_To_Z(self):
        '''
            将8升杯x的水倒入3升杯z中
            因为总共8升的水,就算5升的杯子装满了,剩下的水也还有3升
            所以 x +z >= 3
        '''
        self.x -= 3 - self.z
        self.z = 3
        return True

    def Pour_Y_To_X(self):
        '''
            将5升杯y的水倒入8升杯x中
            分析一下 5升杯的水全倒入8升杯也装不满
        '''
        self.x += self.y
        self.y = 0
        return True

    def Pour_Y_To_Z(self):
        '''
            将5升杯y的水倒入3升杯z中
            这里要讨论了
        '''
        if self.y + self.z >= 3:  # 倒满三升杯
            self.y -= 3 - self.z
            self.z = 3
        else:
            self.z += self.y
            self.y = 0
        return True

    def Pour_Z_To_X(self):
        '''
            将3升杯z的水倒入8升杯x中
            同理,不用讨论
        '''
        self.x += self.z
        self.z = 0
        return True

    def Pour_Z_To_Y(self):
        '''将3升杯z的水倒入5升杯y中'''
        if self.z + self.y >= 5:
            self.z -= 5 - self.y
            self.y = 5
        else:
            self.y += self.z
            self.z = 0
        return True


if __name__ == '__main__':
    beer = Beer()
    final_list, tmp_list = beer.BFS()
    print(final_list)
    result = []
    for i in range(len(final_list)):
        if i == 0:
            continue
        data = final_list[i]
        print('每行的数据为:', data)
        pre = data[2]
        print('当前的父节点为:', pre)
        pre_index = tmp_list.index(pre)
        print('当前父节点在列表中的位置为:', pre_index)
        result = final_list[pre_index][-1]
        print('当前父节点的路径为:', result)
        # md这里被深浅复制搞死了
        a = result.copy()
        a.append(i)
        final_list[i][-1] = a
        print('*********************************************')


    def output(order):
        print('x' + '\t' + 'y' + '\t' + 'z' + '\t' + 'method')
        for i in order:
            print(*final_list[i][0], sep='\t', end='\t')
            print(final_list[i][1])


    for i in range(len(tmp_list)):
        con = 0
        if tmp_list[i] == [4, 4, 0]:
            print(f'-------------方法{con}--------------')
            order = final_list[i][-1]
            output(order)
            con += 1

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐