模擬退火算法解決最近最遠問題

/**
    模擬退火算法真的很巧妙,而且很多問題也能轉換成這個算法
    這個算法最大的妙處,就是它會無序的向着你所要求的答案去
    尋找,直到找到符合你的精度,概率很高

    如:在一個1024*768的平面上有N 個點,現在要你在這個面上找個
    點到這N個點的距離和最小,精度保留 5 位小數,有個很簡單的辦法
    就是枚舉,當然不能這麼幹,而模擬退火算法是處理這類問題的典範

    當然它不僅僅用於處理找點,你知道,網絡流就是求最大流而已,可是
    卻用處很多,所以說好多問題,都可一轉換成模擬退火問題,尤其那些
    沒有確定算法,需要暴力的題,包括今年上海賽區第一題也是可以用這個
    算法,不過會超時。

    接着回到那個題,找一個點到這些點距離最小,模擬退火的思路就是,先任意
    在這個面內找個點 st ,最好在這些點中間,設有個半徑爲 T,以T爲半徑
    st 爲圓心可以包括所有的N 個點。好了,下面開始找了,一般 8 個方向就夠了
    4個其實也差不多,對於有些題要求很高,需要更多的方向,現在就以4 個
    方向分析,上下左右,st點的上下左右,st_up, st_down, st_left, st_right.
    比如:st_up.x = st.x, st_up.y = st.y + T。 在這四個點裏面挑個離得最近的
    把st 移到這裏,然後繼續找直到T 的半徑下再也找不到更近的點,現在T *= 0.5
    繼續重複上面步驟找,要知道,每次讓T 減一般,很快就會減到你想要的精度。

    具體看代碼

*/


typedef struct POINT {
    double x, y;
}Point;


int main() {


    Point st, st_tmp, temp; //st_tmp, temp臨時點
    double T, T_MIN; // T要求把所有點覆蓋,T_MIN可得到的最大精度 比如 T_MIN = 1e-6
    double ans;      //目前可得的最小距離


    //輸入完成後

    st_tmp = st;
    ans = dis(st);  //dis() 函數由自己寫,返回st 離所有點的距離之和

    while (T > T_MIN) {
        int flag;
        while (flag) {
            flag = 0;  //flag 的妙處
            for (int i=-1; i<=1; i++)
                for (int j=-1; j<=1; j++) { //八個方向,根據需要自己看着辦
                    temp.x = st.x + T*i;
                    temp.y = st.y + T*j;
                    double tmp_d = dis(temp);
                    if (tmp_d < ans) {
                        ans = tmp_d;
                        st_tmp = temp;
                        flag = 1;       //在T 半徑上找到個比st 更小的點
                    }
                }

            st = st_tmp;        //如果flag = 0,說明在T 半徑的8個方向上沒有比st 更優的點
        }

        T = 0.5 * T;    //這個也是根據情況,不過一般0.5就行了,越大越精確,速度也越慢
    }

    // 得到 st 爲最近點,ans 爲最近距離和,最遠一樣,改改即可

    return 0;
}

/**
    睡覺了,睡覺了,晚安

*/

 

收藏於 2012-01-13
來自於百度空間

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章