重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
其实不只是python,各种语言都是这样。唯一的办法就是多写,然后不停的回头去看自己写的代码,不停的去重构。同时也要多读,现在网上太多开源的代码,去观摩,一点一点的积累。
在曹县等地区,都构建了全面的区域性战略布局,加强发展的系统性、市场前瞻性、产品创新能力,以专注、极致的服务理念,为客户提供成都做网站、成都网站制作、成都外贸网站建设 网站设计制作定制设计,公司网站建设,企业网站建设,品牌网站建设,全网营销推广,外贸网站制作,曹县网站建设费用合理。
一般来说,Python程序员可能是这样写main()函数的:
"""Module docstring.
This serves as a long usage message.
"""import sysimport getoptdef main():
# parse command line options
try:
opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) except getopt.error, msg: print msg print "for help use --help"
sys.exit(2) # process options
for o, a in opts: if o in ("-h", "--help"): print __doc__
sys.exit(0) # process arguments
for arg in args:
process(arg) # process() is defined elsewhereif __name__ == "__main__":
main()1234567891011121314151617181920212223242526
Guido也承认之前自己写的main()函数也是类似的结构,但是这样写的灵活性还不够高,尤其是需要解析复杂的命令行选项时。为此,他向大家提出了几点建议。
添加可选的 argv 参数
首先,修改main()函数,使其接受一个可选参数 argv,支持在交互式shell中调用该函数:
def main(argv=None):
if argv is None:
argv = sys.argv # etc., replacing sys.argv with argv in the getopt() call.1234
这样做,我们就可以动态地提供 argv 的值,这比下面这样写更加的灵活:
def main(argv=sys.argv):
# etc.12
这是因为在调用函数时,sys.argv 的值可能会发生变化;可选参数的默认值都是在定义main()函数时,就已经计算好的。
但是现在sys.exit()函数调用会产生问题:当main()函数调用sys.exit()时,交互式解释器就会推出!解决办法是让main()函数的返回值指示退出状态(exit status)。因此,最后面的那行代码就变成了这样:
if __name__ == "__main__":
sys.exit(main())12
并且,main()函数中的sys.exit(n)调用全部变成return n。
定义一个Usage()异常
另一个改进之处,就是定义一个Usage()异常,可以在main()函数最后的except子句捕捉该异常:
import sysimport getoptclass Usage(Exception):
def __init__(self, msg):
self.msg = msgdef main(argv=None):
if argv is None:
argv = sys.argv try: try:
opts, args = getopt.getopt(argv[1:], "h", ["help"]) except getopt.error, msg: raise Usage(msg) # more code, unchanged
except Usage, err: print sys.stderr, err.msg print sys.stderr, "for help use --help"
return 2if __name__ == "__main__":
sys.exit(main())123456789101112131415161718192021222324
这样main()函数就只有一个退出点(exit)了,这比之前两个退出点的做法要好。而且,参数解析重构起来也更容易:在辅助函数中引发Usage的问题不大,但是使用return 2却要求仔细处理返回值传递的问题。
下图介绍了两种不同种类的Metaheuristics,我们主要用左边的,尤其是Iterated Greedy。
I 和D 的过程用图像表示如下:
我在这里只用Iterated Greedy算法。原因如下:
其他算法诸如蚁群算法等,可能能提供非常接近最优解的方案,有些算法的运行速度也很快,可以弥补python运行慢的缺陷。但是这些算法通常存在诸多不足,例如算法太复杂,适用面很窄,需要设置过多参数导致很难实现。
Iterated Greedy的优势在于,它由两个简单的阶段构成:
D和I
更好的解决方案总会被接受
更坏的方案以特定的可能性接受,接受的概率如下图
类似 模拟退火法 :
从当前解决方案中随机删除 numberJobsToRemove 个订单。这里 numberJobsToRemove 是Iterated Greedy的一个参数。
输出的结果是被移除的订单集合 removedJobs 和一个不完整的解决方案 partialPermutation 。
* 注意:solver.RNG.choice的结果每次都一样,是因为我们设置了随机数种子,目的就是只要是用同一个种子作为参数构造出的solver的属性RNG都是同一个。
将 removedJobs 重新加回 partialPermutation ,并插入到最佳位置(NEH)的顺序,并返回新的完整解决方案。这个插入过程是通过排列Permutation实现的,看一下Construction函数的参数表可知,需要两个列表。
* 注:前面讲算法的时候说过,重构函数执行之后会生成一个新的方案newSolution,新方案就是通过把removedJobs插入到最佳位置得到的。最优位置的选择需要在Construction函数中借助 solver.EvaluationLogic.DetermineBestInsertion(completeSolution, i)来实现
我们通过简单的解构和重构,必然会得出一个新方案newSolution,那么我们接受新方案newSolution为当前方案currentSolution的前提是,如果
- 新的解决方案( newSolution ),
- 当前解决方案( currentSolution )。
* 公式中,T叫做baseTemperature,是Iterated Greedy的第二个参数,T越大,则接受更坏方案的概率也越大。新的最优方案存储在SolutionPool中(numberJobsToRemove是第一个参数)
下面开始对newSolution进行评估和比较:
* 注意:判断一个新方案是否可接受取决于两方面:1. 如果新方案更好,那么无论如何都会接受新方案;2. 如果新方案并没有优化,则视作WorseSolution,即使是worseSolution也是要按照公式计算出的概率来衡量是否要接受这个不好的方案
为了看起来更加直观,我把接受差方案的过程写成了函数AcceptWorseSolution。注意看参数表,以便于确定何时调用这个函数。
局部搜索可以被用于优化currentSOlution,但不是必须的。通常会使用IterativeImprovement结合Insertion邻域一起使用。
Iterated Greedy是一个迭代的解构D和构建C组成的序列。
在一个循环中被反复执行,直到达到 停止标准 。
在这里的例子中, 停止标准 是迭代次数 maxIterations ,但时间限制或没有优化的迭代次数也是可以的。
后面可能还会讲到怎么设计一个没有优化的迭代次数,这里可以先思考一下。
现在尝试运行一下:
与 IterativeImprovement 算法类似,现在要为 IteratedGreedy 创建一个单独的类,以便该类的一个实例可以传递给求解器Solver。
像 IterativeImprovement 一样, Iterated Greedy 应该继承自 ImprovementAlgorithm 。
必要的 参数 是以下属性:
EvaluationLogic, SolutionPool以及随机数生成器RNG都由求解器solver传递给算法。
添加 IteratedGreedy 类,以便它可以作为一种算法传递给求解器。
成员函数:Konstruktor, Initialize, Destruction, Construction, AcceptWorseSolution, Run
语法错误。
这可能是由于Python脚本本身不在C:/test/temp中造成的。Python将在运行它的目录中查找文件名,这意味着它将尝试重命名不存在的文件。您必须在文件名中添加目标前缀。
重命名是指给文件或文件夹等重新起一个名称。