Ericson's blog Time is limited, To be a better better man

基于多背包问题的人工鱼群算法

人工鱼群算法主要应用于组合优化问题,该算法根据水域中鱼生存数目最多的地方就是本水域中富含营养物质最多的地方这一特性来模拟鱼群的觅食行为而实现寻优。 人工鱼群算法主要利用鱼的三大基本行为:觅食、聚群和追尾行为,采用自上而下的寻优模式从构造个体的底层行为开始,通过鱼群中各个体的底层行为开始,通过鱼群中各个体的局部寻优,达到全局最优值在群体中凸显出来的目的。 人工鱼群算法的行为描述:

  • 觅食行为:设置人工鱼当前状态,并在其感知范围内随机选择另一个状态,如果得到的状态的目标函数大于当前状态,则向新选择得到的状态靠近一步,反之,重新选取新状态,判断是否满足条件,选择次数达到一定数量后,如果仍然不满足条件,则随机移动一步
  • 聚群行为:人工鱼探索当前邻居内的伙伴数量,并计算伙伴的中心位置,然后把新得到的中心位置的目标函数与当前位置的目标函数相比较,如果中心位置的目标函数优于当前位置的目标函数并且不是很拥挤,则当前位置向中心位置移动一步,否则执行觅食行为
  • 追尾行为:人工鱼探索周围邻居鱼的最优位置,当最优位置的目标函数值大于当前位置的目标函数值并且不是很拥挤,则当前位置向最优邻居鱼移动一步,否则执行觅食

人工鱼群算法步骤:

  • Step1:设定鱼群的参数,包括鱼群的规模m,最大迭代次数gen,人工鱼的感知范围Visual,最大移动步长step,拥挤度因子d等
  • Step2:在参数区间内随机生成m条人工鱼个体作为初始鱼群
  • Step3:计算每条鱼的食物浓度函数(目标函数),把最优的值放入公告板中
  • Step4:对于每条人工鱼执行以下操作:1.计算出追尾行为、聚群行为的值,采用行为选择策略,选择最优的行为作为鱼的移动方向,缺省行为是觅食行为;2.计算出每条鱼的食物浓度函数(目标函数),其最优值与公告板中的值进行比较,最终公告板中始终保持最优的值
  • Step5:判断是否满足结束条件,如果满足就结束,否则转Step4

最终的公告板值就是最优值。

多背包问题有很多算法解决,有确定算法和启发式算法,人工鱼群算法是启发式算法,把每个物品当成是人工鱼,m个背包当成是m个鱼群,目标函数就是物品的总价值,由此求得最优值。

Python Matplot画图

最近在写论文,本来用visio画图,然后发现用latex显示的visio图不够精美,查了资料,发现有很多精美的制作图片的软件,所以选用python来画图,正好可以学习下python语法。

python主要是用matplot和numpy这两个库来画图,后一个库主要是用作处理数字。不废话,上代码:

import matplotlib.pyplot as plt
plt.rcParams['text.usetex'] = True
plt.rcParams['mathtext.default'] = 'bf'
# plt.rcParams['lines.linewidth'] = 2
plt.rcParams['font.size'] = 12
time = []  # time
x = []  # x axis
job1 = []  # job1
job2 = []  # job2
job3 = []  # job3
job4 = []  # job4
job5 = []  # job5
job6 = []  # job6
file = open('F:\\test\\HYAS')
for line in file:
    line_split = line.strip('\n').split(',')
    time.append(long(line_split[0]))
    x.append(0)
    size = len(line_split) - 1
    for i in range(size):
        items = line_split[i + 1].split(':')
        if int(items[0]) == 1:
            job1.append(float(items[1]))
        if int(items[0]) == 2:
            job2.append(float(items[1]))
        if int(items[0]) == 3:
            job3.append(float(items[1]))
        if int(items[0]) == 4:
            job4.append(float(items[1]))
        if int(items[0]) == 5:
            job5.append(float(items[1]))
        if int(items[0]) == 6:
            job6.append(float(items[1]))
    while len(job1) < len(time):
        job1.append(0)
    while len(job2) < len(time):
        job2.append(0)
    while len(job3) < len(time):
        job3.append(0)
    while len(job4) < len(time):
        job4.append(0)
    while len(job5) < len(time):
        job5.append(0)
    while len(job6) < len(time):
        job6.append(0)
realtime = []
temp = time[0]
for item in time:
    realtime.append(float(item - temp) / 1000000000)
for i in range(len(job1)):
    job2[i] += job1[i]
for i in range(len(job2)):
    job3[i] += job2[i]
