人工智能概论 | 基于A*算法的8数码问题求解

news2024/10/6 17:55:51

大学四年的全部课程和考试都已经结束啦!

最近闲来无事,随便发发自己的实验报告,供后面的学弟学妹们参考~


目录

实验1 基于A*算法的8数码问题求解

1.1  程序总体流程

1.2  关键代码展示

1.3  输出结果展示及分析

1.3.1  总步数展示  

1.3.2  每一次的Open表和Closed表展示 

1.3.3  对输出结果的分析  

1.4  原始程序存在的问题

1.5  程序修改的核心内容

2.1  对两种新的启发式函数的说明

2.2  关键代码展示(3种不同的启发式函数)

2.3   输出结果展示及对比分析

2.3.1  总步数展示及对比分析  

2.3.2  Open表和Closed表展示及对比分析

2.4  三种启发式函数的特点及比较

2.5 实验一心得与体会


实验1 基于A*算法的8数码问题求解

(1)问题描述:在3*3的棋盘中有8个数码(数字块)和一个空格,只有与空格相邻的数码能移动到空格位置。从初始状态以最小的步长移动到目标状态。

(2)实验要求:参考A*算法核心代码,以8数码问题为例实现A*算法。

(3)实验内容:

1、基于参考代码,修改并运行程序,并画出程序的总体流程图。自动统计并输出到达目标的总步数,自动记录并输出每次移动后open表与closed表的变化。如果原始程序中存在不合理的逻辑,请指出其问题所在。如果程序进行了修改,请说明修改的核心内容。要求初始状态与目标状态分别为:

2、设计两种新的启发式函数,并运行程序。对包含示例在内的3种启发式函数的特点与结果进行对比分析。

1.1  程序总体流程

本实验是基于A*算法的求解8数码问题的实现。下面是该程序的总体流程:

(1)初始化初始状态和目标状态  初始状态和目标状态都是一个3x3的矩阵,分别代表了初始时的数码布局和目标状态下的数码布局。

(2)定义状态类 State  State 类用于表示问题中的一个状态。每个状态包含当前数码布局、可以移动的方向、父状态、从初始状态到当前状态的实际代价 g(n) 和当前状态到目标状态的估计代价 h(n)。这里使用了 Manhattan 距离作为启发式函数进行估计。

State 类中包含了一系列方法来获取方向、计算0点的位置、生成下一个可能的状态、计算启发式函数值等。

(3)A*算法求解  在 solve 方法中,程序使用 A* 算法来求解问题。初始时将初始状态加入 Open 表,然后循环进行以下步骤:

从 Open 表中选择 f(n) 最小的状态,将其移到 Closed 表中。

检查选择的状态是否为目标状态,如果是,则构建路径并返回;否则,生成当前状态的下一个可能状态,并加入 Open 表中。

对加入到 Open 表中的状态,计算其 g(n) 和 h(n),并更新其 f(n) 值。

如果 Open 表为空,且没有找到目标状态,则说明无解。

(4)打印结果  如果找到了解,程序将打印出解决方案的总步数,并逐步打印出每个状态的数码布局。如果未找到解,则打印出无解的消息。

总体而言,该程序的流程是先初始化状态,然后使用A*算法进行搜索,直到找到解或确定无解为止。

本程序的总体流程详见图1-1。

1.2  关键代码展示

本实验的关键代码如下所示。

