📜  模拟退火

📅  最后修改于: 2021-04-26 09:10:19             🧑  作者: Mango

问题:给定一个成本函数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]