for i in range(len(job3)):
    job4[i] += job3[i]
for i in range(len(job4)):
    job5[i] += job4[i]
for i in range(len(job5)):
    job6[i] += job5[i]
print time
print realtime
print job1
print sum(job6)/len(job6)
plt1, = plt.plot(realtime, job6, color='magenta')
plt2, = plt.plot(realtime, job5, color='yellow')
plt3, = plt.plot(realtime, job4, color='black')
plt4, = plt.plot(realtime, job3, color='blue')
plt5, = plt.plot(realtime, job2, color='green')
plt6, = plt.plot(realtime, job1, color='red')
plt.fill_between(realtime, x, job1, facecolor='red')
plt.fill_between(realtime, job1, job2, facecolor='green')
plt.fill_between(realtime, job2, job3, facecolor='blue')
plt.fill_between(realtime, job3, job4, facecolor='black')
plt.fill_between(realtime, job4, job5, facecolor='yellow')
plt.fill_between(realtime, job5, job6, facecolor='magenta')
# # plt.fill_between(x,y,t,facecolor="b")
plt.xlabel('Time(s)',fontsize=16)
plt.ylabel('Memory(G)',fontsize=16)
plt.xlim(0, 600)
plt.ylim(0, 62)
# plt.legend([plt1, plt2, plt3, plt4, plt5, plt6], ['job6', 'job5', 'job4', 'job3', 'job2', 'job1'])  # make legend
leg=plt.legend([plt1,plt2,plt3,plt4,plt5,plt6],['job6','job5','job4','job3','job2','job1'])
leg.get_frame().set_alpha(0.2)
plt.show()

上述代码(该代码源于我实现的Hadoop调度器模块中统计内存利用)可以用来画带填充的折线图,只是初步的python画图,至于一些坐标轴建立,多个子图的画法还没完全懂,后面继续研究。 上面程序的示意图如下:

Linux软链接和硬链接

硬链接和软链接是Linux文件系统中的一个重要的概念,其涉及文件系统中的索引节点(index node又称为inode),而索引节点对象是Linux虚拟文件系统(VFS)的四个基本概念之一。在Linux中文件作为进程创建信息的逻辑单元可被多个进程并发使用。Linux系统顶层文件目录如下图所示: file class 文件分为文件名和数据两部分,这在linux上被分成两个部分:用户数据与元数据。用户数据,即文件数据块,数据块是记录文件真实内容的地方;而元数据则是文件的附加属性,比如文件大小、创建时间等信息。元数据中的inode索引节点号才是文件的唯一标识,而不是文件名。

为解决文件共享,Linux引入了两种链接:硬链接和软链接(又称符号链接)。若一个inode号对应多个文件名,则称这些文件为硬链接。换言之,硬链接就是同一个文件使用了多个别名。硬链接可以使用link或ln命令来创建。硬链接有以下几点特性:

  • 文件有相同的inode及data block
  • 只能对已存在的文件创建硬链接
  • 不能交叉文件系统进行硬链接的创建(因为不同的文件系统可能存在相同的inode号)
  • 不能对目录进行创建,只能对文件进行创建
  • 删除一个硬链接文件不会影响其他相同inode号的文件

软链接与硬链接不同,若文件用户数据块中保存的内容为另一个文件的指向,则该文件就是软链接。换言之,软链接就是一个普通的文件,只是数据块内容有点特殊。软链接有着自己的inode号以及用户数据块。软链接可以用ln -s来创建。软链接的特点如下所示:

  • 软链接有着自己的文件属性及权限
  • 可以对不存在的文件和目录创建软链接
  • 软链接可交叉文件系统
  • 软链接可以对文件和目录创建
  • 创建软链接时,链接计数i_nlink不会增加,而硬链接计数器会增加
  • 删除软链接并不影响被指向文件,但若是原文件被删除,则相关的软链接被称为死链接

硬链接和软链接区别示意图如下:

Java类加载机制

Java类加载器除了根类加载器之外,其他的都是用java语言编写的。当运行某个java程序时,将会启动一个java虚拟机进程,该程序的所有线程都位于该java虚拟机中。两个JVM之间不能共享数据。

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通知加载、连接、初始化3个步骤来对该类进行初始化。类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象。当类被加载之后,系统会为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JAR中。分为以下三个阶段:

  • 验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
  • 准备:类准备阶段则负责为类的静态Field分配内存,并设置默认初始值
  • 解析:将类的二进制数据中的符号引用替换成直接引用