class State:

    answer = np.array([[1, 2, 3], [8, 0, 4], [7, 6, 5]])

    def __init__(self, state, directionFlag=None, parent=None, f=0):

        self.state = state

        self.direction = ['up', 'down', 'right', 'left']

        if directionFlag:

            self.direction.remove(directionFlag)

        self.parent = parent

        self.f = f

        def getFunctionValue_ManhattanDistance(self, g_value):

        dist = 0

        for i in range(len(self.state)):

            for j in range(len(self.state)):

                if self.state[i][j] != State.answer[i][j]:

                    index = np.argwhere(State.answer == self.state[i][j])

                    x = index[0][0]

                    y = index[0][1]

                    dist += (abs(x - i) + abs(y - j))

        return dist + g_value

     def nextStep(self):

        subStates = []

        x, y = self.getZeroPos()

        boarder = len(self.state) - 1

        g_value = self.f - self.getFunctionValue_ManhattanDistance(0)  

        if 'left' in self.direction and y > 0:

            s = self.state.copy()

            s[x, y], s[x, y - 1] = s[x, y - 1], s[x, y]

            news = State(s, directionFlag='right', parent=self)

            news.setF(news.getFunctionValue_ManhattanDistance(g_value))

            subStates.append(news)

        if 'up' in self.direction and x > 0:

            s = self.state.copy()

            s[x, y], s[x - 1, y] = s[x - 1, y], s[x, y]

            news = State(s, directionFlag='down', parent=self)

            news.setF(news.getFunctionValue_ManhattanDistance(g_value))

            subStates.append(news)

        if 'down' in self.direction and x < boarder:

            s = self.state.copy()

            s[x, y], s[x + 1, y] = s[x + 1, y], s[x, y]

            news = State(s, directionFlag='up', parent=self)

            news.setF(news.getFunctionValue_ManhattanDistance(g_value))

            subStates.append(news)

        if 'right' in self.direction and y < boarder:

            s = self.state.copy()

            s[x, y], s[x, y + 1] = s[x, y + 1], s[x, y]

            news = State(s, directionFlag='left', parent=self)

            news.setF(news.getFunctionValue_ManhattanDistance(g_value))

            subStates.append(news)

        return subStates

    def solve(self):

        openTable = []  

closeTable = []

        openTable.append(self)

        step = 0

        while len(openTable) > 0:

            step += 1            

            n = openTable.pop(0)

            closeTable.append(n)

            subStates = n.nextStep()  

            for subState in subStates:

                if (subState.state == State.answer).all():

                    openTable.append(subState)

                    openTable.sort(key=lambda state: state.f)

                    path = [subState]

                    while subState.parent and subState.parent != self:

                        path.append(subState.parent)

                        subState = subState.parent

                    path.reverse()            

                 # 此处省略打印Open表和Closed表代码,详见源程序 #

                    return path

                openTable.append(subState)

            openTable.sort(key=lambda state: state.f)

            # 此处省略打印Open表和Closed表代码,详见源程序 #

        return None

1.3  输出结果展示及分析

1.3.1  总步数展示  

本程序运行后得到的总步数为5,详见表1-1、表1-2。

表1-1 结果展示(总步数)

初始状态

目标状态

是否有解

启发式函数

总步数

2 8 3

1 6 4

7 0 5

1 2 3

8 0 4

7 6 5

曼哈顿距离

5

表1-2 每一步的状态变化

步数

当前状态

0(初始状态)

2 8 3

1 6 4

7 0 5

1

2 8 3

1 0 4

7 6 5

2

2 0 3

1 8 4

7 6 5

3

0 2 3

1 8 4

7 6 5

4

1 2 3

0 8 4

7 6 5

5(目标状态)

1 2 3

8 0 4

7 6 5

1.3.2  每一次的Open表和Closed表展示 

下面展示的是每一次的Open表和Closed表的完整输出内容,详见表1-3和图1-3。(注明:最后一次已经找到目标状态,所以程序未输出此步的Open表和Closed表。)

表1-3 每一步的Open表和Closed表

次数

Open表

Closed表

1

2 8 3   2 8 3   2 8 3

1 0 4   1 6 4   1 6 4

7 6 5   0 7 5   7 5 0

2 8 3

1 6 4

7 0 5

2

2 0 3   2 8 3   2 8 3    

1 8 4   0 1 4   1 4 0   

7 6 5   7 6 5   7 6 5 

  

2 8 3   2 8 3

1 6 4   1 6 4

0 7 5   7 5 0

2 8 3   2 8 3

1 6 4   1 0 4

7 0 5   7 6 5

3

0 2 3   2 8 3   2 8 3

1 8 4   0 1 4   1 4 0

7 6 5   7 6 5   7 6 5

2 8 3   2 8 3   2 3 0

1 6 4   1 6 4   1 8 4

0 7 5   7 5 0   7 6 5

2 8 3   2 8 3   2 0 3

1 6 4   1 0 4   1 8 4

7 0 5   7 6 5   7 6 5

4

1 2 3   2 8 3   2 8 3

