问题:给定一个成本函数f:R ^ n –> R ,找到一个使f值最小的n元组。请注意,最小化函数的值在算法上等效于最大化(因为我们可以将成本函数重新定义为1-f)。
你们中许多具有微积分/分析背景的人可能熟悉单变量函数的简单优化。例如,函数f(x)= x ^ 2 + 2x可以优化,将一阶导数设置为零,获得解x = -1产生最小值f(-1)= -1 。该技术足以满足简单的功能,并且只需很少的变量。但是,通常情况下,研究人员会对优化多个变量的功能感兴趣,在这种情况下,只能通过计算获得解决方案。
困难的优化任务的一个很好的例子是芯片布局计划问题。假设您在英特尔工作,而您要负责设计集成电路的布局。您拥有一组具有不同形状/尺寸的模块,以及一个可以放置模块的固定区域。您需要实现许多目标:最大化连接组件的电线的能力,最小化净面积,最小化芯片成本等。考虑到这些,您将创建一个成本函数,采用所有1000种可变配置并返回表示输入配置的“成本”的单个实际值。我们将此称为目标函数,因为目标是使其价值最小化。
天真的算法将是一个完整的空间搜索-我们搜索所有可能的配置,直到找到最小值。对于少量变量的函数来说,这可能就足够了,但是我们要考虑的问题将是需要在O(n!)中使用这种蛮力算法。
由于此类问题以及其他NP难题的计算难点,因此开发了许多优化试探法,试图产生一个良好的(尽管可能是次优的)值。在我们的案例中,我们不一定需要找到严格的最佳值-找到接近最佳的值可以满足我们的目标。模拟退火是一种广泛使用的技术,通过这种技术,我们引入了一定程度的随机性,可能会从较好的解决方案转变为较差的解决方案,以逃避局部最小值并收敛到更接近全局最优值的程度。
模拟退火基于冶金实践,通过冶金实践将材料加热到高温并冷却。在高温下,原子可能发生不可预料的位移,随着材料冷却成纯净的晶体,通常会消除杂质。这通过模拟退火优化算法进行复制,能量状态对应于当前解。
在此算法中,我们定义一个初始温度(通常设置为1)和一个最低温度(约10 ^ -4)。当前温度乘以某个分数α,因此降低直到达到最低温度。对于每个不同的温度值,我们将核心优化例程运行固定次数。优化例程包括找到一个相邻解并以概率e ^(f(c(c)– f(n)))接受它,其中c是当前解, n是相邻解。通过对当前解决方案施加一些微扰,可以找到一个相邻的解决方案。这种随机性有助于避免优化启发式方法的常见陷阱-陷入局部极小值。通过潜在地接受一个比我们目前不那么理想的解决方案,并以与成本增加成反比的概率来接受它,该算法更有可能收敛于全局最优值附近。设计邻居函数非常棘手,必须逐案进行,但是下面是在位置优化问题中寻找邻居的一些想法。
- 将所有点沿随机方向移动0或1个单位
- 随机移动输入元素
- 交换输入序列中的随机元素
- 置换输入序列
- 将输入序列划分为随机数量的段和置换段
一个警告是我们需要提供一个初始解决方案,以便算法知道从哪里开始。这可以通过两种方式完成:(1)使用有关问题的先验知识输入一个良好的起点,以及(2)生成随机解。尽管生成随机解的效果较差,并且偶尔会抑制算法的成功,但是对于我们对景观一无所知的问题,这是唯一的选择。
还有许多其他优化技术,尽管模拟退火对于大型,离散的搜索空间是一种有用的随机优化启发式算法,在这种搜索空间中,最优性是随时间优先考虑的。下面,我为基于位置的模拟退火提供了一个基本框架(也许是模拟退火最适合的优化方式)。当然,尽管已经实现了核心优化例程,但是必须根据当前的特定问题定义成本函数,候选生成函数和邻居函数。
// Java program to implement Simulated Annealing
import java.util.*;
public class SimulatedAnnealing {
// Initial and final temperature
public static double T = 1;
// Simulated Annealing parameters
// Temperature at which iteration terminates
static final double Tmin = .0001;
// Decrease in temperature
static final double alpha = 0.9;
// Number of iterations of annealing
// before decreasing temperature
static final int numIterations = 100;
// Locational parameters
// Target array is discretized as M*N grid
static final int M = 5, N = 5;
// Number of objects desired
static final int k = 5;
public static void main(String[] args) {
// Problem: place k objects in an MxN target
// plane yielding minimal cost according to
// defined objective function
// Set of all possible candidate locations
String[][] sourceArray = new String[M][N];
// Global minimum
Solution min = new Solution(Double.MAX_VALUE, null);
// Generates random initial candidate solution
// before annealing process
Solution currentSol = genRandSol();
// Continues annealing until reaching minimum
// temprature
while (T > Tmin) {
for (int i=0;i Math.random())
currentSol = newSol;
}
T *= alpha; // Decreases T, cooling phase
}
//Returns minimum value based on optimiation
System.out.println(min.CVRMSE+"\n\n");
for(String[] row:sourceArray) Arrays.fill(row, "X");
// Displays
for (int object:min.config) {
int[] coord = indexToPoints(object);
sourceArray[coord[0]][coord[1]] = "-";
}
// Displays optimal location
for (String[] row:sourceArray)
System.out.println(Arrays.toString(row));
}
// Given current configuration, returns "neighboring"
// configuration (i.e. very similar)
// integer of k points each in range [0, n)
/* Different neighbor selection strategies:
* Move all points 0 or 1 units in a random direction
* Shift input elements randomly
* Swap random elements in input sequence
* Permute input sequence
* Partition input sequence into a random number
of segments and permute segments */
public static Solution neighbor(Solution currentSol){
// Slight perturbation to the current solution
// to avoid getting stuck in local minimas
// Returning for the sake of compilation
return currentSol;
}
// Generates random solution via modified Fisher-Yates
// shuffle for first k elements
// Pseudorandomly selects k integers from the interval
// [0, n-1]
public static Solution genRandSol(){
// Instantiating for the sake of compilation
int[] a = {1, 2, 3, 4, 5};
// Returning for the sake of compilation
return new Solution(-1, a);
}
// Complexity is O(M*N*k), asymptotically tight
public static double cost(int[] inputConfiguration){
// Given specific configuration, return object
// solution with assigned cost
return -1; //Returning for the sake of compilation
}
// Mapping from [0, M*N] --> [0,M]x[0,N]
public static int[] indexToPoints(int index){
int[] points = {index%M, index/M};
return points;
}
// Class solution, bundling configuration with error
static class Solution {
// function value of instance of solution;
// using coefficient of variance root mean
// squared error
public double CVRMSE;
public int[] config; // Configuration array
public Solution(double CVRMSE, int[] configuration) {
this.CVRMSE = CVRMSE;
config = configuration;
}
}
}
输出 :
-1.0
[X, -, X, X, X]
[-, X, X, X, X]
[-, X, X, X, X]
[-, X, X, X, X]
[-, X, X, X, X]