在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对静态Field进行初始化。类加载器负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象。当JVM启动时,会形成3个类加载器组成的初始类加载器层次结构:

  • Bootstrap ClassLoader:根类加载器
  • Extension ClassLoader:扩展类加载器
  • System ClassLoader:系统类加载器

Bootstrap ClassLoader被称为引导类加载器,它负责加载Java核心类。

URL[] urls=sum.misc.Launcher.getBootstrapClassPath().getURLs();
for(int i=0;i<urls.length;i++){
    System.out.println(urls[i].toExternalForm());
}

Externsion ClassLoader被称为扩展类加载器,它负责加载JRE的扩展目录中JAR包的类。System ClassLoader被称为系统类加载器,它负责在JVM启动时加载来自java命令的-classpath选项、java.class.path系统属性或CLASSPATH环境变量所指定的JAR包和类路径。

JVM类加载器机制主要有3种:

  • 全盘负责:当一个类加载器负责加载某个Class时,该Class所依赖和引用的其他Class也将由该类加载器负责载入,除非显式使用另外一个类加载器来载入
  • 父类委托:则是让parent类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
  • 缓存机制:保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象系统才会读取该类对应的二进制数据。

可以通过扩展ClassLoader类来自定义自己的类加载器。ClassLoader有两个关键方法:loadClass(String name,boolean resolve)和findClass(String name)。在ClassLoader里还有一个核心方法:defineClass(String name,byte[] b,int len),该方法负责将指定类的字节码文件读入字节数组b内,并把它转换为Class对象,该字节码文件可以源于文件、网络等。

Java程序中许多对象在运行时都会出现两种类型:编译时类型和运行时类型。例如Person p=new Student();编译时类型为Person,运行时类型为Student。当编译时和运行时完全知道类型的具体信息,在这种情况下,我们可以直接先使用instanceof运算符进行判断,再利用强制类型转换;党编译时根本无法预知该对象和类可能属于哪些类,程序只能依靠运行时的信息来发现该对象和类的真实信息,这就必须使用反射。

Java程序中获得Class对象通常有如下3钟方式:

  • 使用Class类的forName(String clazzName)静态方法,该字符串参数的值是某个类的全限定类名
  • 调用某个类的class属性来获取该类对应的Class对象,Person.class
  • 调用某个对象的getClass方法,该方法将会返回该对象所属类对应的Class对象

Class对象可以获得该类里的方法、构造器、Field,这3个类都位于java.lang.reflect包下,并实现了java.lang.reflect.Member接口。可以通过Class的newInstance方法或者先创建构造器然后通过构造器来实例化对象。Java类可以通过Class类来反射调用java类中的方法。

在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口可以生成JDK动态代理类或动态代理对象。Proxy提供了用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果在程序中为一个或多个接口动态的生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态的创建实例,也可以使用Proxy来创建动态代理实例。Proxy主要包含两个方法:getProxyClass和newProxyInstance方法。InvocationHandler中封装了需要调用的方法。代码示例如下:

InvocationHandler handler=new MyInvocationHandler();
Foo f=(Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[]{Foo.class},handler);

动态代理在代码复用中应用很广泛,在AOP(面向切面编程)中也有广泛的使用。

Java线程

所有运行中的任务通常对应一个进程,当一个程序进入内存运行时,变成了一个进程,进程是系统进行资源分配和调度的一个独立的单位。进程包含下面三个特征:

  • 独立性
  • 动态性
  • 并发性

多线程编程有如下优点:

  • 进程之间不能共享内存,但线程之间共享内存非常容易
  • 系统创建线程的代价较小,多任务并发效率高
  • java语言内置了多线程功能的支持

Java中用Tread类来表示线程,另外也可实现Runnable接口。在java中还可以通过Callable和Future接口创建线程,该两个接口会在后续博客中讨论。

在线程的生命周期中,它主要经过5个状态:新建、就绪、运行、阻塞和死亡状态。 thread state 线程控制,Thread提供了让一个线程等待另一个线程完成的方法——join方法。

有一种线程,它是在后台运行的,它的任务是为其他的线程提供服务,这种线程被称为“后台线程”,又称为“守护线程”。JVM垃圾回收机制是典型的后台守护线程。后台线程有个特征:如果前台线程都死亡了,那么后台线程会自动死亡。yield方法让该线程转为就绪态,让给其他线程执行。线程还可以通过方法更改它的优先级。

Java线程同步提供了synchronized关键字,可同步代码块,还提供了Lock对象进行同步。Lock提供了比synchronized方法和代码块更广泛的锁定操作,Lock是控制多个线程对共享资源进行访问的工具。线程锁以后介绍。