0 8 4   0 1 4   1 4 0

7 6 5   7 6 5   7 6 5

2 8 3   2 8 3   2 3 0

1 6 4   1 6 4   1 8 4

0 7 5   7 5 0   7 6 5

2 8 3   2 8 3   2 0 3

1 6 4   1 0 4   1 8 4

7 0 5   7 6 5   7 6 5

0 2 3

1 8 4

7 6 5

5

1 2 3   2 8 3   2 8 3

8 0 4   0 1 4   1 4 0

7 6 5   7 6 5   7 6 5

2 8 3   2 8 3   2 3 0

1 6 4   1 6 4   1 8 4

0 7 5   7 5 0   7 6 5

1 2 3

7 8 4

0 6 5

2 8 3   2 8 3   2 0 3

1 6 4   1 0 4   1 8 4

7 0 5   7 6 5   7 6 5

0 2 3   1 2 3

1 8 4   0 8 4

7 6 5   7 6 5

6

当前Sg是目标状态,成功退出,本次搜索结束!

1.3.3  对输出结果的分析  

在这个结果中,搜索算法成功地找到了从初始状态到目标状态的路径,总共经过了5个步骤。这个结果表明了所使用的A*算法能够有效地在状态空间中搜索,并找到最优解。其中,分析结果的关键点包括:

(1)搜索策略有效性  搜索过程中,每一步都是根据启发式函数的估计值来决定下一步的状态选择,这使得算法能够朝着更有可能达到目标的方向前进。

(2)搜索算法优化  在搜索过程中,采用了一些优化策略,比如对已经搜索过的状态进行记录和排除,以避免重复搜索。此外,对于下一步可能的状态,根据启发式函数的估值进行排序,优先选择估值较小的状态,以加速搜索过程。

(3)搜索路径可视化  最终输出了搜索过程中的每个步骤和状态,这有助于理解算法的执行过程,并且提供了一种对算法性能的直观评估方式。

总的来说,这个结果表明所使用的搜索算法在解决八数码问题上具有较好的效果,能够快速而有效地找到最优解。

1.4  原始程序存在的问题

原始代码的逻辑错误在于nextStep函数中的实现和solve函数中的调用方式。 具体来说,nextStep函数应该返回一个包含所有可能下一步状态的列表,而不是仅返回一个状态。当前的实现在每次调用nextStep时只返回一个状态,这会导致只能扩展一个状态,而不是所有可能的状态。

在solve函数中,虽然将subStates加入了openTable,但是openTable应该存放待探索的状态,而不是直接放入subStates,应该将subStates中的所有状态加入openTable,而不是仅加入其中的一个。

除了上述逻辑问题外,我还发现原始代码存在下列问题。

(1)getFunctionValue 方法中的变量 self.answer 未定义。这个变量在类中未被初始化或设置。为了解决这个问题,可以将 State 类的构造函数修改为接收 answer 参数,并在初始化时设置该属性。

(2)在 nextStep 方法中,当检查是否可以向右移动时,使用了 count 方法来检查 'right' 是否在 self.direction 中。应该使用 in 关键字来检查。修正这一问题后,if 语句应该是 if 'right' in self.direction and y < boarder:。

(3)在使用曼哈顿距离作为启发式函数getFunctionValue的实现中,g(x)不应该恒等于1,而应该等于初始状态到当前状态的实际路径成本。

1.5  程序修改的核心内容

原始程序的核心修改内容如下。

(1)修改了 nextStep 方法,使其返回包含所有可能下一步状态的列表,而不是仅返回一个状态。

(2)在 solve 方法中,将 subStates 中的所有状态都加入 openTable 中,而不仅仅是其中的一个状态。

(3)在 State 类中添加了 answer 参数,以解决 getFunctionValue 方法中的 self.answer 未定义的问题。

(4)在 getFunctionValue 方法中,计算实际路径成本 g(x),而不是恒等于 1。这通过计算从初始状态到当前状态的移动步数来实现。

(5)在 nextStep 方法中,使用 in 关键字来检查方向是否在 self.direction 中,并修复了向右移动的判断逻辑。

2.1  对两种新的启发式函数的说明

