A*-Algorithmus und acht digitale Probleme (numpy)

Fügen Sie hier eine Bildbeschreibung ein

Harte Arbeit darf nicht mittelmäßig sein

Der wichtigste Grund für das Lernen besteht darin, die Mittelmäßigkeit loszuwerden. Einen Tag früher wird es mehr Glanz im Leben geben, einen Tag später wird es einen Tag später mehr Mittelmäßigkeit geben.

Inhaltsverzeichnis

I. Einleitung 

Zweitens: Ideen

1. Identifizieren Sie das Problem und die Ziele:

2. Bestimmen Sie den Algorithmus und die Datenstruktur:

3. Schreiben Sie das Code-Framework

4. Implementieren Sie die Hilfsfunktion:

5. Implementieren Sie die Hauptfunktion:

6. Implementieren Sie den Suchalgorithmus:

7. Implementieren Sie die Zustandsoperationsfunktion:

8. Testen und debuggen:

 3. Code und Funktion

A. Die Methode, der Zweck und die Bedeutung der Codeschritte

① Importieren Sie die erforderliche Bibliothek:

② Definieren Sie die offene Tabelle und die geschlossene Tabelle sowie den Anfangszustand und den Zielzustand:

③ Definieren Sie die Knotenklasse:

④ Definieren Sie Hilfsfunktionen zur Implementierung von Knotenklassen:

⑤ Definieren Sie die Funktion get_reverse_num(state), um die umgekehrte Sequenznummer zu berechnen und die Existenz der Lösung zu beurteilen.

⑥Definieren Sie die Funktionsanzeige (cur_node), um Status- und Tiefeninformationen auszugeben.

⑦ Definieren Sie die Funktion is_in_list(alist, state), um zu prüfen, ob der Status in der Liste enthalten ist.

⑧ Definieren Sie die Gewichtsfunktion Delta (Knoten) für die Sortierung.

⑨Ausgangszustand und Zielzustand eingeben:

⑩Beurteilen Sie, ob es eine praktikable Lösung gibt:

⑪ Implementierung der heuristischen Suche:

B. Funktionsaufbau und Funktion

1. Funktion get_reverse_num(state):

2. Funktionsanzeige (cur_node):

3. Die Funktion is_in_list(alist, state):

4. Funktionsdelta (Knoten):

4. Die Attribute und Funktionen von Staat und Eltern

1. Zustandsattribut:

2. Elternattribut:

Fünftens Problemlösungscode


I. Einleitung 

    Der A*-Algorithmus ist ein heuristischer Graphsuchalgorithmus, der durch die Definition der Bewertungsfunktion gekennzeichnet ist. Wählen Sie für die allgemeine heuristische Diagrammsuche immer den Knoten mit dem kleinsten f-Wert der Bewertungsfunktion als Erweiterungsknoten. Daher schätzt f Knoten basierend auf der Notwendigkeit, einen Pfad mit minimalen Kosten zu finden. Daher kann davon ausgegangen werden, dass der Bewertungsfunktionswert jedes Knotens n zwei Komponenten aufweist: die tatsächlichen Kosten vom Startknoten zu Knoten n und die tatsächlichen Kosten vom Knoten n zu erreichen Die geschätzten Kosten des Zielknotens.

 Nehmen Sie das 8-stellige Problem als Beispiel, um das Lösungsprogramm des A*-Algorithmus zu realisieren.

 Bewertungsfunktion f(n) = g(n) + h(n)

g(n)=d(n) – die Tiefe des Knotens n im Suchbaum

h(n) kann h1(n) wählen – die Anzahl der „nicht in Position“ im Knoten n oder h2(n) =p(n)= die Distanzsumme der „nicht in Position“ der Karte

Zweitens: Ideen

1. Identifizieren Sie das Problem und die Ziele:

   Das Acht-Zahlen-Problem, bei dem ein Anfangszustand durch Verschieben von Zahlen in einen Zielzustand umgewandelt wird. Ziel ist es, ein Programm zu schreiben, das das Acht-Zahlen-Problem automatisch löst.

2. Bestimmen Sie den Algorithmus und die Datenstruktur:

   Wählen Sie den A*-Algorithmus aus und bestimmen Sie dann die erforderliche Datenstruktur, z. B. Knotenklasse, Zustandsdarstellungsmethode usw.

