A星算法和 IDA星算法

这两种算法是我在搜索中除了 DLX 所见过的最适合用于搜索的算法了。可以把 A * 看作广搜的进化,再把 IDAstar 看作 A * 的时间换空间形式。

首先讨论 A * 算法。其实我觉得他和广搜很相像,但是又有如下的要素:

1.估价函数 f = g + h,由两个部分组成: g 和 h,分别表示初始状态到当前状态的距离和当前状态到目标状态的估计距离。

2.每次扩展最优节点。即每次从所有已扩展的节点中选取估价函数值最小的节点先扩展。

3.估价函数中的 h <= h *,即估计距离一定要不大于最优距离。这是很关键的一点:这直接关系到 A * 算法是否同于 A 算法,还有所搜到的结果是否为最优解。

如何实现呢?首先,需要设计满足条件 1 和 3 的估价函数计算公式,并且 h 的值越大(在满足不大于 h * 时),搜索越快。在广搜中,可以把 h 函数视作 0,所以也可以说广搜就是最垃圾的 A * 算法。再者,为了满足条件 2,需要每次快速取出最优的节点扩展。毫无疑问堆是最合适的。

这样,A * 算法即:在广搜的基础上加上 h <= h * 的估价函数,然后将队列换成优先队列(堆)。

再来讨论 IDA *。首先我们需要知道什么是迭代加深深搜。

有的时候,一个局面可以搜索很多很多层,这样一般的深搜肯定会挂。怎么办呢?在某些限制了搜索深度的情况下,可以每次限值当前搜索的深度,然后对每一个深度下进行搜索。

听起来这是在浪费时间,几乎每次都要重复搜索原来搜过的状态。但是这样又有一个显而易见的好处:如果所需要的是最优解,那么就可以避免扩展完所有的状态(深搜),就可以得到最优解。 

那为什么不用广搜呢?显然,在某些搜索中,超过 20 层所需的空间就可以爆掉 512 M 了。而迭代加深深搜很好的用时间弥补了这种不足。所以说,他是介于广搜和深搜之间的一种算法。

那什么是 IDA * 呢?显然,就是加上和 A * 算法中的估价函数的迭代加深深搜。如何利用估价函数呢?显然就是估价函数的不超过当前所限制的深度。由于 h <= h *,所以结果最优绝对可以保证。

这样,IDA * 算法即:在迭代加深深搜的基础上加上 h <= h * 的估价函数,然后用所限制的深度限值估价函数。

那么他们的效率如何呢?不妨在 15 数码问题下来讨论。

首先是时间效率。根据测试结果,有  A * >= 双向广搜 >= IDA *(其实和双向广搜差不多) >= 广搜 (>= 深搜,不过没人写而已)。

再者是空间效率。显然,IDA * 远优于其他所有可行的算法。

测评结果如下:先后为广搜、IDA*、A *(A * 后面的错误是因为卡了空间)





下面分别上 A * 和 IDA * 的代码:

#include 
#include 
#include 
#include 
#include 
#include 

typedef long long int64;
typedef unsigned int uint;
typedef unsigned long long uint64;

#define swap(a, b, t) ({t _ = (a); (a) = (b); (b) = _;})
#define MAX(a, b, t) ({t _ = (a), __ = (b); _ > __ ? _ : __;})
#define MIN(a, b, t) ({t _ = (a), __ = (b); _ < __ ? _ : __;})

typedef int maintype;
#define max(a, b) MAX(a, b, maintype)
#define min(a, b) MIN(a, b, maintype)

#define maxs 18
#define maxn 200005
#define abs(a) ({int _ = a; _ < 0 ? - _ : _;})
#define opt(a) (a[maxs - 1])
#define data(u) ((u)->f + (u)->g)

const int ppp[maxs] = {1, 2, 6, 24, 120, 720, 5040, 40320, 
    362880, 3628800, 39916800, 479001600, 227020758, 
    1178290605, 1674359019, 789744213, 1073741823};

typedef int gate[maxs];
gate a, b, c, d;
int cost[maxs][maxs], hash[maxn];
int n, m, dis, top, tot, p, q, f, g, temp, ha;
struct node{gate a; node * pre; int f, g;};
node vess[maxn], * h[maxn], * u, * v;

void up(int p)
{
    for (; (p >> 1) and data(h[p]) < data(h[p >> 1]); p >>= 1)
        swap(h[p], h[p >> 1], node *);
}

void down(int p)
{
    while ((p << 1) <= tot)
        if ((p << 1) == tot)
        {
            if (data(h[p << 1]) < data(h[p]))
                swap(h[p << 1], h[p], node *);
            break;
        }
        else
        {
            int q = data(h[p << 1]) < data(h[p << 1 | 1]) ? (p << 1) : (p << 1 | 1);
            if (data(h[q]) < data(h[p])) swap(h[q], h[p], node *), p = q; else break;
        }
}

node * pop()
{
    node * p = h[1];
    return h[1] = h[tot --], down(1), p;
}

