人工知能-実験1

最初の実験

1. 実験目的

  • 情報検索戦略のアルゴリズムのアイデアをマスターする
  • 検索アルゴリズムをプログラムする機能
  • A* 検索アルゴリズムを適用してルーマニアの問題を解決する

2. アルゴリズム原理

1. A*検索の評価関数

アルゴリズムAはヒューリスティック アルゴリズムです。A* 検索によるノードの評価には 2 つの部分が含まれます。1 つの部分は、ノードに到達するまでに費やされたコストであり、g(n) として記録されます。もう 1 つの部分は、ノードからターゲットまでの最小コストの推定です。ノード、h.(n) として記録されます。したがって、ノード n を通過する最小コスト解の推定コストは次のようになります。
f (n) = g (n) + h (n) f(n) = g(n) + h(n)f ( n )=g ( n )+h ( n )
ノードが展開されるたびに、最小の f(n) を持つノードが最初に展開されます。ヒューリスティック関数 h(n) が特定の条件を満たしていると仮定すると、
検索は完全かつ最適になります。

2. 最適性を確保するための条件

A* アルゴリズムが最適性を保証するための条件は、許容性と一貫性です。

許容性とは、目標に到達するコストを推定するときに、推定値が実際の値より小さくなければならないこと、つまり、f(n) がノード n を通過する解の実際のコストを超えてはいけないことを意味します。したがって、対象ノードを探索する際には最適解を求める必要があり、これより小さい推定値を持つノードは存在しない。

