C#,数值计算——基于模拟退火的极小化问题单纯形(下山)算法的计算方法与C#源程序

1 模拟退火

模拟退火算法其实是一个类似于仿生学的算法,模仿的就是物理退火的过程。
我们炼钢的时候,如果我们急速冷凝,这时候的状态是不稳定的,原子间杂乱无章的排序,能量很高。而如果我们让钢水慢慢冷凝,很缓慢的降温,那么这个时候的状态就是很稳定的,各个分子都趋向于自己能量最低的位置。而模拟退火算法,恰恰就是利用了物理退火这一过程的原理,求解一个优化目标(目标函数)的最小值。

模拟退火算法来源于固体退火原理,是一种基于概率的算法,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小。
 

2 模拟退火算法

模拟退火算法(Simulated Annealing,SA)最早由Metropolis等人于 1953 年提出。1983 年Kirkpatrick等人第一次使用模拟退火算法求解组合优化问题后,它就发表在了 Science 上。直到今天,它依然被广泛使用,这篇文章将详细介绍C#的代码实现。
 

3 单纯形法

单纯形法是针对求解线性规划问题的一个算法,这个名称里的'单纯形'是代数拓扑里的一个概念
实际问题当中变量x是多维的,约束条件也会比示例多的多,这就需要一个一劳永逸的算法能通过计算机来获得正解,单纯形法就是这样的一个算法。单纯形法最早由 George Dantzig于1947年提出,单纯形法对于求解线性规划问题是具有跨时代意义的,其实不仅仅是针对线性规划,对于非线性规划问题在求解的过程中也大量依赖单纯形法,George Dantzig本人也被称为'线性规划之父',他是好莱坞电影《心灵捕手》的原型。
 

4 C#源程序

using System;

namespace Legalsoft.Truffer
{
    ///


    /// 模拟退火下山单纯形极小化
    /// Downhill simplex minimization with simulated annealing
    ///

    public class Amebsa
    {
        public RealValueFun funk;

        public Ranq1 ran;
        private int mpts { get; set; }
        private int ndim { get; set; }
        private double[] pb { get; set; }
        private double[] y { get; set; }
        private double[,] p { get; set; }
        private double ftol { get; }
        private double yb { get; set; }
        private double tt { get; set; }

        public Amebsa(double[] point, double del, RealValueFun funkk, double ftoll)
        {
            this.funk = funkk;
            this.ran = new Ranq1(1234);
            this.ftol = ftoll;
            this.yb = double.MaxValue;
            this.ndim = point.Length;
            this.pb = new double[ndim];
            this.mpts = ndim + 1;
            this.y = new double[mpts];
            this.p = new double[mpts, ndim];
            for (int i = 0; i < mpts; i++)
            {
                for (int j = 0; j < ndim; j++)
                {
                    p[i, j] = point[j];
                }
                if (i != 0)
                {
                    p[i, i - 1] += del;
                }
            }
            inity();
        }

        public Amebsa(double[] point, double[] dels, RealValueFun funkk, double ftoll)
        {
            this.funk = funkk;
            this.ran = new Ranq1(1234);
            this.ftol = ftoll;
            this.yb = double.MaxValue;
            this.ndim = point.Length;
            this.pb = new double[ndim];
            this.mpts = ndim + 1;
            this.y = new double[mpts];
            this.p = new double[mpts, ndim];
            for (int i = 0; i < mpts; i++)
            {
                for (int j = 0; j < ndim; j++)
                {
                    p[i, j] = point[j];
                }
                if (i != 0)
                {
                    p[i, i - 1] += dels[i - 1];
                }
            }
            inity();
        }

        public Amebsa(double[,] pp, RealValueFun funkk, double ftoll)
        {
            this.funk = funkk;
            this.ran = new Ranq1(1234);
            this.ftol = ftoll;
            this.yb = double.MaxValue;
            this.ndim = pp.GetLength(1);
            this.pb = new double[ndim];
            this.mpts = pp.GetLength(0);
            this.y = new double[mpts];
            this.p = pp;

            inity();
        }