当使用A*搜索算法解决问题时,需要使用启发式函数来评估状态的优劣。这些启发式函数可以帮助算法更有效地搜索最优解。在本实验中,除了一开始使用的曼哈顿距离之外,我还设计了以下两种新的启发式函数。

(1)不在位数字的数量  这个启发式函数简单地计算当前状态中与目标状态不同的数字的数量。这些数字的数量越多,表示状态越远离目标状态。因此,该启发式函数的值越大,表示状态越不优。具体地,如果一个数字不在它应该在的位置上,则它是一个不在位数字。不在位数字的数量可以直接用来评估状态的距离。

(2)欧几里得距离  欧几里得距离是当前状态与目标状态的每个数字位置的欧几里得距离之和。简单来说,对于每个数字,我们计算其在当前状态和目标状态中的位置之间的直线距离,然后将所有数字的距离加起来。这个距离是欧几里得距离的一种应用。该启发式函数考虑了数字在空间上的位置,可以更准确地评估状态之间的距离。因此,如果一个状态中的数字与目标状态的数字更接近,那么欧几里得距离就会更小,启发式函数值也会更小,表示状态更优。

在实践中,这两种启发式函数都可以有效地帮助A*算法搜索到最优解。不在位数字的数量是一种直观的评估方式,但可能不够准确。而欧几里得距离考虑了数字之间的空间关系,提供了更精确的评估,但也更加复杂和计算密集。在选择启发式函数时,需要根据具体情况进行权衡和选择。

2.2  关键代码展示(3种不同的启发式函数)

#使用曼哈顿距离作为启发式函数

def getFunctionValue_ManhattanDistance(self, g_value):

        dist = 0

        for i in range(len(self.state)):

            for j in range(len(self.state)):

                if self.state[i][j] != State.answer[i][j]:

                    index = np.argwhere(State.answer == self.state[i][j])

                    x = index[0][0]

                    y = index[0][1]

                    dist += (abs(x - i) + abs(y - j))

        return dist + g_value

    #使用不在位个数作为启发式函数

    def getFunctionValue_notInPosition(self):

        count = 0

        for i in range(len(self.state)):

            for j in range(len(self.state[0])):

                if self.state[i][j] != State.answer[i][j]:

                    count += 1

        if self.state[1][1] == State.answer[1][1]:

            count += 1

        return count - 1

  

  #使用欧几里得距离作为启发式函数

    def getFunctionValue_EuclideanDistance(self, g_value):

        dist = 0

        for i in range(len(self.state)):

            for j in range(len(self.state[0])):

                if self.state[i][j] != 0 and self.state[i][j] < len(self.state) * len(self.state[0]):

                    target_i, target_j = divmod(State.answer.flatten().tolist().index(self.state[i][j]), len(self.state))

                    dist += ((target_i - i) ** 2 + (target_j - j) ** 2) ** 0.5

        return dist + g_value

2.3   输出结果展示及对比分析

下面展示的是三种不同启发式函数的程序运行结果,其中曼哈顿距离作为启发式函数的结果已经在第1个问题中进行了详细说明,因此这里不再赘述。

2.3.1  总步数展示及对比分析  

(1)不在位数字的数量  本程序运行后得到的总步数为5,详见图2-1。

(2)欧几里得距离  本程序运行后得到的总步数为5,详见图2-2。

(3)对比分析  由上述结果可知,三种不同的启发式函数的总步数都是相同的,均为5步。

从效率上来看,由三种启发式函数的总步数相同可知,在这个问题中的效率是相同的,它们都找到了最优路径。

从复杂度来看,虽然总步数相同,但每种启发式函数在搜索过程中所考虑的因素不同。不在位数字的数量直接计算了当前状态与目标状态之间的差异,欧几里得距离和曼哈顿距离则考虑了数字的位置信息。在实际问题中,曼哈顿距离通常更容易计算,因为它只涉及数字的位置之差的绝对值之和。

2.3.2  Open表和Closed表展示及对比分析

(1)不在位数字的数量  本程序运行后得到的Open表和Closed表,如图2-3所示。

(2)欧几里得距离  本程序运行后得到的Open表和Closed表和以曼哈顿距离作为启发式函数的结果完全一致,在此不再赘述。

