现在的位置: 首页 > 综合 > 正文

用模拟退火法挑战建立信号基站的问题

2013年03月21日 ⁄ 综合 ⁄ 共 2493字 ⁄ 字号 评论关闭

挑战建立信号基站问题成功,下面说说具体的想法。

模拟退火法其实很简单,分如下几步:

1、首先确定评价函数。

2、选取初始点,并用评价函数计算初始值,把初始值作为当前值。

3、按步进规则获取新点

4、计算新点的值(也可能是附近的多个值)。

5、比较新值和当前值差距,如果小于预期,执行步骤6,否则按一定概率执行步骤6。

6、那么取把新值作为当前值,新点作为当前点,执行步骤3。

7、更新步进值,执行步骤3。

首先说评价函数,在这个问题中,即为选取点到所有基站的距离和。在这里说一下,网上不少人用欧式空间距离,即sqrt((x1-x0)*(x1-x0),(y1-y0)*(y1-y0)),增加了计算时间。其实没必要,完全可以用max(|x1-x0|,|y1-y0|)。因为作为空间长度的度量只要满足如下几个条件就可以了:

    1、||A,A||=0;

    2、||A,B||=||B,A||

    3、||A,B||+||B,C||>=||A,C||

大家可以证明下,max(|x1-x0|,|y1-y0|)是可以作为空间距离度量的。

初始点选取,我就选择了第一点,没什么道理,随便选的一个,反正最后都会收敛的。

唯一修改的地方就是步进值得选择上,稍稍改进了一下,判断新值和原值的差距,如果相差大,就大幅调整步进,否则小幅调整步进。

贴上代码

package pango.bestdistance;

public class Main {

	public static double getDistance(double[] pointA, double[] pointB) {
		return Math.max(Math.abs(pointA[0] - pointB[0]),
				Math.abs(pointA[1] - pointB[1]));
	}

	public static double getSumDistance(double[][] points, double[] point0) {
		double result = 0;
		for (int i = 0; i < points.length; i++) {
			result += getDistance(points[i], point0);
		}
		return result;
	}

	public static double bestDistance(int[] x, int[] y) {
		if (x == null || y == null || x.length != y.length)
			throw new UnknownError();
		double[] nextPoint = new double[2];
		double[] curPoint = new double[2];
		double[][] direct = new double[][] { { 0, 1 }, { 0, -1 }, { 1, 0 },
				{ -1, 0 }, { 0.7071, 0.7071 }, { 0.7071, -0.7071 },
				{ -0.7071, 0.7071 }, { -0.7071, -0.7071 }, };
		double step = 100000000;
		double r = 0.99;
		double chance = 0.99;
		double precision = 0.0001;
		double[][] points = new double[x.length][2];
		for (int i = 0; i < x.length; i++) {
			points[i][0] = x[i];
			points[i][1] = y[i];
		}
		curPoint[0] = points[0][0];
		curPoint[1] = points[0][1];
		double result = getSumDistance(points, curPoint);
		while (Double.compare(step, precision) > 0) {
			boolean ok = true;
			double good = 0.0;
			while (ok) {
				ok = false;
				for (int i = 0; i < direct.length; i++) {
					nextPoint[0] = direct[i][0] * step + curPoint[0];
					nextPoint[1] = direct[i][1] * step + curPoint[1];
					if (Double.compare(nextPoint[0], -1000000000.0) < 0
							|| Double.compare(nextPoint[1], -1000000000.0) < 0) {
						continue;
					}
					double t = getSumDistance(points, nextPoint);
					if(Double.compare(result, 0.0) != 0 ) {
						good=Math.abs(1.0 - Math.abs(t - result / result));
					}else {
						good=Math.abs(1 - Math.abs(t));
					}
					if (Double.compare(t, result) < 0) {
						result = t;
						curPoint[0] = nextPoint[0];
						curPoint[1] = nextPoint[1];
						ok = true;
					} else if (Math.random() > chance) {
						result = t;
						curPoint[0] = nextPoint[0];
						curPoint[1] = nextPoint[1];
						ok = true;
					}
				}
			}
			step = Double.compare(good, 1) > 0 ? step * r : step * good * 0.9;
		}
		return result;
	}

	public static void main(String[] args) {
		/***
		 * (1,4) (2,3) (0,1) (1,1)
		 */
		int[] x = new int[] { 1, 2, 0, 1 };
		int[] y = new int[] { 4, 3, 1, 1 };
		System.out.println(bestDistance(x, y));
	}
}

解释一下69行那个步长更新,原来解释不是很清楚,实际上是越接近最优点,步长减的更慢,我称之为让子弹多飞一会儿。道理嘛,是根据我的本专业《金属材料》知识,凭直觉加的那么一句。步长的更新速度实际上等同于温度降低的快慢,让温度降的慢一点,就更有利于原子的向更低的能级组成方式排列。

抱歉!评论已关闭.