一貫性はグラフ検索にのみ適用されます。各ノード n と、任意のアクション a を通じて n を生成する各後続ノード n' について、ノード n からゴールに到達する推定コストは、n から n' への単一ステップのコストとゴールに到達するコストを超えない場合、 n からの費用の推定合計。これにより、h(n) が n 経由でターゲット ノードに到達する下限であることが保証されます。
h ( n ) ≤ c ( n , a , n ' ) + h ( n ' ) h(n)\leq c(n,a,n') + h(n')h ( n )c ( n ,_n _+h ( n' )
h(n) が許容できる場合、ツリー検索の A* アルゴリズムが最適であり、h(n) が一貫している場合、グラフ検索の A* アルゴリズムが最適です。探索中、A* アルゴリズムの許容性により、展開されたノードが最小の下限値を持ち、ターゲット ノードが展開されたときに最適解を取得する必要があります。ターゲット ノードの h(n)=g(n) であり、この値は他のノードの下限以下であり、整合性に従って、後で拡張されるターゲット ノードのコストは低くならないため、したがって、最初に展開されたターゲット ノードは最適なソリューションである必要があります。

3. 完全性

完全性には、コストが C* (最適解パスのコスト値) 以下のノードが有限であり、各ステップのコストが ε を超え、b が有限であることが必要です。

3. ルーマニア問題を解決するためのアルゴリズムの実装

ルーマニア問題は、A* アルゴリズムを使用して解くことができるグラフ最適経路探索問題です。評価関数f ( n ) = g ( n ) + h ( n ) f(n)=g(n)+h(n)f ( n )=g ( n )+h ( n )、 g(n) はノード n を拡張するときに計算される、ノードに到達するコストです。 h(n) は、次の式で与えられる直線距離を使用した、ノード n から終点までの推定コストです。質問。ヒューリスティック情報 h(n) は許容性と一貫性を満たすために直線距離を使用するため、この探索方法が最適です。

特定の実装では、各ノードが保存する必要がある情報には、g(n)、h(n)、および f(n)=g(n)+h(n) が含まれます。毎回 f(n) が最小のノードを取り出す必要があるため、 < をオーバーロードするとノードのソートが容易になります。

struct node{
    int g;
    int h;
    int f;
    int name;
    node(int name, int g, int h){
        this->name = name;
        this->g = g;
        this->h = h;
        this->f = g + h;
    };
    bool operator <(const node &a)const{
        return f < a.f;
    }
};

グラフは隣接行列を使用して保存され、すべてのエッジに関する情報を初期化できます。

class Graph{
public:
    Graph()	{memset(graph, -1, sizeof(graph));}
    int getEdge(int from, int to)	{return graph[from][to];}
    void addEdge(int from, int to, int cost){
        if (from >= 20 || from < 0 || to >= 20 || to < 0)
            return;
        graph[from][to] = cost;
    }
	void init(){
        addEdge(O, Z, 71);
		...添加所有边
	}
private:
    int graph[20][20];
};

検索プロセス中、展開されるノードは順序付きキューを形成します。各ノードは 1 回だけ展開され、展開されたノードを記録する必要があります。初期状態では、各ノードの g(n) 値は不明であるため、現在のノードを拡張する場合、g(n) が確実に満たされるように、現在のノードが到達できるすべてのノードの g(n) を更新する必要があります。更新されたノードがキュー内にあるかどうかを迅速に判断するために、ノードが現在のキュー内にあるかどうかを直接記録できます。

bool list[20];				//记录结点是否在当前队列
vector<node> openList;		//扩展队列
bool closeList[20];			//记录结点是否扩展过
stack<int> road;			//记录路径
int parent[20];				//记录路径

検索を開始するときは、まず開始点をキューに追加します。キュー内に展開可能なノードがある限り展開を続け、展開されたノードがターゲット ノードになると検索は終了します。ノードを拡張するときは、まず現在のノードが新しいノードに到達できるかどうか、および新しいノードが拡張されているかどうかを確認します。新しいノードを展開でき、新しいノードがキュー内にある場合は、キューを走査してノードを見つけ、g(n) と f(n) の更新を試みます。それ以外の場合は、新しいノードを直接構築してキューに追加します。現在のノードが展開された後、キューをソートし、最小の f(n) を持つノードが展開のために引き続き取り出されることを確認します。

void A_star(int goal,node &src,Graph &graph){
    openList.push_back(src);
    sort(openList.begin(), openList.end());
    while (!openList.empty()){
        /********** Begin **********/
		node cur = openList[0];
        if(cur.name==goal) return;								//取出目标结点,搜索结束
        openList.erase(openList.begin());
        closeList[cur.name] = true;								//当前结点已扩展
        list[cur.name] = false;									//当前结点出队
        for(int i=0;i<20;i++){
            if(graph.getEdge(cur.name, i)!=-1 && !closeList[i]){
                int cost = cur.g + graph.getEdge(cur.name, i);	//到达下一个城市的代价
                if(list[i]){
                   //更新已有结点
                    for(int j=0;j<openList.size();j++){
                        if(openList[j].name==i){
                            if(openList[j].g>cost){
                               openList[j].g = cost;
                               openList[j].f = openList[j].h + cost;
                               parent[i] = cur.name;
                           }
                           break;
                       }
                   }
               }
               else{
                   //构造新结点
                   node newNode(i, cost, h[i]);
                   openList.push_back(newNode);
                   list[i] = true;
                   parent[i] = cur.name;
               }
            }
        }
        sort(openList.begin(), openList.end());
		/********** End **********/  
    }
}

検索結果が最適解です。

ここに画像の説明を挿入します

ステップ コストが一定の問題の場合、時間計算量の増加は最適解の深さ d の関数であり、ヒューリスティックな絶対誤差と相対誤差によって分析できます。絶対誤差はΔ = h ∗ − h Δ=h^*-hです。D=hhh ∗ h^*hはルートノードからターゲットノードまでの実際のコストです。絶対誤差が最大の場合、A* の時間計算量は指数関数O ( b Δ ) O(b^Δ)O ( bΔ )の場合、各ステップのコストは一定であるため、O ( b ε d ) O(b^{εd})O ( bε d )、d は溶液の深さです。

4. 考える質問

1.幅優先探索、深さ優先探索、一貫コスト探索、反復深化深さ優先探索アルゴリズムのうち、どの方法が最適ですか?

アクションの各ステップのコストが等しい場合、反復深化を伴う幅優先検索と深さ優先検索が最適です。それ以外の場合は、一貫した検索コスト アルゴリズムが最適です。幅優先アルゴリズムは、最も浅いターゲットが有限の深さにある場合に完成しますが、パス コストがノードの深さに基づいて非減少関数である場合にのみ最適です。最も典型的なものは、アクション コストが次の場合です。優先探索と反復深化の深さは同等であり、両者の時間計算量と空間計算量も同じです。一貫したコスト検索では、拡張パスの消費量が最小のノードが最適なノードとなりますが、コストが負ではないため、最初に拡張されたターゲット ノードが最適解となる必要があります。ただし、一貫したコスト検索では、より高価な安価なアクションの検索ツリーを探索する可能性があります。深さ優先検索は完全でも最適でもありません。

ここに画像の説明を挿入します

2. 貪欲な最良の最初の検索と A* 検索のどちらの方法が優れていますか?

A* 検索アルゴリズムが最適です。貪欲なベストファーストアルゴリズムには完全性や最適性はなく、最適解が見つかるかどうかはヒューリスティック関数に依存します。A* 検索アルゴリズムは、許容性と一貫性を満たしている場合に最適であり、分岐が制限されている限り完全です。

3. 未情報検索戦略と情報検索戦略を分析および比較します。

情報なしの検索戦略はブラインド検索であり、解決策を見つけるためにより多くの時間とスペースのオーバーヘッドが必要になる可能性がありますが、優れた汎用性を備えています。情報ベースの検索戦略は、ヒューリスティック関数を通じて問題の追加情報を使用し、検索プロセス中に可能な最適解の方向に進むため、検索効率が向上します。パフォーマンスはヒューリスティック関数の品質に関係します。 。

おすすめ

転載: blog.csdn.net/Aaron503/article/details/131104697