模拟退火算法
摘至 百度百科
模拟退火算法来源于固体退火原理,是一种基于概率的算法,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小。
模拟退火算法(Simulated Annealing,SA)最早的思想是由N. Metropolis [1] 等人于1953年提出。1983 年,S. Kirkpatrick 等成功地将退火思想引入到组合优化领域。它是基于Monte-Carlo迭代求解策略的一种随机寻优算法,其出发点是基于物理中固体物质的退火过程与一般组合优化问题之间的相似性。模拟退火算法从某一较高初温出发,伴随温度参数的不断下降,结合概率突跳特性在解空间中随机寻找目标函数的全局最优解,即在局部最优解能概率性地跳出并最终趋于全局最优。模拟退火算法是一种通用的优化算法,理论上算法具有概率的全局优化性能,目前已在工程中得到了广泛应用,诸如VLSI、生产调度、控制工程、机器学习、神经网络、信号处理等领域。 模拟退火算法是通过赋予搜索过程一种时变且最终趋于零的概率突跳性,从而可有效避免陷入局部极小并最终趋于全局最优的串行结构的优化算法。
LeetCode 5707. 得到新鲜甜甜圈的最多组数
题目描述:
题目分析:
题目等价于
找到一个序列 使得 这个序列 能分成最多的组数,每组满足组中元素和为 batchSize 的倍数
如果用暴力的话 : 30 ! > 10 ^ 30 一定会超时
所以可以使用 模拟退火 来解决
具体看代码来理解分析
class Solution {
public:
/*
模拟退火 算法 :
一个可以尽量达到全局最优的算法
每次随机交换序列中的两个位置 来判断当前的序列是否可以比原始序列更优
1. 若比原始序列更优,则保留交换
2. 否则按一定概率保留交换 (这样才有机会达到全局最优)
*/
int b, n;
vector<int> w;
int ans;
int cal() {
int res = 0;
int s = 0;
for(int i = 0;i < w.size();i++) {
s += w[i];
if(s % b == 0) {
res++;
s = 0;
}
}
if(s > 0) 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) {
// 模拟退火的过程 t 为 温度 慢慢下降的过程
// 越到后面整个状态要更趋于稳定
// 即当 t 越低 更趋向于保持原状态
int i = rand() % n;
int j = rand() % n;
int x = cal();// 原先序列的 "值"
swap(w[i],w[j]);// 交换两个位置
int y = cal();// 交换后序列的 "值"
int delta = y - x;// 评价函数
if(delta < 0 && (double) rand() / RAND_MAX <= exp(- 1 * delta / t)) {
// 当 y 小于 x 时 , 一定概率 exp(-1 * delta / t) 保留操作
swap(w[i],w[j]);
}
}
return ;
}
int maxHappyGroups(int b_, vector<int>& g) {
b = b_;
if(b == 1) return g.size();
w.clear();
// 小的优化,在序列中不加入 取模后为 0 的数
int res = 0;
for(auto& i : g) {
if(i % b == 0) res++;
else w.push_back(i % b);
}
n = w.size();
if(n < 2) return res + n;
ans = 0;
for(int i = 0;i < 10;i++) {
simulate_anneal();
// 循环多次模拟退火算法,参数可自己设置
}
return ans + res;
}
};
模拟退化的模板 :
int main() {
for(int i = 0;i < 10;i++) {
simulate_anneal();
// 循环多次模拟退火算法,参数可自己设置
}
}
// 模拟退火 算法
void simulate_anneal() {
random_shuffle(w.begin(),w.end());
// 初始化一个序列
for(double t = 1e6;t > 1e-5;t *= 0.97) {
// 模拟退火的过程 t 为 温度 慢慢下降的过程
// 越到后面整个状态要更趋于稳定
// 即当 t 越低 更趋向于保持原状态
int i = rand() % n;
int j = rand() % n;
int x = cal();// 原先序列的 "值"
swap(w[i],w[j]);// 交换两个位置
int y = cal();// 交换后序列的 "值"
int delta = y - x;// 评价函数
if(delta < 0 && (double) rand() / RAND_MAX <= exp(- 1 * delta / t)) {
// 当 y 小于 x 时 , 一定概率 exp(-1 * delta / t) 保留操作
swap(w[i],w[j]);
}
}
return ;
}