模拟退火算法

Posted 兮何其

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模拟退火算法相关的知识,希望对你有一定的参考价值。

eg1:
Leecode

class Solution 
public:
    int maxHappyGroups(int batchSize, vector<int>& groups) 
        w = groups;
        n = w.size();
        m = batchSize;
        ans = 0;
        for(int i = 0; i < 80; i++) simulate_anneal();
        return ans;
    

private:
    int n, m;
    vector<int> w;
    int ans;

    int calc() 
        int res = 1;
        for(int i = 0, s = 0; i < n; i++) 
            s = (s + w[i]) % m;
            if(!s && i < n - 1) res++;
        
        ans = max(ans, res);
        return res;
    

    void simulate_anneal() 
        random_shuffle(w.begin(), w.end());
        for(double t = 1e6; t > 1e-5; t *= 0.97) 
            int a = rand() % n, b = rand() % n;
            int x = calc();
            swap(w[a], w[b]);
            int y = calc();
            int delta = x - y;
            if(!(exp(-delta / t) > (double)rand() / RAND_MAX)) 
                swap(w[a], w[b]);
            
        
    
;

random_shuffle()函数:可以对一个容器或者数组进行重排,但是重拍规律是相同的,如果要每次都不同,需要加入第三个参数,即随机种子参数


 
eg2:
Leecode

func(arr, l, r) 就是返回 arr[l..r] 子区间的与值;
固定 l,然后随着 r 的增加,func(arr, l, r) 单调非递增,变成了一个凹函数
枚举l,然后模拟退火求出一个最优 r。这样就得到了 n 个局部最优的区间 [l,r],在这些区间中最优的那个就是答案

class Solution 
public:
    
    //通过预处理,快速求解arr[L..R]的与值
    int pre[100001][20] = 0;
    
    int get(int L, int R, int target) 
        int val = 0;
        for(int i = 0, bit = 1; i < 20; i++, bit <<= 1) 
            // 如果第 i 个bit 在 [L,R] 中全为 1,那么与值的该bit也必然为 1。
            if(pre[R][i] - pre[L-1][i] == R-L+1) 
                val |= bit;   
            
        
        return abs(val-target);
    
    
    // 用模拟退火求解关于 L 的局部最优解
    int query(int L, int n, int target) 
        int dir[2] = -1, 1;  // 两个方向
        int step = 1000; // 初始步长
        int now = L; // R 的起始位置
        int best = 100000000; // 局部最优解
        
        while(step > 0) 
            int Lpos = now + step*dir[0];
            if(Lpos < L) 
                Lpos = L;
            
            int Rpos = now + step*dir[1];
            if(Rpos > n) 
                Rpos = n;
            
            // 向左右两个方向各走一步,求值
            int ldis = get(L, Lpos, target);
            int rdis = get(L, Rpos, target);
            int pbest = best;
            
            //更新位置及最优解
            if(ldis < best) 
                now = Lpos;
                best = ldis;
            
            if(rdis < best) 
                now = Rpos;
                best = rdis;
            
            
            //如果没有找到更优解,那就缩小步长
            if(pbest == best) 
                step /= 2;
            
        
        return best;
    
    
    int closestToTarget(vector<int>& arr, int target) 
        int anw = 100000000;
        
        //统计前 i 个数字中,第 j 个bit 为 1 的数量。
        for(int i = 0; i < arr.size(); i++) 
            for(int j = 0, bit = 1; j < 20; j++, bit <<= 1) 
                pre[i+1][j] = pre[i][j] + ((bit&arr[i]) ? 1 : 0);
            
        
        
        for(int i = 1; i <= arr.size(); i++) 
            anw = min(anw, query(i, arr.size(), target));
        
        
        return anw;
    
;

以上是关于模拟退火算法的主要内容,如果未能解决你的问题,请参考以下文章

Python数模笔记-模拟退火算法求解旅行商问题的联合算子模拟退火算法

Python数模笔记-模拟退火算法求解旅行商问题的联合算子模拟退火算法

模拟退火算法通俗讲解

Matlab:数模06-模拟退火模型

模拟退火算法浅谈

模拟退火算法