3. Schreiben Sie das Code-Framework

   ① Definieren Sie die erforderlichen Variablen und Datenstrukturen, z. B. offene Tabelle, geschlossene Tabelle, Anfangszustand und Zielzustand usw.

   ②Erstellen Sie eine Knotenklasse und definieren Sie die Eigenschaften und Methoden der Knotenklasse.

4. Implementieren Sie die Hilfsfunktion:

   Implementieren Sie entsprechend den Anforderungen im Code Hilfsfunktionen, z. B. die Funktion find_pos, um die Position der Zahl im Status zu ermitteln.

5. Implementieren Sie die Hauptfunktion:

   ① Schreiben Sie die Hauptfunktion, um Benutzereingaben und -ausgaben zu verarbeiten.

   ② Erhalten Sie den vom Benutzer eingegebenen Anfangszustand und Zielzustand.

   ③ Überprüfen Sie die Existenz einer realisierbaren Lösung und beurteilen Sie, ob es durch Verschieben der Zahlen möglich ist, vom Anfangszustand zum Zielzustand zu gelangen.

   ④ Wenn eine praktikable Lösung existiert, starten Sie den heuristischen Suchprozess.

6. Implementieren Sie den Suchalgorithmus:

   ① Schreiben Sie die Logik der heuristischen Suche in die Hauptfunktion.

   ② Erstellen Sie eine offene Tabelle und eine geschlossene Tabelle und initialisieren Sie sie.

   ③ Verwenden Sie Schleifeniterationen, um zu suchen, bis eine realisierbare Lösung oder keine Lösung gefunden wird.

   ④ In jeder Iteration wird der Status erweitert und gemäß den Algorithmusregeln beurteilt, und die offene Tabelle und die geschlossene Tabelle werden aktualisiert.

7. Implementieren Sie die Zustandsoperationsfunktion:

   Implementieren Sie gemäß den Algorithmusanforderungen Statusoperationsfunktionen, z. B. das Verschieben leerer Zellen, die Beurteilung, ob der Status in der Tabelle enthalten ist usw.

8. Testen und debuggen:

   ① Schreiben Sie Testfälle, einschließlich verschiedener Anfangs- und Zielzustände.

   ②Führen Sie den Code aus und beobachten Sie, ob die Ausgabeergebnisse den Erwartungen entsprechen.

   ③Debuggen und korrigieren Sie Codefehler nach Bedarf.

 3. Code und Funktion

A. Die Methode, der Zweck und die Bedeutung der Codeschritte

① Importieren Sie die erforderliche Bibliothek:

   Hier kommt die NumPy-Bibliothek zum Einsatz, die Funktionen für effiziente Operationen auf mehrdimensionalen Arrays in Python bereitstellt.

② Definieren Sie die offene Tabelle und die geschlossene Tabelle sowie den Anfangszustand und den Zielzustand:

   Die offene Tabelle wird zum Speichern der zu erweiternden Knoten und die geschlossene Tabelle zum Speichern der erweiterten Knoten verwendet. start_state und target_state stellen den Anfangszustand bzw. den Zielzustand dar, bei denen es sich jeweils um zweidimensionale 3x3-Arrays handelt.

③ Definieren Sie die Knotenklasse:

   Hier wird eine Klasse namens Node definiert, die den Knoten im Suchprozess darstellt. Diese Klasse verfügt über einige Eigenschaften und Methoden zum Speichern von Informationen wie Knotenstatus und Berechnungskosten.

④ Definieren Sie Hilfsfunktionen zur Implementierung von Knotenklassen:

   find_pos(self, state, num): Finden Sie die Position des Elements mit dem Wert num im Status state.

   __init__(self, state, prt=[]): Der Konstruktor der Knotenklasse, der den Status, den übergeordneten Knoten, die Kosten und andere Informationen des Knotens initialisiert.

   moveto(self, x, y): Verschieben Sie die leere Zelle an die angegebene Position (x, y), um einen neuen Zustand zu generieren.

⑤ Definieren Sie die Funktion get_reverse_num(state), um die umgekehrte Sequenznummer zu berechnen und die Existenz der Lösung zu beurteilen.

   Bedeutung: Die umgekehrte Ordnungszahl wird verwendet, um zu beurteilen, ob das achtstellige Problem eine zulässige Lösung hat.