        public void inity()
        {
            double[] x = new double[ndim];
            for (int i = 0; i < mpts; i++)
            {
                for (int j = 0; j < ndim; j++)
                {
                    x[j] = p[i, j];
                }
                y[i] = funk.funk(x);
            }
        }

        public bool anneal(ref int iter, double temperature)
        {
            double[] psum = new double[ndim];
            tt = -temperature;
            get_psum(p, psum);
            for (; ; )
            {
                int ilo = 0;
                int ihi = 1;
                double ylo = y[0] + tt * Math.Log(ran.doub());
                double ynhi = ylo;
                double yhi = y[1] + tt * Math.Log(ran.doub());
                if (ylo > yhi)
                {
                    ihi = 0;
                    ilo = 1;
                    ynhi = yhi;
                    yhi = ylo;
                    ylo = ynhi;
                }
                for (int i = 3; i <= mpts; i++)
                {
                    double yt = y[i - 1] + tt * Math.Log(ran.doub());
                    if (yt <= ylo)
                    {
                        ilo = i - 1;
                        ylo = yt;
                    }
                    if (yt > yhi)
                    {
                        ynhi = yhi;
                        ihi = i - 1;
                        yhi = yt;
                    }
                    else if (yt > ynhi)
                    {
                        ynhi = yt;
                    }
                }
                double rtol = 2.0 * Math.Abs(yhi - ylo) / (Math.Abs(yhi) + Math.Abs(ylo));
                if (rtol < ftol || iter < 0)
                {
                    Globals.SWAP(ref y[0], ref y[ilo]);
                    for (int n = 0; n < ndim; n++)
                    {
                        Globals.SWAP(ref p[0, n], ref p[ilo, n]);
                    }
                    if (rtol < ftol)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                iter -= 2;
                double ytry = amotsa(p, y, psum, ihi, ref yhi, -1.0);
                if (ytry <= ylo)
                {
                    ytry = amotsa(p, y, psum, ihi, ref yhi, 2.0);
                }
                else if (ytry >= ynhi)
                {
                    double ysave = yhi;
                    ytry = amotsa(p, y, psum, ihi, ref yhi, 0.5);
                    if (ytry >= ysave)
                    {
                        for (int i = 0; i < mpts; i++)
                        {
                            if (i != ilo)
                            {
                                for (int j = 0; j < ndim; j++)
                                {
                                    psum[j] = 0.5 * (p[i, j] + p[ilo, j]);
                                    p[i, j] = psum[j];
                                }
                                y[i] = funk.funk(psum);
                            }
                        }
                        iter -= ndim;
                        get_psum(p, psum);
                    }
                }
                else
                {
                    ++iter;
                }
            }
        }

        public void get_psum(double[,] p, double[] psum)
        {
            for (int n = 0; n < ndim; n++)
            {
                double sum = 0.0;
                for (int m = 0; m < mpts; m++)
                {
                    sum += p[m, n];
                }
                psum[n] = sum;
            }
        }

        public double amotsa(double[,] p, double[] y, double[] psum, int ihi, ref double yhi, double fac)
        {
            double[] ptry = new double[ndim];
            double fac1 = (1.0 - fac) / ndim;
            double fac2 = fac1 - fac;
            for (int j = 0; j < ndim; j++)
            {
                ptry[j] = psum[j] * fac1 - p[ihi, j] * fac2;
            }
            double ytry = funk.funk(ptry);
            if (ytry <= yb)
            {
                for (int j = 0; j < ndim; j++)
                {
                    pb[j] = ptry[j];
                }
                yb = ytry;
            }
            double yflu = ytry - tt * Math.Log(ran.doub());
            if (yflu < yhi)
            {
                y[ihi] = ytry;
                yhi = yflu;
                for (int j = 0; j < ndim; j++)
                {
                    psum[j] += ptry[j] - p[ihi, j];
                    p[ihi, j] = ptry[j];
                }
            }
            return yflu;
        }
    }
}
 

你可能感兴趣的:(C#数值计算,Numerical,Recipes,算法,c#,数值计算,模拟退火算法)