精华内容
下载资源
问答
  • 算法设计技巧与分析

    热门讨论 2016-04-26 21:49:18
    算法设计技巧与分析》同时也强调了每一种算法的详细的复杂性分析。全书分七部分19章,从算法设计和算法分析的基本概念和方法入手,先后介绍了递归技术、分治、动态规划、贪心算法、图的遍历等技术,NP完全问题...
  • 面试之排序算法

    千人学习 2019-12-24 15:54:46
    排序算法是我们面试被问到最多的基础算法,本课程详细介绍了七种排序算法,包括插入排序、选择排序、冒泡...每种算法都详细介绍了核心思想、详细步骤、时间复杂度和代码实现,希望帮助大家深入理解排序算法,搞定面试!
  • 算法设计与分析学习总结

    千次阅读 多人点赞 2019-12-11 01:27:04
    通过算法设计与分析》这门课的学习,似乎算法有了一定的了解,之前算法并没有太多的接触,只是一些普通的编程。选课的时候,觉得特别有意思,就选了这门课,通过学习各类算法,有了一定的认识。递归法,分治...

    算法设计与分析学习总结

    通过对《算法设计与分析》这门课的学习,似乎对算法有了一定的了解,之前对算法并没有太多的接触,只是一些普通的编程。选课的时候,觉得特别有意思,就选了这门课,通过学习各类算法,有了一定的认识。递归法,分治法,蛮力法,回溯法。贪心算法等等,对之后的编都有很大的帮助。

    现在,我深刻的明白这门课的对软件工程专业的重要性,程序的编写都离不开它,对培养思维能力也有一定的帮助,还能培养我们思考分析、解决问题的能力。对于一个问题,运用不同的算法都会有不同的解决方法,空间复杂度和时间复杂度都不太相同,合适的算法能更加高效,更加便捷的完成问题。算法可以用自然语言,伪代码,流程图等来描述,各种程序设计,软件设计,数据库设计,系统设计归根到底都要用算法来完成。

    在课堂上,老师讲的各种方法,虽说有一些不太理解,经过课后的学习,也有了一定的认识,结合课后作业,每一题都是对每一种算法针对性的练习,巩固了所学知识。因为专业所学的编程语言就是java,所以都是运用Java语言来实现的,对其他语言没有去深入了解,应该也都是差不多的解决问题。现阶段学习到的都是算法的一些皮毛,之后会继续深入学习,体验其中的奥妙。

    接下来是一些方法的理解,其中包括课堂学过的各种方法。

    **递归法:**能采用递归描述的算法通常有这样的特征:为求解规模为N的问题,设法将它分解成规模较小的问题,然后从这些小问题的解方便地构造出大问题的解,并且这些规模较小的问题也能采用同样的分解和综合方法,分解成规模更小的问题,并从这些更小问题的解构造出规模较大问题的解。特别地,当规模N=1时,能直接得解。

    **分治法:**对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。分治法在每一层递归上都有三个步骤,1.分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;2.解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;3.合并:将各个子问题的解合并为原问题的解。

    **蛮力法:**也称为穷举法或者枚举法,它是算法设计中最常见的方法之一。蛮力法的基本思路是问题的所有可能状态一一测试,直到找到解或者全部可能状态都测试为止。蛮力法的有点有很多例如应用范围广,不受实例规模的限,当要解决的问题低频率出现,并且高效算法很难设计时可选蛮力法,对解决一些小规模问题实例仍然有效,可作为衡量其他算法的参照物等等。

    **回溯法:**是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。 运用回溯法解题的关键要素有三点,1.针对给定的问题,定义问题的解空间;2.确定易于搜索的解空间结构;3.以深度优先方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。

    **分枝限界法:**是一个用途十分广泛的算法,运用这种算法的技巧性很强,不同类型的问题解法也各不相同。分枝限界法的基本思想是对有约束条件的最优化问题的所有可行解(数目有限)空间进行搜索。在每次分支后,对凡是界限超出已知可行解值那些子集不再做进一步分支。这样,解的许多子集(即搜索树上的许多结点)就可以不予考虑了,从而缩小了搜索范围。这一过程一直进行到找出可行解为止,该可行解的值不大于任何子集的界限。因此这种算法一般可以求得最优解。

    **贪心算法:**指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。贪心算法的过程:1.建立数学模型来描述问题;2.把求解的问题分成若干个子问题;3.对每一子问题求解,得到子问题的局部最优解;4.把子问题的解局部最优解合成原来解问题的一个解。例:设a、b为互质正整数,a<b 分数a/b 可用以下的步骤分解成若干个单位分数之和:步骤一: 用b 除以a,得商数q1 及余数r1。(r1=b - a*q1),步骤二:把a/b 记作:a/b=1/(q1+1)+(a-r1)/b(q1+1),步骤三:重复步骤2,直到分解完毕。

    **动态规划算法:**将待求解的问题分解为若干个子问题,先求解子问题,然后从这些子问题得到原问题的解。经动态规划法得到的子问题往往不是相互独立的,为了避免重复计算,我们可以用一个表来记录所有已解决的子问题的答案。它常用于求解具有最优子结构性质(问题的最优解包含了子问题的最优解)和子问题重叠性质(在用递归算法自顶向下的解决问题时,每次产生的子问题并不总是新问题,有些问题被反复计算多次)的问题。基本步骤:1.找出最优解的性质,并刻画其特征;2.递归地定义最优值;3.以自底向上的方式计算出最优值;4.根据计算最优值时得到的信息,构造一个最优解。

    除此之外还有很多的算法,暂时没有深入去了解和学习,通过以上是多种算法的描述,明白了算法是对问题的模型化,求解的算法化和设计最优解。对于繁琐的计算机问题有了一定的解决方法,算法这门课也对我的学习起到了承前启后的作用,大二学习数据结构,对编程有了一定的了解,结合算法分析,假以时日,对我的编程能力一定有很大的帮助,对数据库的学习也有很大的帮助,以上便是我对《算法设计与分析》这门课的学习心得。
    2019.12.11
    广理丶小泽

    在这里插入图片描述

    展开全文
  • 算法——个人对算法的一些理解

    千次阅读 2018-02-05 14:08:49
    个人对算法的一些理解  在学校的同学们之间,算法总是被放在一个非常高的位置,有多高呢?嗯...就是非常非常高啦,高到有人只要能说出几个非常牛掰的和算法有关的名词,比如NP完全问题啦、模拟退火啦就觉得自己是...

                         个人对算法的一些理解

          在学校的同学们之间,算法总是被放在一个非常高的位置,有多高呢?嗯...就是非常非常高啦,高到有人只要能说出几个非常牛掰的和算法有关的名词,比如NP完全问题啦、模拟退火啦就觉得自己是大神了(其实有些人连快排都写不出来,或者写个快排还要先翻翻书)。其实我觉得算法跟数据结构、设计模式一样,都只是一种工具,用这样的工具能快速有效地解决一些实际问题,如果只知道算法,而不懂这些算法能够用于哪些场景,那么这些算法在这些人的大脑中是没有什么意义的。
         根据我的经验,算法对于大一的学生是“那个人算法很厉害,大神大神”,对于大二"你听说过模拟退火过没?你听说过遗传算法没?你听说过反馈神经网络没?现在的AI算法可以......",对于大三"嗯,算法很重要,以后工作有比没有好,工作中经常用到,平时有空闲的时间多研究研究,对未来的成长大大的有好处"。
          算法其实就是对一个问题或一类问题的解决过程的描述。比如说用加法求1加到100,那么就可以用
    count=0
    for i in 1...100
        count += i
    来描述。这个描述的过程就是不断的累加。算法非常关注于复杂度,也就是时间复杂度和空间复杂度。有些算法可以让时间用得尽量少,而有些算法为了节约空间,会用牺牲时间的方式来换取少用空间。不过,由于现在的物理储存设备的空间越来越大,空间暂时不是非常重要,除非在一些特殊的场景,比如说在大规模数据的情况下。当数据只有1M的时候,空间增加100倍没有什么很大的问题,但是如果数据有1T的话,再增加100倍后的数据量,是很难让人接受的。对于一个算法,我们经常需要在时间和空间之间进行取舍。
          算法跟程序不同,算法是对一个问题解决过程的描述,需要有0个或多个输入,至少1个输出,每个过程都是确定的,并且一定要有有限的时间内完成。但程序就不一定,有些程序可以一直运行下去,只有一直给机器供电,程序一直不出现致命错误。
          算法也可以一种模型,数据结构是对数据储存的模型,而算法是对问题解决过程的模型。数据结构是为了更方便的储存和访问数据,算法是为了更有效率地处理数据。所以算法和数据结构是不分家的,没有数据结构,算法根本没有数据可以操作,算法也无法进行,而没有算法,数据结构光储存有数据,这些数据不经处理,也大部分是无用的。
          经过无数计算机算法前辈的研究,已经有非常多现成的算法供我们学习和使用。算法并非是计算机专有的,或者说算法的思想来源于很多其他行业。比如说前面说到的模拟退火算法。算法还涉及到了很多微积分、线性代数、离散数学、概率论、博弈论、数论方面的知识。
          
    展开全文
  • 遗传算法

    万次阅读 多人点赞 2019-04-06 21:41:47
    使用遗传算法求解多峰函数的最大值,是我的一项课程作业,做完之后,顺便把文档整理出来做个记录。全部内容如下: 1、问题描述 编程实现遗传算法,并求解多峰函数的最大值。多峰函数的表达式如下所示: 用MATLAB...

    使用遗传算法求解多峰函数的最大值,是我的一项课程作业,做完之后,顺便把文档整理出来做个记录。全部内容如下:

    1、问题描述

    编程实现遗传算法,并求解多峰函数的最大值。多峰函数的表达式如下所示:
    在这里插入图片描述
    用MATLAB做出函数的图像如下:
    在这里插入图片描述

    2、算法描述及实现

    2.1、遗传算法概述

    遗传算法(GA,Genetic Algorithm),也称为进化算法。遗传算法是受达尔文的进化论的启发,借鉴生物进化过程而提出的一种启发式搜索算法。其主要特点是直接对结构对象进行操作,因此不同于其他求解最优解的算法,遗传算法不存在求导和对函数连续性的限定,采用概率化的寻优方法,不需要确定的规则就能自动获取和指导优化的搜索空间,自适应地调整搜索方向。

    以上是对遗传算法相对抽象的总结,为了更具体形象的解释遗传算法的一般原理,我们首先介绍一些生物学上的概念

    ①种群:不同生物个体形成的群体,生物的进化以群体的形式进行,这样的一个群体称为种群;

    ②个体:组成种群的单个生物;

    ③基因:带有遗传信息的DNA片段,可以通俗的将基因理解为一段信息,这段信息决定的生物个体的性状;

    ④表现型:根据基因形成的个体的外部表现;

    ⑤适应度:生物个体对于生存环境的适应程度,越适应那么其得以存活和繁衍的概率就越大;

    ⑥遗传:通过繁殖过程,子代将从父母双方各获取一部分基因,形成新的自己的基因,这个过程中,会发生基因的复制、交叉,也会以较低的概率发生基因突变;

    ⑦自然选择:物竞天择,适者生存的自然淘汰机制。具体为对环境适应度高的个体参与繁殖的机会比较多,后代就会越来越多。适应度低的个体参与繁殖的机会比较少,后代就会越来越少;

    ⑧进化:种群通过代际繁衍不断适应生存环境的过程,在这个过程中,以对外界环境的适应度为评判标准,生物的性状不断得到改良。

    了解了这些术语的含义,我们就可以进一步说说生物进化的过程了。由于自然选择是客观存在的,即生物只能改变自己去适应环境,那么在自然选择的过程中,适应度低的个体会被淘汰,适应度高的个体被保留,高适应度的父体与母体又有更高的概率繁衍出适应度高的子代,因此在一代又一代的繁衍之后,高适应度的个体在种群中所占的比例越来越大,种群就这样完成了进化。

    现在我们要参考生物进化的过程来设计算法解决求最优解的问题。对此,遗传算法的思路是,将要解决的问题模拟成一个生物进化的过程,通过进化来寻找最优解。以我们题目中寻找多峰函数的最大值这个问题为例

    将(x, y)这一可能的解作为一个个体;将多峰函数的函数值f(x, y)作为个体的适应度;对(x, y)进行编码作为个体的基因;以适应度为标准不断筛选生物个体;通过遗传算子(如复制、交叉、变异等)不断产生下一代。如此不断循环迭代,完成进化。最终,根据设定的迭代次数,可得到最后一代种群,该种群中的个体适应度都较高,而多峰函数的最大值就有比较大的概率存在于这一群解中,以种群中适应度最高的个体作为问题的解,则可以说该解有比较高的概率就是我们希望求得的最优解。

    文字述说终究还是不如图表好理解,因此还是看图吧(下图将本题与自然遗传联系了起来):
    在这里插入图片描述
    通过以上描述,我们不难看出,遗传算法不能保证一定能求得最优解,而只能以一定的概率求最优解。但是使用遗传算法时,我们可以不用关心具体如何去找最优解,要做的只是简单的否定一些表现不好的个体。这一优点也是遗传算法能够取得广泛应用的原因之一。

    2.2、算法的流程

    通过上文的阐述,对于如何模拟自然进化来求题中多峰函数的最优解已经比较明晰了。这里我将列出遗传算法的主要步骤,并一一解析:

    第一步:随机产生一个种群,作为问题的初代解(通常,初代解可能与最优解相差较大,这是可以容忍的,只要保证初代解是随机产生的,以确保个体基因的多样性即可);

    第二步:寻找一种合适的编码方案对种群中的个体进行编码,可以选择如浮点数编码或二进制编码等常用编码方案(需要指出的是,不同的编码方案直接影响后续遗传算子的实现细节);

    第三步:以多峰函数的函数值 作为个体的适应度,计算种群中每个个体的适应度(算出的适应度将为后续的个体选择提供依据);

    第四步:根据适应度的高低选择参与繁衍的父体与母体,选择的原则是适应度越高的个体越可能被选中(以此不断淘汰适应度低的个体);

    第五步:对被选出的父体与母体执行遗传操作,即复制父体与母体的基因,并采用交叉、变异等算子产生出子代(在较大程度保留优秀基因的基础上,变异增加了基因的多样性,从而提高找到最优解的概率);

    第六步:根据一定的准则判断是继续执行算法,还是找出所有子代中适应度最高个体作为解返回并结束程序(判断的准则可以是设定的解的阈值、指定的迭代次数等)。
    在这里插入图片描述

    2.3、算法的编码实现

    2.3.1、编码

    本文采用的是二进制编码方式,这种编码方式编解码过程简单易行,相应的交叉算子、变异算子等操作用位运算即可实现。当然,它也有一定的缺点,比如连续性不够强。为保证求解的精度,本文使用14个bit为 编码,使用11个bit为 编码,两者组合成25个bit的最终结果。不难算出,该方式的编码精度可达千分位。具体的编码操作可总结为,将 或 的取值区间映射到0~2n-1这一整数范围,其中n表示编码位数, 或 在其取值区间的某点,相应的映射到整数区间中的某点,改点即为 或 的基因编码。程序如下:

    /*
        基因编码
        gene1       输入型参数,待编码的基因段1
        gene2       输入型参数,待编码的基因段2
        gene_code   输出型参数,基因编码
    
        返回值:当输入的基因不符合要求时返回false,否则返回true
    */
    static bool gene_encode(const double gene1, const double gene2, unsigned int *gene_code)
    {
        /* 判断基因是否合法 */
        if (!is_gene_legal(gene1, gene2))
            return false;
    
        /* 若基因合法则对其进行编码 */
        unsigned int gene1_code = (gene1 - GENE1_RANGE_LEFT) * (GENE1_CODE_MAX - 1) / (GENE1_RANGE_RIGHT - GENE1_RANGE_LEFT);
        unsigned int gene2_code = (gene2 - GENE2_RANGE_LEFT) * (GENE2_CODE_MAX - 1) / (GENE2_RANGE_RIGHT - GENE2_RANGE_LEFT);
        
        /* 组合基因片段 */
        *gene_code = (gene1_code << 11) | gene2_code;
    
        return true;
    }
    

    2.3.2、解码

    解码是编码的逆过程,无需赘述,程序如下:

    /*
        基因解码
        gene_code   输入型参数,基因编码
        gene1       输出型参数,解码后的基因段1
        gene2       输出型参数,解码后的基因段2
    
        返回值:当输入的基因编码不符合要求时返回false,否则返回true
    */
    static bool gene_decode(const unsigned int gene_code, double *gene1, double *gene2)
    {
        /* 判断基因编码是否合法 */
        if (!is_gene_code_legal(gene_code))
            return false;
    
        /* 若基因编码合法则对其进行解码 */
        unsigned int gene1_code = GET_GENE1_CODE(gene_code);
        unsigned int gene2_code = GET_GENE2_CODE(gene_code);
    
        *gene1 = (double)gene1_code * (GENE1_RANGE_RIGHT - GENE1_RANGE_LEFT) / (GENE1_CODE_MAX - 1) + GENE1_RANGE_LEFT;
        *gene2 = (double)gene2_code * (GENE2_RANGE_RIGHT - GENE2_RANGE_LEFT) / (GENE2_CODE_MAX - 1) + GENE2_RANGE_LEFT;
    
        return true;
    }
    

    2.3.3、计算适应度

    适应度函数也称评价函数,通常用于区分群体中个体好坏的标准。适应度高的,也就是优秀的个体有更大的几率参与繁衍,遗传自己的基因。一般的,适应度函数根据目标函数来确定,有时候直接将目标函数值作为适应度。这里,考虑到待求解的多峰函数,尖峰分布密集而且峰的直径很窄,这不利于遗传算法的收敛,因此本文不直接将多峰函数值作为适应度,而是利用对数函数将多峰函数进行平缓,并将平缓后的函数值作为目标函数。具体做法是,将多峰函数进行两次求对数,因此,多峰函数与适应度的关系可如下表示:
    在这里插入图片描述
    用MATLAB做出适应度函数图像如下:
    在这里插入图片描述
    对比前文中的图不难看出,图像得到了有效的平缓,同时不同峰之间也保持着一定的高低之别。值得一提的是,这里更主要的是给出优化遗传算法的一个思路,即可以在适应度函数上做文章。本题的适应度函数只是对多峰函数本身做了一个简单的变换,读者不妨思考一下,就本题而言有没有什么非常好的适应度函数。

    据上文所述,适应度求值函数如下:

    /*
        多峰函数:z = 21.5 + x *sin(4 * 3.1415926 * x) + y * sin(20 * 3.1415926 * y)
        适 应 度:log(log(z))
        约    束:-3.0 <= x <= 12.1; 4.1 <= y <= 5.8
        精    度:精确到千分位
    */
    double get_fitness(const double x, const double y)
    {
        return log(log(21.5 + x * sin(4 * PI * x) + y * sin(20 * PI * y)));
    }
    

    2.3.4、选择算子

    本文的选择算法采用了非常常用的“轮盘赌算法”,赌盘算法的原理非常简单明了。创建赌盘时,我们将种群中所有个体的适应度求和,不妨将得到的结果称为总和适应度。然后,将每个个体的适应度除以总和适应度,然后将得到的商逐个累加,每加一次就得到赌盘的一个边界,累加完成后总和为1。如下的饼状图可以更形象的表明赌盘的原理:
    在这里插入图片描述
    由上文所述,赌盘创建函数可如下编写:

    /*
        创建赌盘
        ga      遗传算法器指针
    */
    static void create_roulette(GA *ga)
    {
        /* 计算赌盘中的概率 */
        ga->roulette[0] = ga->fitness[0] / ga->sum_fitness;
    
        for (int num = 1; num < ga->population_num - 1; num++)
        {
            ga->roulette[num] = ga->roulette[num - 1] + ga->fitness[num] / ga->sum_fitness;
        }
    
        ga->roulette[ga->population_num - 1] = 1.0;
    }
    

    再回到选择算子,选择算子需要赌盘作为基础,其运行时,会产生一个0到1的随机数,然后在赌盘中找到该数所在的区间,这个区间对应的个体即为被选中的个体。因此,适应度越高的个体被选中的几率越大,这是合理的。当然,也存在较小的概率选出适应度较低的个体,为了避免这种情况,本文引入了竞争机制,即一次选择的过程选出2个个体,再取其中适应度较高的那个个体,具体的程序如下:

    /*
        基因选择函数
        ga      遗传算法器指针
        返回值:返回使用轮盘赌的方式选出的个体(编号)
        说  明:选择策略为轮盘赌+随机竞争
    */
    static unsigned int select(GA *ga)
    {
        unsigned int index1 = 0, index2 = 0;
    
        /* 产生一个[0.0, 1.0]之间的浮点数 */
        double selector1 = rand() * 1.0 / RAND_MAX;
        double selector2 = rand() * 1.0 / RAND_MAX;
    
        /* 找出被选中的个体的索引 */
        for (; selector1 > ga->roulette[index1]; index1++);
        for (; selector2 > ga->roulette[index2]; index2++);
    
        return (ga->fitness[index1] > ga->fitness[index2] ? index1 : index2);
    }
    

    2.3.5、交叉算子

    遗传算法的交叉操作实质上是按某种方式交换父体和母体的部分基因,常见的交叉算子有单点交叉、两点交叉、多点交叉、均匀交叉及算术交叉等。本文选用两点交叉法,实现过程既不复杂,也有较好的随机性,该方法可由下图示意:
    在这里插入图片描述
    图中虚线指出的两个交叉点是随机产生的。具体程序如下:

    /*
        交叉函数
        ga          遗传算法器指针
        one         输出型参数,待交叉基因
        another     输出型参数,待交叉基因
        说明:
        1.对传入的基因编码执行两点交叉操作
    */
    static void cross(GA *ga, unsigned int *one, unsigned int *another)
    {
        /* 1.随机产生两个交叉点的位置 */
        unsigned char pos1 = rand() % GENE_CODE_LENGTH + 1;
        unsigned char pos2 = rand() % GENE_CODE_LENGTH + 1;
        unsigned char min_pos = min(pos1, pos2);
        unsigned char max_pos = max(pos1, pos2);
    
        /* 2.截出需要交换的基因段 */
        unsigned int one_gene_seg = get_bits(*one, min_pos, max_pos) << (min_pos - 1);
        unsigned int another_gene_seg = get_bits(*another, min_pos, max_pos) << (min_pos - 1);
        unsigned int mask = ~(get_bits(~(0U), min_pos, max_pos) << (min_pos - 1));
    
        /* 3.执行交叉操作 */
        *one = (*one & mask) | another_gene_seg;
        *another = (*another & mask) | one_gene_seg;
    }
    

    2.3.6、变异算子

    在自然界中,基因变异可以增加个体的多样性,这对于遗传算法来说是增加了个体的随机性,可以增加找到最优解的概率。本文采用的变异算子所做的操作是随机选择基因的某一位进行反转,程序如下:

    /*
        变异函数
        gene_code       输入型参数
        说明:
        1.对传入的基因编码执行变异操作
        2.随机选择基因编码中的一位做反转操作
    */
    static void mutate(unsigned int *gene_code)
    {
        unsigned int mutate_bit = 1 << (rand() % GENE_CODE_LENGTH);
        *gene_code ^= mutate_bit;
    }
    

    2.3.7、繁殖函数及进化函数

    遗传算法的主要算子都在上文中分析过了,下面要做的就是根据遗传算法的流程将这些算子整合起来以实现算法功能。在本文中,这其中涉及到两个关键的函数,即繁殖函数和进化函数。繁殖函数包括基因的复制、交叉及变异,同时本文还采用了子代竞争策略,即父代产生的两个子代个体仅保留适应度最高的,程序如下:

    /*
        繁殖函数
        ga       遗传算法器指针
        father   从种群中选出的父体
        mother   从种群中选出的母体
        返回值:  适应度最高的子代的基因编码
        说明: 
        1.一对父体与母体将繁殖出一对子代
        2.选择出适应性更好的子代返回
    */
    static unsigned int inherit(GA *ga, unsigned int father, unsigned int mother)
    {
        unsigned int son1 = ga->gene_code[father];
        unsigned int son2 = ga->gene_code[mother];
    
        /* 1.交叉 */
        cross(ga, &son1, &son2);
    
        /* 2.变异 */
        mutate(&son1);
        mutate(&son2);
    
        /* 3.子代竞争 */
        double son1_gene1, son1_gene2, son2_gene1, son2_gene2;
        gene_decode(son1, &son1_gene1, &son1_gene2);
        gene_decode(son2, &son2_gene1, &son2_gene2);
    
        return (ga->get_fitness(son1_gene1, son1_gene2) > ga->get_fitness(son2_gene1, son2_gene2)) ? son1 : son2;
    }
    

    进化函数则实现了遗传算法的一次完整的迭代过程,根据上文给出的遗传算法流程图,不难进行如下编码:

    /*
        进化函数
        ga      遗传算法器指针
    */
    static void evolve(GA *ga)
    {
        /* 1.申请暂存子代基因编码的内存 */
        unsigned int *descendants = (unsigned int *)calloc(ga->population_num, sizeof(unsigned int));
        
        /* 2.精英保留(将上一代中适应度最高的个体的基因编码保留) */
        descendants[0] = ga->gene_code[ga->best_individual];
        
        /* 3.选择合适的父体与母体 */
        unsigned int father = select(ga);
        unsigned int mother = select(ga);
    
        /* 4.繁殖(包含交叉与变异) */
        for (int num = 1; num < ga->population_num; num++)
            descendants[num] = inherit(ga, father, mother);
    
        /* 5.将子代记录到ga中并进行基因解码(使新一代的基因编码与基因对应) */
        for (int num = 0; num < ga->population_num; num++)
        {
            ga->gene_code[num] = descendants[num];
            gene_decode(ga->gene_code[num], &ga->gene[num].gene1, &ga->gene[num].gene2);
        }
        
        /* 5.更新种群适应度 */
        fit(ga);
        
        /* 6.更新赌盘 */
        create_roulette(ga);
    
        /* 7.释放之前申请的空间 */
        free(descendants);
    }
    

    3、运行结果及分析

    至此,本文已经给出了一个遗传算法的C语言实现的所有关键程序。下面就调用编写的遗传算法进行测试。本文将创建含有100个个体的种群,并进行100代迭代以求解多峰函数的最大值,一次完整的调用本文实现的遗传算法的程序如下所示:

    /* 创建遗传算法器 */
    GA *ga = create_ga(get_fitness, 100);
    
    /* 初始化遗传算法器 */
    ga->init(ga);
    
    /*迭代100代*/
    for (int i = 0; i < 100; i++)
    ga->evolve(ga);
    
    /*销毁遗传算法器*/
    delete_ga(ga);
    

    经多次调用测试,算法执行的结果较为稳定,所得的多峰函数最大值大多在38以上,多次运行结果中最好的解为38.849744,对应的坐标为(11.625331, 5.725256)。将迭代求得的最大值用MATLAB作图如下:
    在这里插入图片描述
    为验证是否找到了最优解,用MATLAB遍历求出该多峰函数在给定定义域内的最大值为38.8501,与本文求出的结果相差0.000356,可见本文实现的遗传算法表现还不算太差。

    文中给出的程序比较散,这里给出完整程序的下载链接

    展开全文
  • 算法设计与分析 (知识点总结)

    千次阅读 多人点赞 2021-03-03 23:08:42
        通过学习掌握算法设计的主要方法,算法的时、空复杂性有正确分析的能力,能够针对具体的应用问题选择合适的数据结构并设计结构清晰、正确有效的算法,为独立设计算法和算法进行复杂性分析奠定坚实的理论...

    算法设计与分析

    前言

        通过学习掌握算法设计的主要方法,对算法的时、空复杂性有正确分析的能力,能够针对具体的应用问题选择合适的数据结构并设计结构清晰、正确有效的算法,为独立设计算法和对算法进行复杂性分析奠定坚实的理论基础。

    第一章 算法基础

    1.1 算法概述

    1.什么是算法?
        算法(algorithm):算法是对特定问题求解步骤的描述,是指令的有限序列。就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为输出。简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果。
    2.算法的五个特征

    • 输入:算法有零个或多个输入量;
    • 输出:算法至少产生一个输出量;
    • 确定性:算法的每一条指令都有确切的定义,没有二义性;
    • 可行性:算法的每一条指令必须足够基本,它们可以通过已经实现的基本运算执行有限次来实现;
    • 有穷性:算法必须执行有限步之后终止。

    3.问题和问题求解
        问题求解:寻找一种方法来实现目标。
        问题求解过程:人们通过使用问题领域知识来理解和定义问题,并凭借自身的经验和知识求选择和使用适当的问题求解策略、技术和工具,将一个问题描述转换成问题解的过程。
        计算机求解问题的关键之一是寻找一种问题求解策略得到求解问题的算法,从而得到问题的解。
    4.问题求解过程

    • 理解问题
    • 设计方案
    • 实现方案
    • 回顾复查

    1.2 算法分析

    1.算法问题求解过程
    在这里插入图片描述
    2.算法分类
        精确算法总能保证求得问题的解。
        启发式算法通过使用某种规则、简化或智能猜测来减少问题求解时间。
        对于最优化问题,一个算法如果致力于寻找近似解而不是最优解,被称为近似算法
        如果在算法中需要做出某些随机选择,则称为随机算法
    3.算法设计

    • 计算机的问题求解策略主要指算法设计策略。
    • 如果所求问题符合某种算法设计策略处理问题的特性,就可使用该算法设计策略设计算法、求解问题。

    4.算法表示

    算法描述方法

    • 自然语言
    • 流程图
    • 伪代码
    • 程序设计语言
    • 使用c/c++语言描述

    5.算法确认

    • 算法确认:确认一个算法是否正确的活动。
    • 算法证明:使用数学方法证明算法的正确性。
    • 程序测试:是指对程序模块或程序总体,输入事先准备好的样本数据(称为测试用例),检查该程序的输出,来发现程序存在的错误及判定程序是否满足其设计要求和活动。

    6.算法分析

    • 算法分析:对算法的执行时间和所需空间的估量。(时间复杂度和空间复杂度)
    • 程序的性能测量:使用样本数据,实际测量一个程序所消耗的时间和空间。

    1.3 算法复杂度

    1.什么是好的算法

        一个好的算法应具备以下4个重要特性:

    • 正确性:算法的执行结果应当满足预先规定的功能和性能要求。
    • 简明性:算法要思路清晰、层次分明、容易理解、利于编码和调试。
    • 高效性:算法要有效使用存储空间,并具有高的时间效率。
    • 最优性:算法的执行时间已达到求解该类问题所需时间的下界。

        程序健壮性:是指当输入不合法数据时,程序可以做适当处理而不至于引起严重后果。 其含义是:当程序万一遇到意外时,能按某种预定方式作出适当处理。

        正确性和健壮性是相互补充的。

    2.影响程序时间的因素

        影响程序运行时间的因素主要有:

    • 程序所依赖的算法;

    • 问题规模和输入数据;

    • 计算机系统性能
      3.算法的时间复杂度

    • 抽象机模型
          设抽象机提供由m个基本运算组成的运算集O={O1,O2,…,Om},每个运算都是元运算(运算亦称演算,数学的基本概念之一,指使的一些计算规则,算术中有加、减、乘、除、乘方、开方六种运算,其中加、减、乘、除是从两个已知数得出第三个数的运算,称为二元运算;乘方、开方是从一个已知数得出另一个数的运算,称为一元运算)。 它们的执行时间是有限常量。设执行第i个运算Oi所需的时间是αi,1≤i≤m。
      一个算法给定一个输入并在抽象机上执行一次,该执行过程表现为执行一个基本运算序列

    • 时间复杂度
          算法的时间复杂度是指算法运行所需的时间。
          设有一个在抽象机上运行的算法A,I是某次运行时的输入数据,其规模为n,则算法A的运行时间T是n和I的函数,记做T(n,I)。又设在该次运算中抽象机的第i个基本运算Oi的执行次数为βi,1≤i≤m。βi也是n和I的函数,记做βi(n,I)。那么算法A在输入为I时的运行时间是:
      在这里插入图片描述

    • 最好、最坏和平均时间复杂度:

    • 最好时间复杂度
      在这里插入图片描述

    • 最坏时间复杂度
      在这里插入图片描述

    • 平均时间复杂度(与概率论中的数学期望概念类似,在概率论和统计学中,期望值(或数学期望、或均值,亦简称期望,物理学中称为期待值)是指在一个离散性随机变量试验中每次可能结果的概率乘以其结果的总和。换句话说,期望值是随机试验在同样的机会下重复多次的结果计算出的等同“期望”的平均值。
      在这里插入图片描述
      4.算法分析

    • 事前分析:在算法实际运行前分析算法的效率。

    • 事后测试:运行程序来测试一个程序在所输入数据下实际运行的时间。

    • 程序步:在语法或语义上有意义的程序段,该程序段的执行时间必须与问题实例的规模无关。

    • 实例
      在这里插入图片描述
      5.算法的空间复杂度

    • 算法的空间复杂度:算法运行所需的存储空间

    • 程序运行所需的存储空间包括以下两个部分:

    • 固定空间需求:这部分空间与所处理数据的大小和个数无关,即与问题实例的特征无关。

    • 可变空间需求:这部分空间大小与算法在某次执行中处理的特定数据的规模有关。

    1.4 渐近表示法

    1.大O记号
        定义:设函数f(n)和g(n)是定义在非负整数集合上的正函数,如果存在两个正常数c和n0,使得当n≥n0时,有f(n)≤cg(n),则记做f(n)=O(g(n)),称为大O记号。
        意义:该算法的运行时间 不会超过 g(n)的某个常数倍。 g(n)是该算法运行时间的上界。
    在这里插入图片描述

    • 渐进时间复杂度
          使用大O记号及下面定义的几种渐近表示法表示的算法时间复杂度,称为算法的渐近时间复杂度。
          只要适当选择关键操作,算法的渐近时间复杂度可以由关键操作的执行次数之和来计算。一般地,关键操作的执行次数与问题的规模有关,是n的函数。
      在这里插入图片描述

    2.Ω 记号
        定义:设有函数f(n)和g(n)是定义在非负整数集合上的正函数,如果存在两个正常数c和n0,使得当n≥n0时,有f(n)≥cg(n),则记做f(n)=Ω (g(n)),称为Ω记号。
        意义:该算法至少需要g(n)的某个常数倍大小的时间量。g(n)是该算法运行时间的下界
    例题1:
    在这里插入图片描述
    例题2:
    在这里插入图片描述
    3.Θ记号
        定义:设有函数f(n)和g(n)是定义在非负整数集合上的正函数,如果存在正常数c1,c2和n0,使得当n≥n0时,有c1g(n)≤f(n)≤c2g(n),则记做f(n)=Θ(g(n)),称为Θ记号。
        意义:该算法实际运行时间大约为g(n)的某个常数倍大小的时间量。(有上界也有下界)
    例题1:
    在这里插入图片描述
    4.小o记号
        定义:f(n)=o(g(n))当且仅当f(n)=O(g(n))且f(n)≠ Ω(g(n))
        意义:该算法的运行时间f(n)的阶比g(n)低。

    5.算法按时间复杂度分类

    • 算法按计算时间分类
          渐近时间复杂度有多项式时间限界的算法称做多项式时间算法。
          渐近时间复杂度为指数函数限界的算法称做指数时间算法。

    • 最常见的多项式时间算法的渐近时间复杂度
          O(1)<O(log n)<O(n)<O(nlog n)<O(n2)<O(n3)

    • 最常见的指数时间算法的渐近时间复杂度
           O(2n)<O(n!)<O(nn)
      在这里插入图片描述

    第二章 分治法

    展开全文
  • 算法设计与分析学习心得

    千次阅读 2020-06-23 22:13:14
    计算机算法设计与分析主要通过介绍常见的算法设计策略及复杂性分析方法,培养学生分析问题和解决问题的能力,为开发高效的软件系统及参加相关领域的研究工作奠定坚实的基础。该课程理论与实践并重,内容具有综合性、...
  • 遗传算法python实现(适合初学者)

    万次阅读 多人点赞 2019-05-31 14:10:19
    在某网页上碰巧看到了 遗传算法 ,就决定学习整理一下这个熟悉又陌生的经典算法。 遗传算法有趣的应用有:寻路问题,8数码问题,囚犯困境,动作控制,找圆心问题,TSP问题,生产调度问题,人工生命模拟等。 遗传...
  • 算法设计与分析

    2021-05-18 22:55:07
    进一步用C++语言实现算法,通过代码实现进一步加深算法原理的理解,提高学员的编程能力,同时还会加强作业辅导并通过CDSN的列表进行讨论,希望通过课后作业、习题进一步训练学习的算法设计、分析和编程能力。...
  • 算法算法实现

    千次阅读 多人点赞 2017-02-14 17:59:55
    算法是一切程序设计的灵魂和基础。 很多开发者都知道“程序=数据结构+算法”这个著名的公式,简单点来说,我更喜欢表单成为“后台程序=数据+逻辑”。1 什么是算法? 字面含义: 算法是用于计算的方法,通过这种...
  • 如何理解算法

    千次阅读 2018-12-07 21:42:26
    算法执行的计算过程的具体描述。简单点说,算法就是解决问题的方法步骤。 在编程中,算法通常是由类方法或者函数来实现的。比如之前提到的数据结构异同图,上面有些数据结构搜索快,插入和删除慢,有些数据结构...
  • 算法设计与分析】分治法与最近点问题

    千次阅读 多人点赞 2018-10-15 17:26:32
    说明:这是武汉理工大学计算机学院【算法设计与分析】课程的第一次实验第三题:分治法与最近点问题 >>点击查看WUTer计算机专业实验汇总 谨记:纸上得来终觉浅,绝知此事要躬行。 一、问题描述 设,,…...
  • 其中,逻辑结构是数据组织形式在逻辑上的抽象,物理结构则是基于存储设备数据真实的组织管理,因此又被成为存储结构。学习和研究数据结构,就是研究数据的逻辑结构、物理结构及二者之间的关系。因此,数据结构中...
  • 算法设计与分析之递归算法

    千次阅读 2020-11-26 22:40:27
    简单易懂的介绍了递归算法的原理和设计方法
  • 算法设计 - 概述

    千次阅读 2018-10-21 21:01:15
    本文作为一个对算法设计的概述,着眼于讨论如下几点:其一,算法界的知识图谱;其二,五类算法设计思路的引入;其三,枚举决策类算法的解空间树;其四,枚举决策类算法的分类;其五,算法的递归实现和递推实现;其六...
  • 时间复杂度和空间复杂度的概念及各种算法的时间复杂度 及举例 算法的复杂度可分为俩种 一种时间复杂度 另一种是空间复杂度。 俩者的概念:时间复杂度是指执行这个算法所需要的计算工作量;而空间复杂度是指执行这个...
  • 粒子群优化算法(PSO)python 3实现

    万次阅读 多人点赞 2019-06-10 20:31:20
    粒子群优化算法(Particle Swarm Optimization,PSO)属于进化算法的一种,是通过模拟鸟群捕食行为设计的。从随机解出发,通过迭代寻找最优解,通过适应度来评价解的品质。在这里,我们举一个例子来深入理解一下该...
  • 模糊PID算法及其MATLAB仿真(1)

    万次阅读 多人点赞 2019-04-15 20:34:35
    (2)模糊自整定PID算法(名字多:模糊自组织PID,模糊自调节PID、模糊自适应PID...) 这种方法比较常用,也就是使用模糊规则的方式进行PID三个参数的整定,至于它是否优于PID算法,个人持怀疑态度,后湖会提到。...
  • 算法设计与分析期末复习题(史上最详细)

    千次阅读 多人点赞 2021-06-07 13:25:06
    算法设计与分析期末复习题(一) 1、二分搜索算法是利用( A )实现的算法。 A、分治策略 B、动态规划法 C、贪心法 D、回溯法 2、下列不是动态规划算法基本步骤的是( A )。 A、找出最优解的性质 B、构造最优解 C、...
  • 算法设计与分析课程总结

    万次阅读 2017-11-04 18:04:11
     算法设计与分析是面向设计的核心课程,主要通过介绍常见的算法设计策略及复杂性分析方法,培养学生分析问题和解决问题的能力,为开发高效的软件系统及参加相关领域的研究工作奠定坚实的基础。该课程理论与实践并重...
  • 优化算法——遗传算法

    万次阅读 多人点赞 2015-05-10 17:09:28
    与遗传算法的第一次接触 遗传算法的基本概念 基本定义 遗传算法的基本流程 遗传算法过程中的具体操作 参数的编码 二进制编码 ...算法设计 个体编码 适应值函数 选择策略 杂交算子 变异算子 参数设置
  • 算法与程序设计

    千次阅读 2018-07-21 18:35:40
    内容:1、简要介绍程序,算法与程序的关系;  2、了解结构化程序设计;  3、构建对称方阵。 1、什么是程序?  所谓程序,就是一组计算机能识别与执行的指令。每一条指令使计算机执行特定的操作,用来完成一定...
  • 图像识别算法

    万次阅读 多人点赞 2019-08-15 17:36:40
    这里局部特征点的应用主要表现在第一种相似性上,也就是说我们需要设计某种图像匹配算法来判断两幅图像是否是同一物体或场景所成的图像。理想的图像匹配算法应该认为两幅同一物体的图像之间相似度很高,而两幅不同...
  • Bresenham算法理解

    万次阅读 多人点赞 2017-12-24 17:48:49
    bresenham算法是计算机图形学中为了“显示器(屏幕或打印机)系由像素构成”的这个特性而设计出来的算法,使得在求直线各点的过程中全部以整数来运算,因而大幅度提升计算速度。 实现代码这篇文章主要下面的代码...
  • 从零开始学习机器学习视频教程

    万人学习 2017-12-04 22:38:30
    机器学习难学,主要的难度在于算法模型多不好理解,各种各样的工具不知道如何使用,实际项目不知道如何开发。本门课程将系统入门机器学习,课程内容不光是对算法的学习,还包括诸如算法的评价,方法的选择,模型的...
  • 我在留学期间设计的一个电影推荐系统的设计思路,因为我觉得比较有趣,所以放出来也算是一个怀念 Method of measuring the quality recommendation system
  • 从斐波那契数列讲解算法的设计思路从斐波那契到递归带备忘录的递归从递归到动态规划动态规划算法设计思路分治算法设计贪心算法设计斐波那契数列的用途与特性斐波那契数列的用途斐波那契数列的特性算法设计总结 ...
  • 最直接,最易于理解设计方法,发掘和利用现有串行算法中的并行性,直接将串行算法改造为并行算法。 Case 1:快速排序 ​ 快速排序的串行算法思想为随机选取主元进行划分,之后递归排序。直接并行化思路即每次划分...
  • 基于物品的协同过滤算法实现图书推荐系统

    万次阅读 多人点赞 2019-09-14 21:20:24
    本文首先介绍了推荐系统的发展历史,及目前常用的几种推荐算法的介绍与比较,然后以基于物品的协同过滤算法为基础,详细介绍图书推荐系统的构建。在该系统中,主要功能分为用户功能和图书推荐功能...
  • PSO-粒子群优化算法

    千次阅读 2017-03-31 10:43:22
    PSO算法 粒子集群算法1. 常见的群体智能优化算法分类常见的群体智能优化算法主要有如下几类: (1)蚁群算法(Ant Colony Optimization,简称ACO)[1992年提出]; (2)粒子群优化算法(Particle Swarm ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 490,225
精华内容 196,090
关键字:

对算法设计的理解