⑥Definieren Sie die Funktionsanzeige (cur_node), um Status- und Tiefeninformationen auszugeben.

   Bedeutung: Wird verwendet, um den Status, die Suchtiefe, die Kosten und andere Informationen jedes Knotens während des Suchvorgangs anzuzeigen.

⑦ Definieren Sie die Funktion is_in_list(alist, state), um zu prüfen, ob der Status in der Liste enthalten ist.

   Bedeutung: Wird verwendet, um zu beurteilen, ob sich ein Status bereits in der offenen oder geschlossenen Tabelle befindet.

⑧ Definieren Sie die Gewichtsfunktion Delta (Knoten) für die Sortierung.

Bedeutung: Wird verwendet, um das Sortiergewicht des Knotens anzugeben. Hier wird der F-Wert des Knotens als Sortierbasis verwendet.

⑨Ausgangszustand und Zielzustand eingeben:

   Bedeutung: Verwenden Sie die Eingabefunktion, um nacheinander den vom Benutzer eingegebenen Anfangszustand und Zielzustand abzurufen.

⑩Beurteilen Sie, ob es eine praktikable Lösung gibt:

    Verwenden Sie umgekehrte Ordnungszahlen, um zu bestimmen, ob das achtstellige Problem eine zulässige Lösung hat. Wenn die Parität der Umkehrzahlen unterschiedlich ist, gibt es keine zulässige Lösung.

⑪ Implementierung der heuristischen Suche:

    Bei der heuristischen Suche wird die offene Tabelle zum Speichern der zu erweiternden Knoten und die geschlossene Tabelle zum Speichern der erweiterten Knoten verwendet. Durch kontinuierliches Erweitern der Knoten und Hinzufügen zur offenen Tabelle und zur geschlossenen Tabelle wird schließlich eine praktikable Lösung gefunden. Verwenden Sie während des Suchvorgangs die Delta-Funktion, um die Knoten in der geöffneten Tabelle nach dem F-Wert zu sortieren

Das Obige ist die Analyse der wichtigsten Schritte des Codes.

B. Funktionsaufbau und Funktion

1. Funktion get_reverse_num(state):

   bilden:

          ① Parameter: s t ate, ein zweidimensionales Array, das einen Zustand darstellt.

          ② Rückgabewert: Umkehrsequenznummer, die die Anzahl der Elementpaare in umgekehrter Reihenfolge im Zustand angibt.

   Funktion: Diese Funktion wird verwendet, um die umgekehrte Sequenznummer zu berechnen und zu beurteilen, ob das Acht-Zahlen-Problem eine zulässige Lösung hat. Indem man die nicht leeren Elemente der Reihe nach zu einer Zeichenfolge s verkettet und dann die Anzahl der in s auftretenden umgekehrten Paare zählt, erhält man schließlich die umgekehrte Zahl.

2. Funktionsanzeige (cur_node):

   bilden:

          ① Parameter: cur_node, der das aktuelle Knotenobjekt angibt.

          ② Funktion: Mit dieser Funktion werden der Status, die Suchtiefe, die Kosten und andere Informationen jedes Knotens im Suchprozess angezeigt. Zunächst werden alle Knoten im Suchpfad in einer Liste gespeichert, indem die übergeordneten Knoten des Knotens verfolgt werden. Anschließend durchläuft es die Liste in der Reihenfolge des Suchpfads und gibt die Suchtiefe G, den Statusstatus und die Kosteninformationen G, H und F jedes Knotens aus.

3. Die Funktion is_in_list(alist, state):

   bilden:

           ① Parameter: alist, was eine Liste von Knoten darstellt; state, was ein zweidimensionales Array eines Zustands darstellt.

           ② Rückgabewert: Wenn der Statusstatus in der Liste vorhanden ist, wird das entsprechende Knotenobjekt zurückgegeben. Andernfalls wird -1 zurückgegeben.

   Funktion: Mit dieser Funktion wird beurteilt, ob sich ein Status bereits in der offenen oder geschlossenen Tabelle befindet. Es durchläuft die Knotenobjekte in der Liste und vergleicht den Status jedes Knotens mit dem Eingabestatus. Wenn ein passender Status gefunden wird, wird das entsprechende Knotenobjekt zurückgegeben; andernfalls wird -1 zurückgegeben, was angibt, dass der Status nicht in der Liste enthalten ist.