void push(gate a, node * pre, int f, int g)
{
    h[++ tot] = vess + ++ top;
    memcpy(h[tot]->a, a, sizeof (gate));
    h[tot]->pre = pre, h[tot]->f = f, h[tot]->g = g, up(tot);
}

int cantor(gate a)
{
    int k = 0;
    for (int i = m; i; -- i)
        k += a[i] * ppp[i];
    return (k & 0xFFFFFFF) % maxn;
}

void printans(node * u)
{
    if (opt(u->pre->a)) printans(u->pre);
    printf("%d\n", opt(u->a));
}

void work()
{
    for (int i = m; i; -- i)
        if (a[i]) dis += cost[i][c[a[i]]];
    hash[cantor(a)] = dis;
    for (push(a, 0, dis, 0); top + 5 < maxn; )
    {
        u = pop(), p = u->a[0], g = u->g + 1;
        for (int i = 1; i <= 4; ++ i)
        {
            if ((q = p + d[i]) < 1 or q > m or cost[p][q] != 1) continue;
            f = u->f - cost[q][c[u->a[q]]] + cost[p][c[u->a[q]]];
            if (g + f > 30) continue;
            if (f == 0)
            {
                printf("%d\n", g);
                printans(u), printf("%d\n", i);
                exit(0);
            }
            swap(u->a[p], u->a[q], int);
            if (hash[ha = cantor(u->a)])
                if (hash[ha] < f) continue; else;
            else hash[ha] = f;
            u->a[0] = q, temp = opt(u->a), opt(u->a) = i;
            push(u->a, u, f, g);
            u->a[0] = p, opt(u->a) = temp;
            swap(u->a[p], u->a[q], int);
        }
    }
    puts("No");
}

void init()
{
    scanf("%d", & n), m = n * n;
    d[1] = - n, d[2] = n, d[3] = - 1, d[4] = 1;
    for (int i = 1; i <= m; ++ i)
    {
		scanf("%d", & a[i]);
        if (a[i] == 0) a[0] = i;
    }
    for (int i = 1; i <= m; ++ i)
		scanf("%d", & b[i]), c[b[i]] = i;
	for (int i = m; i; -- i)
		for (int j = m; j; -- j)
		{
			cost[i][j] += abs((i - 1) / n - (j - 1) / n);
			cost[i][j] += abs((i - 1) % n - (j - 1) % n);
		}
}

int main()
{
    freopen("gate.in", "r", stdin);
    freopen("gate.out", "w", stdout);
    
    init();
    work();
    
    return 0;
}


#include 
#include 
#include 
#include 
#include 
#include 

typedef long long int64;
typedef unsigned int uint;
typedef unsigned long long uint64;

#define swap(a, b, t) ({t _ = (a); (a) = (b); (b) = _;})
#define MAX(a, b, t) ({t _ = (a), __ = (b); _ > __ ? _ : __;})
#define MIN(a, b, t) ({t _ = (a), __ = (b); _ < __ ? _ : __;})

typedef int MAINTYPE;
#define max(a, b) MAX(a, b, MAINTYPE)
#define min(a, b) MIN(a, b, MAINTYPE)

#define maxs 18
#define maxd 30

typedef int gate[maxs];
gate a, b, c, d;
int n, m, dis, cost[maxs][maxs];
int now, temp, opt[maxd + 5];

void printans(int i, int g)
{
	printf("%d\n", now);
	for (int j = 1; j < g; ++ j)
		printf("%d\n", opt[j]);
	printf("%d\n", i);
	exit(0);
}

void dfs(int g, int h, int p)
{
	for (int i = 1, q; i <= 4; ++ i)
	{
		if ((q = p + d[i]) < 1 or q > m or cost[p][q] != 1) continue;
		int gg = g + 1, hh = h - cost[q][c[a[q]]] + cost[p][c[a[q]]];
		if (gg + hh > now) continue;
		if (hh == 0) printans(i, gg);
		swap(a[p], a[q], int);
		opt[gg] = i, dfs(gg, hh, q);
		swap(a[p], a[q], int);
	}
}

void work()
{
	for (int i = 1; i <= m; ++ i)
		if (a[i]) dis += cost[i][c[a[i]]];
	for (now = 1; now <= maxd; ++ now)
		dfs(0, dis, a[0]);
	puts("No");
}

void init()
{
	scanf("%d", & n), m = n * n;
	d[1] = - n, d[2] = n, d[3] = - 1, d[4] = 1;
	for (int i = 1; i <= m; ++ i)
		scanf("%d", & a[i]), a[0] = a[i] ? a[0] : i;
	for (int i = 1; i <= m; ++ i)
		scanf("%d", & b[i]), c[b[i]] = i;
	for (int i = 1; i <= m; ++ i)
		for (int j = 1; j <= m; ++ j)
			cost[i][j] = abs((i - 1) / n - (j - 1) / n) + abs((i - 1) % n - (j - 1) % n);
}

int main()
{
    freopen("gate.in", "r", stdin);
    freopen("gate.out", "w", stdout);
    
    init();
	work();
    
    return 0;
}
可以看出,代码量和效率的比值上,IDA * 的性价比是相当高的。




你可能感兴趣的:(算法,扩展,c,up,测试)