zoukankan      html  css  js  c++  java
  • 退而求其次(4)——椭圆中的最大矩形

      在椭圆x2+4y2=4 中有很多内接的矩形,这些矩形的边平行于x轴和y轴,找出它们中面积最大的一个。

      先作图,椭圆的中心在原点,其内接矩形的中心也在原点。设矩形的其中一点内接椭圆于P(x,y) , P在第一象限:

      矩形的两条边长分别是2x和2y,面积是A=4xy 。还知道xy都在椭圆上,因此问题可以转换为约束条件下的极值:

    数学方案

      最直接的方案是使用拉格朗日乘子法:

      由于拉格朗日乘子法无法确定最值的类型,所以还要计算函数边界值。当P在椭圆上移动时,如果正好落在x轴上,则长方形退化成直线,此时面积是0;另一个边界值是P落在y轴上,面积也是0。所以判定4是面积的最大值,P的坐标是(1.414, 0.707)

    抛开数学的遗传算法

      格朗日乘子法很简单,但前提是需要了解偏导、梯度、拉格朗日乘子等一系列前置知识,而遗传算法的优点是不需要复杂的数学知识,可以直接对问题编程求解,这对庞大的程序员群体来说无疑是个福音。

      可以通过椭圆得到xy的关系:

      矩形的面积可以写成:

      只需要对x进行基因编码就可以利用遗传算法求得最优解。在编写代码前,可以考虑是否能够去掉影响算法运行速度和计算精度根号。

      由于xy都大于等于0,所以当4xy达到最大值时,(4xy)2也将达到最大。在求最值问题时,常系数起不到任何作用,因此求(4xy)2的最大值相当于求x2y2的最大值。结合xy的关系,原问题转换为:

      现在看起来简单多了。

      我们把问题精确到小数点后3位,从0.000到1.000之间有999个数,由于999转换成二进制是1111100111,因此可以使用10位二进制基因编码表示y

      代码如下:

      1 import math
      2 import random
      3 import numpy as np
      4 import matplotlib.pyplot as plt
      5
      6 MAX, MIN = 0.999, 0 # y的最大值和最小值
      7 CODE_SIZE = 10 # 基因编码长度
      8 POPULATION_SIZE = 20 # 种群数量
      9
     10 def print_solution(code):
     11     '''
     12      打印解决方案
     13     :param code: 基因编码
     14     '''
     15     x, y, A = decode(code)
     16     print('x = {0}, y = {1}, A = {2}'.format(x, y, A))
     17
     18 def decode(code:list):
     19     '''
     20     解码
     21     :param code: 基因编码
     22     :return: x,y,4xy (x,y都放大了1000倍)
     23     '''
     24     y = int(''.join(code), 2) * 0.001  # 将code转换成十进制数
     25     if y > MAX:  # y超过了边界
     26         return -1, -1, -1
     27     x = math.sqrt(4 - 4 * (y ** 2)) # x = sqrt(4 -4(y^2))
     28     # 使用退一法保留小数点后三位有效数字
     29     x, y = float('%.3f' % x), float('%.3f' % y)
     30     return x, y, 4 * x * y
     31
     32 def fitness_fun(code):
     33     ''' 适应度函数 '''
     34     return decode(code)[2]
     35
     36 def max_fitness(population):
     37     ''' 种群中的最优适应个体 '''
     38     return max([fitness_fun(code) for code in population])
     39
     40 def init_population():
     41     ''' 构造初始种群 '''
     42     population = []
     43     for i in range(POPULATION_SIZE):
     44         population.append(random.choices(['0', '1'], k=CODE_SIZE))
     45     return population
     46
     47 def selection(population):
     48     ''' 精英选择策略'''
     49     # 按适应度从大到小排序
     50     pop_parents = sorted(population, key=lambda x: fitness_fun(x), reverse=True)
     51     # 选择种群中适应度最高的40%作为精英
     52     return pop_parents[0: int(POPULATION_SIZE * 0.4)]
     53
     54 def crossover(population):
     55     ''' 单点交叉 '''
     56     pop_new = []  # 新种群
     57     code_len = len(population[0])  # 基因编码的长度
     58     for i in range(POPULATION_SIZE):
     59         p = random.randint(1, code_len - 1)  # 随机选择一个交叉点
     60         r_list = random.choices(population, k=2)  # 选择两个随机的个体
     61         pop_new.append(r_list[0][0:p] + r_list[1][p:])
     62     return pop_new
     63
     64 def mutation(population):
     65     '''单点变异'''
     66     code_len = len(population[0])  # 基因编码的长度
     67     mp = 0.2 # 变异率
     68     for i, r in enumerate(population):
     69         if random.random() < mp:
     70             p = random.randint(0, code_len - 1)  # 随机变异点
     71             r[p] = '1' if r[p] == '0' else '1'
     72             population[i] = r
     73
     74 def ga():
     75     ''' 遗传算法 '''
     76     population = init_population() # 构建初始化种群
     77     max_fit = max_fitness(population) # 种群最优个体的适应度
     78     max_fit_list = [max_fit]  # 每一代种群的最优适应度
     79     i = 0
     80     while i < 5: # 如果连续5代没有改进,结束算法
     81         pop_next = selection(population) # 选择种群
     82         pop_new = crossover(pop_next) # 交叉
     83         mutation(pop_new) # 变异
     84         max_fit_new = max_fitness(pop_new) # 新种群中最优个体的适应度
     85         if max_fit < max_fit_new:
     86             max_fit = max_fit_new
     87             i = 0
     88         else:
     89             i += 1
     90         population = pop_new
     91         max_fit_list.append(max_fit_new)
     92     # 按适应度值从大到小排序
     93     population = sorted(population, key=lambda x: fitness_fun(x), reverse=True)
     94     # 返回最优的个体和每一代种群中最优个体的适应度
     95     return population[0], max_fit_list
     96
     97 def pop_curve(max_fit_list):
     98     ''' 显示种群进化曲线 '''
     99     x = np.arange(1, len(max_fit_list) + 1, 1)
    100     y = np.array(max_fit_list)
    101     plot = plt.plot(x, y, '-')
    102     plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
    103     plt.title('种群进化曲线')
    104     plt.xlabel('种群代数')
    105     plt.ylabel('种群总成本')
    106     plt.show()
    107
    108 if __name__ == '__main__':
    109     # code = ['1','0','1','1','0','0','0','0','1','1']  # 707的二进制基因编码
    110     # print_solution(code)
    111     best, max_fit_list = ga()
    112     print_solution(best)
    113     pop_curve(max_fit_list)

      decode方法先将y的基因编码转换成对应的十进制数,之后乘以0.001变成相应的小数。适应度函数的值是矩形的面积。遗传算法的主体方法ga()除了得到最优个体外,还额外返回了每一代种群中最优个体的适应度,以便展示种群进化曲线。在实际应用中,可以通过观察进化曲线来观察算法在哪里收敛,从而调整算法的终止条件。

      种群的进化曲线

      可以看到,算法收敛的相当快。一种可能的结果是:

      


       作者:我是8位的

      出处:http://www.cnblogs.com/bigmonkey

      本文以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,非商业用途! 

      扫描二维码关注公众号“我是8位的”

  • 相关阅读:
    Springboot使用slf4j记录日志和lombok(能用的1)-主要看这个!
    mysql-覆盖索引(转载)(收藏过)
    Github上开源仿京东商城项目启动配置详解(小白版)
    java8 stream常用用法(转载)
    JDK下载过慢的问题解决方案
    intellij idea 的全局搜索快捷键方法(转载)
    Linux终端复制粘贴快捷命令
    Kali入门配置
    Google搜索
    Dig
  • 原文地址:https://www.cnblogs.com/bigmonkey/p/10829980.html
Copyright © 2011-2022 走看看