(3)对比分析  由上述结果可知,当不在位数字的数量作为启发式函数时,输出的Open表和Closed表和另外两个启发式函数的不同。

每种启发式函数的Open表和Closed表在每个步骤中可能会有所不同。这是因为不同的启发式函数会影响搜索算法如何评估和排序每个状态的优先级,从而影响到算法的具体执行路径。虽然三者的最终解决方案路径相同的,但在搜索过程中,Open表和Closed表的具体内容可能会有所不同。

不在位数字的数量作为启发式函数,会根据每个状态中不在位的数字数量来评估其与目标状态的距离。这种方式可能会导致搜索算法在展开状态时优先考虑那些不在位数字数量较少的状态,从而影响Open表和Closed表的内容。这也是为什么不在位数字的数量作为启发式函数时,得到了与另外两个启发式函数不同的输出过程。

而欧几里得距离和曼哈顿距离作为启发式函数,考虑了数字的位置信息,因此在搜索过程中可能会产生不同的评估结果,从而影响Open表和Closed表的内容。在这里,我们的发现欧几里得距离和曼哈顿距离作为启发式函数,得到结果相同。

因此,即使是相同的问题和初始状态,不同的启发式函数可能会导致不同的搜索路径和展开顺序,最终可能会得到略有不同的Open表和Closed表。

2.4  三种启发式函数的特点及比较

最后让我们对这三种启发式函数的各自特点进行比较分析。

对于不在位数字的数量,其特点及比较分析如下所示。

(1)简单直观  只需计算当前状态与目标状态中不同数字的数量。

(2)计算简单  只需遍历状态矩阵一次即可完成计算。

(3)比较分析  该启发式函数的结果会高估状态之间的距离,因为它只考虑了数字的数量而忽略了数字的位置信息。

当状态之间的差异主要是由不在位数字引起时,该启发式函数可能会提供合理的估计,但在某些情况下可能不够准确。

对于欧几里得距离,其特点及比较分析如下所示。

(1)考虑空间关系  计算每个数字在空间中的位置,并将其转化为欧几里得距离。

(2)提供精确估计  考虑了数字之间的实际距离,提供了更精确的状态距离估计。

(3)比较分析  由于考虑了数字之间的实际距离,该启发式函数提供了更准确的状态距离估计。但是,计算欧几里得距离可能会更复杂,尤其是对于大型状态空间。

对于曼哈顿距离,其特点及比较分析如下所示。

(1)考虑位移关系  考虑了每个数字在水平和垂直方向上的位移。

(2)简化计算  只需计算数字的水平和垂直位移的绝对值之和。

(3)比较分析  曼哈顿距离提供了对状态距离的相对准确估计。

与欧几里得距离相比,曼哈顿距离更简单,计算速度更快,但通常会高估状态之间的距离。

综上所述,不同的启发式函数在八数码问题中都有其优缺点。欧几里得距离提供了最准确的距离估计,但计算复杂度较高。曼哈顿距离相对较简单,计算速度快,但可能会高估状态之间的距离。而不在位数字的数量则是一种简单直观的评估方式,计算简单但可能不够准确。在实践中,根据问题的具体情况和要求,选择适当的启发式函数非常重要。

2.5 实验一心得与体会

在本次实验中,我实现了A*搜索算法来解决八数码问题,并设计了三种不同的启发式函数。下面是我在完成本实验后的一些个人心得与体会。

首先,启发式函数对A*搜索算法的性能至关重要。曼哈顿距离作为一种经典的启发式函数,简单易用且在许多情况下能提供合理的结果。然而,本次实验中,我发现不在位数字的数量和欧几里得距离这两种新的启发式函数也能有效地帮助算法找到最优解。这提醒我在解决问题时应该多角度思考,不拘泥于传统的方法,而是灵活运用各种技术手段。

其次,实验中对比了三种启发式函数的性能及特点。欧几里得距离在提供更准确的状态距离估计方面具有优势,但计算复杂度相对较高;曼哈顿距离计算简单且速度快,但可能会高估状态之间的距离;而不在位数字的数量则简单直观,但准确性可能不足。这表明在选择启发式函数时需要综合考虑准确性和计算复杂度,并根据具体问题的需求做出权衡。