4. Funktionsdelta (Knoten):

   bilden:

            ① Parameter: Knoten, der ein Knotenobjekt darstellt.

            ② Rückgabewert: die Kosten F des Knotens.

   Funktion: Diese Funktion wird als Sortiergewichtsfunktion verwendet. Bei der heuristischen Suche werden die Knoten in der offenen Liste nach ihrem Kosten-F-Wert sortiert.

4. Die Attribute und Funktionen von Staat und Eltern

state und parent sind zwei wichtige Attribute in der Knotenklasse

1. Zustandsattribut:

   Zusammensetzung: Zustand ist ein zweidimensionales Array, das den Zustand darstellt, der dem Knoten entspricht.

   Funktion: Der Status zeichnet den aktuellen Knotenstatus auf, dh eine bestimmte Anordnung. Durch unterschiedliche Zustandskombinationen können unterschiedliche achtstellige Schachbrettlayouts dargestellt werden.

   Beispiel: Verwendung im Code: Der Status des aktuellen Knotens kann über current_node.state abgerufen werden.

2. Elternattribut:

   Zusammensetzung: Parent ist eine Liste, die Verweise auf übergeordnete Knoten speichert.

   Funktion: Parent zeichnet den übergeordneten Knoten des aktuellen Knotens auf, dh den oberen Knoten, der auf den aktuellen Knoten im Suchbaum zeigt.

   Beispiel: Verwendung im Code: Der übergeordnete Knoten des aktuellen Knotens kann über current_node.parent abgerufen werden. Dies ist nützlich, um Suchpfade zurückzuverfolgen und Lösungen zu demonstrieren, da ein Suchpfad durch die Verfolgung der übergeordneten Knoten vom Zielknoten bis zurück zum ursprünglichen Knoten erstellt werden kann.

Bei einer heuristischen Suche generiert jeder Knoten einen neuen Status, indem er leere Zellen verschiebt und ihn als untergeordneten Knoten zum Suchbaum hinzufügt. Durch Festlegen und Aktualisieren der Status- und übergeordneten Attribute kann ein Suchbaum erstellt, der Suchpfad verfolgt und schließlich der Pfad zur Lösung des achtstelligen Problems gefunden werden.

Fünftens Problemlösungscode

import numpy as np

# 定义open表与close表
open_list = []  # 存放待扩展的节点
close_list = []  # 存放已扩展的节点
start_state = np.zeros((3, 3), dtype=int)  # 初始状态,3×3矩阵,先全设为零
target_state = np.zeros((3, 3), dtype=int)  # 目标状态,3×3矩阵,先全设为零

# 定义节点类
# 定义了一个名为Node的类,表示搜索过程中的节点。该类具有一些属性和方法,用于存储节点状态、计算代价等信息。
class Node:
    G = 0  # 起点到当前节点的最短路径代价值
    H = 0  # 当前节点到目标节点的最短路径代价值
    F = 0  # F = G + H,起点经过当前节点到目标节点的最短路径的总代价值
    state = np.zeros((3, 3), dtype=int)  # 一个二维数组,记录了当前节点的状态,即八数码问题中的一个具体排列。通过不同的状态组合,可以表示不同的八数码棋盘布局。
    parent = []  # 一个列表,记录了当前节点的父节点,即在搜索树中指向当前节点的上一层节点。


    #find_pos(self, state, num)是节点类中的一个方法,用于找到指定数字在状态中的位置
    # self:表示节点对象自身。  state:表示一个状态的二维数组。  num:表示要查找的数字。  返回一个包含两个元素的元组(i, j),表示数字num在状态state中的行索引和列索引。
    def find_pos(self, state, num):             # 在状态state中查找值为num的元素的位置x,y
        for i in range(len(state)):
            for j in range(len(state[i])):
                if state[i][j] == num:
                    return i, j

    # 节点类的构造函数,初始化节点的状态、父节点、代价等信息。
    def __init__(self, state, prt=[]):
        self.state = state
        if prt:
            self.parent = prt
            self.G = prt.G + 1
        for i in range(len(state)):
            for j in range(len(state[i])):
                x, y = self.find_pos(target_state, state[i][j])
                self.H = self.H + abs(x - i) + abs(y - j)
        self.F = self.G * 1 + self.H * 10

    # 将空白格移动到指定位置(x, y),生成新的状态。
    def moveto(self, x, y):
        x0, y0 = self.find_pos(self.state, 0)
        newstate = self.state.copy()
        tmp = newstate[x0][y0]
        newstate[x0][y0] = newstate[x][y]
        newstate[x][y] = tmp
        return newstate

