模擬退火 (poj 2420, poj 2069)
阿新 • • 發佈:2017-05-27
mes 全局 poj include using div tracking out amp
模擬退火基本知識
其偽代碼例如以下:
樣例:
poj 2420
題意:
平面上給你n個點(xi,yi),讓你求一個點。到這n點的距離和最小。
poj 2069 Super Star
題意:
給定n個點(xi, yi, zi), 要求覆蓋這些點的最小球半徑。
其偽代碼例如以下:
Let s = s0 For k = 0 through k_max (exclusive): T := temperature(k / k_max) Pick a random neighbour, s_new := neighbour(s) If P(E(s), E(s_new), T) > random(0, 1), move to the new state: s := s_new Output: the final state s
樣例:
poj 2420
題意:
平面上給你n個點(xi,yi),讓你求一個點。到這n點的距離和最小。
限制:
1 <= n <= 100
0 <= xi,yi <= 1e4, 為整數
/*poj 2420 題意: 平面上給你n個點(xi,yi),讓你求一個點,到這n點的距離和最小。 限制: 1 <= n <= 100 0 <= xi,yi <= 1e4, 為整數 思路: 模擬退火 模擬退火基本知識: 其偽代碼例如以下: Let s = s0 For k = 0 through k_max (exclusive): T := temperature(k / k_max) Pick a random neighbour, s_new := neighbour(s) If P(E(s), E(s_new), T) > random(0, 1), move to the new state: s := s_new Output: the final state s */ #include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> using namespace std; const double E = exp(1.0); struct Pt{ double x,y; }p[105]; double sqr(double x){ return x*x; } double dis(Pt a, Pt b){ return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y)); } double get_sum(Pt p0, int n){ double ret = 0; for(int i = 0; i < n; ++i) ret += dis(p0, p[i]); return ret; } int main(){ srand(1123); int n; int limit = 10000; //(x,y)的範圍 while(scanf("%d", &n) != EOF){ double x0 = 0, y0 = 0; for(int i = 0; i < n; ++i){ scanf("%lf%lf", &p[i].x, &p[i].y); x0 += p[i].x; y0 += p[i].y; } x0 /= n; y0 /= n; double ans = get_sum((Pt){x0, y0}, n); double temp = 1e5; //初始溫度, 依據題目改動 while(temp > 0.02){ //0.02為溫度的下限, 若溫度temp達到下限, 則停止搜索 double x = 0, y = 0; for(int i = 0; i < n; ++i){ //獲取步長的規則依據題目而定 x += (p[i].x - x0) / dis((Pt){x0, y0}, p[i]); y += (p[i].y - y0) / dis((Pt){x0, y0}, p[i]); } double tmp = get_sum((Pt){x0 + x * temp, y0 + y * temp}, n); //標函數E(x_new); if(tmp < ans){ ans = tmp; x0 += x * temp; y0 += y * temp; } else if(pow(E, (ans - tmp) / temp) > (rand() % limit) / (double)limit){ ans = tmp; x0 += x * temp; y0 += y * temp; } temp *= 0.9; //0.9為降溫退火速率, (範圍為0~1, 越大得到全局最優解的概率越高, 執行時間越長 } printf("%.0f\n", ans); } return 0; }
poj 2069 Super Star
題意:
給定n個點(xi, yi, zi), 要求覆蓋這些點的最小球半徑。
限制:
4 <= n <= 30
0 <= xi, yi, zi <= 100
/*poj 2069 Super Star 題意: 給定n個點(xi, yi, zi), 要求覆蓋這些點的最小球半徑。 限制: 4 <= n <= 30 0 <= xi, yi, zi <= 100 思路: 模擬退火 */ #include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> using namespace std; const double E = exp(1.0); struct Pt{ double x, y, z; }p[35]; double sqr(double x){ return x * x; } double dis(Pt a, Pt b){ return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y) + sqr(a.z - b.z)); } double get_max_r(Pt p0, int n){ double ret = 0; for(int i = 0; i < n; ++i) ret = max(ret, dis(p0, p[i])); return ret; } int main() { int n; int limit = 100; while(scanf("%d", &n) && n){ double x0 = 0, y0 = 0, z0 = 0; for(int i = 0; i < n; ++i){ scanf("%lf%lf%lf", &p[i].x, &p[i].y, &p[i].z); x0 += p[i].x; y0 += p[i].y; z0 += p[i].z; } x0 /= n; y0 /= n; z0 /= n; //cout<<x0<<‘ ‘<<y0<<‘ ‘<<z0<<endl; double ans = get_max_r((Pt){x0, y0, z0}, n); double temp = 1e5; while(temp > 1e-8){ double x = 0, y = 0, z = 0; double max_r = 0; for(int i = 0; i < n; ++i){ double r = dis((Pt){x0, y0, z0}, p[i]); if(r > max_r){ x = (p[i].x - x0) / r; y = (p[i].y - y0) / r; z = (p[i].z - z0) / r; max_r = r; } } double tmp = get_max_r((Pt){ x0 + x * temp, y0 + y * temp, z0 + z * temp}, n); if(tmp < ans){ ans = tmp; x0 += x * temp; y0 += y * temp; z0 += z * temp; } else if(pow(E, (ans - tmp) / temp) > (rand() % limit) / (double)limit){ ans = tmp; x0 += x * temp; y0 += y * temp; z0 += z * temp; } temp *= 0.998; } printf("%.5f\n", ans); } return 0; }
模擬退火 (poj 2420, poj 2069)