最后,实验过程中我也意识到了算法的执行路径可能会受到启发式函数的影响。不同的启发式函数会导致搜索算法展开状态的顺序不同,从而影响到算法的执行路径。这提示我在实际问题中,要对不同的启发式函数进行综合评估,并选择最适合的启发式函数以达到更好的性能。

总的来说,本次实验让我深入了解了A*搜索算法及启发式函数的原理和应用,提高了我对算法设计和问题求解的理解能力。同时也加深了我对算法性能评估和选择的认识,为我今后在解决实际问题时提供了有益的指导。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1890469.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ENVI5.6使用笔记

目录 1. ENVI安装扩展2. ENVI绘制高光谱3D数据立体图3. 对本次工作存档&#xff0c;下次打开软件可直接续档4. 对图像进行分类 1. ENVI安装扩展 从ENVI App Store下载商店envi_app_store.zip&#xff0c;解压得到ENVI_App_Store.sav&#xff0c;将其复制到ENVI的扩展文件夹下&…

如何优化网站SEO排名?

选择那些容易排名的关键词。使用工具找到那些竞争少但有流量的词语。其次&#xff0c;内部链接非常重要。通过合理的内部链接&#xff0c;可以提升各个页面的权重。 增加FAQ部分能帮助你捕捉更多的长尾关键词流量。争取出现在精选摘要的位置&#xff0c;可以直接提升你的曝光率…

从全连接到卷积

一、全连接到卷积 1、卷积具有两个原则&#xff1a; 平移不变性&#xff1a;无论作用在哪个部分&#xff0c;它都要有相同的作用&#xff0c;而不会随着位置的改变而改变 局部性&#xff1a;卷积核作用处&#xff0c;作用域应该是核作用点的周围一小部分而不作用于更大的部分 …

仓库管理系统27--项目完结

原创不易&#xff0c;打字不易&#xff0c;截图不易&#xff0c;多多点赞&#xff0c;送人玫瑰&#xff0c;留有余香&#xff0c;财务自由明日实现 1、列表分页 这里我们以物资列表分页为例来讲讲如何实现分页&#xff0c;wpf中没有现成封装好的分页&#xff0c;需要自己封装…

提升Unity WebGL游戏启动速度

一、查看启动耗时 通过修改unity-namespace.js中hideTimeLogModal为false&#xff0c;显示timelog开发者可以看到小游戏目前的启动首屏时长&#xff1a; 将其设置为false后&#xff0c;启动小程序后就会显示启动耗时 要知道各个阶段的含义&#xff0c;我们必要理解启动流程。 …

CDC模型

引言 聚类是一种强大的机器学习方法&#xff0c;用于根据特征空间中元素的接近程度发现相似的模式。它广泛用于计算机科学、生物科学、地球科学和经济学。尽管已经开发了最先进的基于分区和基于连接的聚类方法&#xff0c;但数据中的弱连接性和异构密度阻碍了其有效性。在这项…

基于Java英语学习网站设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;…

玩玩快速冥(LeetCode50题与70题以及联系斐波那契)

一.算法快速幂 今天刷到两个题,比较有意思,还是记录一下. 先来讲讲50题. LeetCode50(Pow(x,n)) 实现 pow(x, n) &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即&#xff0c;xn &#xff09;。 这道题一看很平常啊,不就一直乘嘛,循环走一次就够了.但是很抱歉,单纯的想…

计算机网络知识普及之四元组

在涉及到TCP/UDP等IP类通信协议时&#xff0c;存在四元组概念 这里只是普及使用 先来一些前置知识&#xff0c;什么是IP协议&#xff1f; IP协议全称为互联网协议&#xff0c;处于网络层中&#xff0c;主要作用是标识网络中的设备&#xff0c;每个设备的IP地址是唯一的。 在网…

ASO优化:如何提高应用的可见性

在竞争激烈的应用市场中ASO优化成为了提高应用可见性的重要手段。以下是一些提高应用可见性的方法&#xff1a; 1. 选择合适的关键词 找出和您应用高度相关并且具有一定搜索量的关键词&#xff0c;确保关键词与应用的核心功能和用户搜索意图匹配。比如&#xff1a;当您的应用是…

【Rust入门】生成随机数