# 得到逆序数,用于判断解的存在性
# 逆序数用于判断八数码问题是否有可行解
def get_reverse_num(state):
    ans = 0
    s = ""
    for i in range(len(state)):
        for j in range(len(state[i])):
            if not state[i][j] == 0:
                s += str(state[i][j])

    for i in range(len(s)):
        for j in range(i):
            if s[j] > s[i]:
                ans += 1
    return ans

# 输出状态及深度信息
# 用于展示搜索过程中每个节点的状态、搜索深度、代价等信息
def display(cur_node):
    alist = []
    tmp = cur_node
    while tmp:
        alist.append(tmp)
        tmp = tmp.parent
    alist.reverse()
    for node in alist:
        print("搜索深度%d" % node.G)
        print(node.state)
        print("G={0},H={1},F={2}".format(node.G, node.H, node.F))
        print()
    print("总共探索了%d次" % int(node.G + 1))

# 检查state状态是否在list中(可能是open或close表)
# 用于判断一个状态是否已经在open表或close表中
def is_in_list(alist, state):
    for stat in alist:
        if (stat.state == state).all():
            return stat
    return -1

# 排序的权值函数
# 用于指定节点的排序权值,这里使用节点的F值作为排序依据
def delta(node):
    return node.F

# 输入初始与目标状态
# 使用输入函数逐个获取用户输入的初始状态和目标状态
for i in range(len(start_state)):
    for j in range(len(start_state[i])):
        start_state[i][j] = input("start_state" + "(" + str(i + 1) + "," + str(j + 1) + "):")

print("the start state is:")
print(start_state)

for i in range(len(target_state)):
    for j in range(len(target_state)):
        target_state[i][j] = input("target_state" + "(" + str(i + 1) + "," + str(j + 1) + "):")

print("the target state is:")
print(target_state)

# 可行解判断
# 使用逆序数判断八数码问题是否有可行解。如果逆序数的奇偶性不同,则不存在可行解
if get_reverse_num(target_state) % 2 != get_reverse_num(start_state) % 2:
    print(get_reverse_num(target_state))
    print(get_reverse_num(start_state))
    print("找不到可行解!")
    exit(-1)

# 可行解存在时,开始启发搜索
# 在启发式搜索中,使用open表存放待扩展的节点,close表存放已扩展的节点。
# 通过不断扩展节点,并将其加入open表和close表,最终找到可行解。搜索过程中,使用delta函数对open表中的节点按照F值进行排序
open_list.append(Node(start_state))
while open_list:
    current_node = open_list.pop(0)  # 从open表中取出F值最小的节点进行扩展
    close_list.append(current_node)  # 将扩展的节点加入close表
    # 当open表中取出的恰好为目标状态时,找到可行解
    if (current_node.state == target_state).all():
        print("可行解已找到!")
        display(current_node)
        exit(0)
    # 否则对当前节点进行拓展
    x, y = current_node.find_pos(current_node.state, 0)  # 找到空白格的位置
    for [x_, y_] in [[x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]]:
        if 0 <= x_ < len(start_state) and 0 <= y_ < len(start_state):
            new_state = current_node.moveto(x_, y_)  # 移动空白格得到新状态
            # 判断新状态是否在close表
            if is_in_list(close_list, new_state) == -1:
                # 如果不在close表
                if is_in_list(open_list, new_state) == -1:
                    # 如果也不在open表,则将其加入open表
                    open_list.append(Node(new_state, current_node))
                else:
                    # 如果open表中已存在这种状态,则选取G值较小的
                    index = is_in_list(open_list, new_state)
                    if current_node.G + 1 < open_list[index].G:
                        # 如果新路线更好,则放弃旧路线而选择新路线
                        open_list.pop(index)
                        open_list.append(Node(new_state, current_node))
                    # 否则忽略
    # 对open表按F值从小到大进行排序
    open_list.sort(key=delta)

Je suppose que tu aimes

Origine blog.csdn.net/m0_63794226/article/details/131191603
conseillé
Classement