文章目录 前言随机数库rand添加rand库到我们的工程生成一个随机数示例代码 总结 前言 在编程中&#xff0c;生成随机数是一种常见的需求&#xff0c;无论是用于数据分析、游戏开发还是模拟实验。Rust提供了强大的库来帮助我们生成随机数。在这篇文章中&#xff0c;我们将通过一…

1.4 ROS2集成开发环境搭建

1.4.1 安装VSCode VSCode全称Visual Studio Code&#xff0c;是微软推出的一款轻量级代码编辑器&#xff0c;免费、开源而且功能强大。它支持几乎所有主流的程序语言的语法高亮、智能代码补全、自定义热键、括号匹配、代码片段、代码对比Diff、GIT 等特性&#xff0c;支持插件…

谈谈浏览器的事件机制:捕获、冒泡

浏览器事件的传播有三个阶段&#xff1a;捕获阶段&#xff0c;目标阶段&#xff0c;冒泡阶段 第一部分称为捕获阶段&#xff0c;该阶段包括从舞台到目标节点的父节点范围内的所有节点。第二部分称为目标阶段&#xff0c;该阶段仅包括目标节点。第三部分称为冒泡阶段。冒泡阶段包…

安装Gitlab+Jenkins

GItlab概述 GitLab概述&#xff1a; 是一个利用 Ruby on Rails 开发的开源应用程序&#xff0c;实现一个自托管的Git项目仓库&#xff0c;可通过Web界面进行访问公开的或者私人项目。 Ruby on Rails 是一个可以使你开发、部署、维护 web 应用程序变得简单的框架。 GitLab拥有与…

工业一体机根据软件应用需求灵活选配

在当今工业领域&#xff0c;数字化、智能化的发展趋势愈发明显&#xff0c;工业一体机作为关键的设备&#xff0c;其重要性日益凸显。而能够根据软件应用需求进行灵活选配的工业一体机&#xff0c;更是为企业提供了高效、定制化的解决方案。 一、工业一体机的全封闭无风扇散热功…

【软件测试】性能测试 | 概念 | 常见术语 | 性能指标 | 分类 | 流程

性能测试 文章目录 性能测试一、什么是性能测试1.生活中遇到的软件的性能问题2.性能测试定义3.性能测试和功能测试有什么区别4.什么样的表现属于软件性能好、什么样的表现不好5.影响一个软件性能因素有哪些 二、为什么进行性能测试三、性能测试的常见术语以及性能测试的衡量指标…

深入了解自动化:聊聊什么项目适合做自动化测试?

自动化测试 什么是自动化测 什么是自动化测试&#xff1f; 随着软件产业的不断发展&#xff0c;市场对软件周期的要求越来越高&#xff0c;于是催生了各种开发模式&#xff0c;如大家熟知的敏捷开发&#xff0c;从而对测试提出了更高的要求。此时&#xff0c;产生了自动化测试…

双指针系列第 8 篇:盛水最多的容器。几句话讲明白!

Leetcode 题目链接 思路 取首尾双指针和水量如下所示&#xff0c;设高度函数为 h ( i ) h(i) h(i)&#xff0c;在下图中 h ( l ) < h ( r ) h(l) < h(r) h(l)<h(r)。 观察以 l l l 为左边界所能构成的其他水量&#xff0c;与矮的右边界搭配结果如下。 与高的…

LLM大模型中LoRA是什么?面试经验回答汇总(2024.7月最新)

目录 1 什么是 LoRA&#xff1f; 2 LoRA 的思路是什么&#xff1f; 3 LoRA 的特点是什么&#xff1f; 4 简单描述一下 LoRA? 5 QLoRA 的思路是怎么样的&#xff1f; 6 QLoRA 的特点是什么&#xff1f; 7 AdaLoRA 的思路是怎么样的&#xff1f; 8 LoRA权重是否可以合入…

乘用车副水箱浮球式液位计传感器

浮球式液位计概述 浮球式液位计是一种利用浮球在液体中浮动的原理来测量液位的设备&#xff0c;广泛应用于各种工业自动化控制系统中&#xff0c;如石油化工、水处理、食品饮料等行业。它通过浮球的上下运动来测量液位的高低&#xff0c;具有结构简单、安装方便、测量范围广、…