﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-&lt;font size=5 color="99eedd"&gt;Coundy&lt;/font&gt;-文章分类-Algorithm</title><link>http://www.blogjava.net/coundy/category/21377.html</link><description>&lt;p&gt;
&lt;font size=2 color=#550edd&gt;
&amp;nbsp;&amp;nbsp;袈裟点点疑樱瓣，半是脂痕半泪痕；清风细雨红泥寺，不见僧归见燕归；逢君别有伤心在，且看寒梅未落花；我本将心向明月，谁知明月照沟渠&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;宠辱不惊，静观堂前花开花落；去留无意，漫看天边云卷云舒
&lt;/font&gt;</description><language>zh-cn</language><lastBuildDate>Fri, 24 Aug 2007 20:04:58 GMT</lastBuildDate><pubDate>Fri, 24 Aug 2007 20:04:58 GMT</pubDate><ttl>60</ttl><item><title>遗传算法介绍(转)</title><link>http://www.blogjava.net/coundy/articles/109616.html</link><dc:creator>Coundy</dc:creator><author>Coundy</author><pubDate>Tue, 10 Apr 2007 05:57:00 GMT</pubDate><guid>http://www.blogjava.net/coundy/articles/109616.html</guid><wfw:comment>http://www.blogjava.net/coundy/comments/109616.html</wfw:comment><comments>http://www.blogjava.net/coundy/articles/109616.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coundy/comments/commentRss/109616.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coundy/services/trackbacks/109616.html</trackback:ping><description><![CDATA[<p>(转自)<a href="http://www.cnblogs.com/waterflier/archive/2006/12/04/153545.html">http://www.cnblogs.com/waterflier/archive/2006/12/04/153545.html</a><strong><br><br>现代生物遗传学中描述的生物进化理论: <br></strong>遗传物质的主要载体是染色体(chromsome),染色体主要由DNA和蛋白质组成。其中DNA为最主要的遗传物质。 <br>基因(gene)是有遗传效应的片断,它存储着遗传信息,可以准确地复制,也能发生突变,并可通过控制蛋白质的合成而控制生物的状态.生物自身通过对基因的复制(reproduction)和交叉(crossover,即基因分离,基因组合和基因连锁互换)的操作时其性状的遗传得到选择和控制。生物的遗传特性,使生物界的物种能保持相对的稳定;生物的变异特性,使生物个体产生新的性状,以至于形成了新的物种(量变积累为质变),推动了生物的进化和发展。 </p>
<p><strong>遗传学算法和遗传学中的基础术语比较</strong> </p>
<p>
<table style="WIDTH: 635px; HEIGHT: 128px" height=128 cellSpacing=0 cellPadding=0 width=635 border=1>
    <tbody>
        <tr>
            <td>染色体(chromosome)&nbsp;&nbsp;&nbsp;</td>
            <td>数据,数组,序列</td>
        </tr>
        <tr>
            <td>基因(gene)</td>
            <td>单个元素,位</td>
        </tr>
        <tr>
            <td>等位基因(allele)</td>
            <td>数据值,属性,值</td>
        </tr>
        <tr>
            <td>基因座(locus)&nbsp;</td>
            <td>位置,iterator位置</td>
        </tr>
        <tr>
            <td>表现型(phenotype)&nbsp;</td>
            <td>参数集,解码结构,候选解</td>
        </tr>
        <tr>
            <td>遗传隐匿(epistasis)&nbsp;</td>
            <td>非线性</td>
        </tr>
    </tbody>
</table>
</p>
<p>染色体又可以叫做基因型个体(individuals),一定数量的个体组成了群体(population),群体中个体的数量叫做群体大小。各个个体对环境的适应程度叫做适应度(fitness) <br></p>
<p><strong>遗传算法的准备工作: <br></strong>1)数据转换操作,包括表现型到基因型的转换和基因型到表现型的转换。前者是把求解空间中的参数转化成遗传空间中的染色体或者个体(encoding),后者是它的逆操作(decoding) <br>2)确定适应度计算函数,可以将个体值经过该函数转换为该个体的适应度,该适应度的高低要能充分反映该个体对于解得优秀程度。非常重要的过程！ </p>
<p><strong>遗传算法的基本步骤</strong> <br>遗传算法是具有"生成+检测"(generate-and-test)的迭代过程的搜索算法。 <br>基本过程为: <br>1)编码,创建初始集团 <br>2)集团中个体适应度计算 <br>3)评估适应度 <br>4)根据适应度选择个体 <br>5)被选择个体进行交叉繁殖, <br>6)在繁殖的过程中引入变异机制 <br>7)繁殖出新的集团,回到第二步 </p>
<p><strong>一个简单的遗传算法的例子:求 [0,31]范围内的y=(x-10)^2的最小值</strong> <br>1)编码算法选择为"将x转化为2进制的串",串的长度为5位。(等位基因的值为0 or 1) <br>2)计算适应度的方法是:先将个体串进行解码,转化为int型的x值,然后使用y=(x-10)^2作为其适应度计算合适(由于是最小值,所以结果越小,适应度也越好) <br>3)正式开始,先设置群体大小为4,然后初始化群体 =&gt; (在[0,31]范围内随机选取4个整数就可以,编码) <br>4)计算适应度Fi(由于是最小值,可以选取一个大的基准线1000,Fi = 1000 - (x-10)^2) <br>5)计算每个个体的选择概率.选择概率要能够反映个体的优秀程度.这里用一个很简单的方法来确定选择概率 <br>P=Fi / TOTAL(Fi). <br>6)选择. <br>根据所有个体的选择概率进行淘汰选择.这里使用的是一个赌轮的方式进行淘汰选择.先按照每个个体的选择概率创建一个赌轮,然后选取4次,每次先产生一个0-1的随机小数,然后判断该随机数落在那个段内就选取相对应的个体.这个过程中,选取概率P高的个体将可能被多次选择,而概率低的就可能被淘汰. </p>
<p>下面是一个简单的赌轮的例子 <br><font face=宋体 size=2>&nbsp;&nbsp; 13%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 35%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 15%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 37%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>----------|----------------------------|------------|-*-------------------------| <br>&nbsp;&nbsp; 个体1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 个体2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 个体3&nbsp;&nbsp;&nbsp; ^0.67&nbsp;&nbsp;&nbsp; 个体4</font> <br>随机数为0.67落在了个体4的端内.本次选择了个体4.&nbsp; </p>
<p>被选中的个体将进入配对库(mating pool,配对集团)准备开始繁殖. <br>7)简单交叉 <br>先对配对库中的个体进行随机配对.然后在配对的2个个体中设置交叉点,交换2个个体的信息后产生下一代. <br>比如( | 代表简单串的交叉位置) <br>&nbsp;( 0110|1, 1100|0 ) --交叉--&gt; (01100,11001) <br>&nbsp;( 01|000, 11|011 ) --交叉--&gt; (01011,11000) <br>2个父代的个体在交叉后繁殖出了下一代的同样数量的个体. <br>复杂的交叉在交叉的位置,交叉的方法,双亲的数量上都可以选择.其目的都在于尽可能的培育出更优秀的后 <br>代 <br>8)变异 <br>变异操作时按照基因座来的.比如说没计算2万个基因座就发生一个变异(我们现在的每个个体有5个基因座.也就是说要进化1000代后才会在其中的某个基因座发生一次变异.)变异的结果是基因座上的等位基因发生了变化.我们这里的例子就是把0变成1或则1变成0. <br>至此,我们已经产生了一个新的(下一代)集团.然后回到第4步,周而复始,生生不息下去:) </p>
<p><strong>伪代码实例(适合爱看代码的朋友~):</strong> </p>
<p><font face=Tahoma size=2>//Init population <br>foreach individual in population <br>{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;individual = Encode(Random(0,31)); <br>}</font> </p>
<p><font face=Tahoma size=2>while (App.IsRun) <br>{&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//计算个体适应度 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int TotalF = 0; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach individual in population <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;individual.F = 1000 - (Decode(individual)-10)^2; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TotalF += individual.F; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</font> </p>
<p><font face=Tahoma size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//------选择过程,计算个体选择概率----------- <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach individual in population <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;individual.P = individual.F / TotalF; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//选择 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(int i=0;i&lt;4;i++) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//SelectIndividual(float p)是根据随机数落在段落计算选取哪个个体的函数 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MatingPool[i] = population[SelectIndividual(Random(0,1))]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//-------简单交叉--------------------------- <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//由于只有4个个体,配对2次 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(int i=0;i&lt;2;i++) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MatingPool.Parents[i].Mother = MatingPool.RandomPop(); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MatingPool.Parents[i].Father = MatingPool.RandomPop(); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</font> </p>
<p><font face=Tahoma size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//交叉后创建新的集团 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;population.Clean(); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach Parent in MatingPool.Parents <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//注意在copy 双亲的染色体时在某个基因座上发生的变异未表现. <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child1 = Parent.Mother.DivHeader + Parent.Father.DivEnd; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child2 = Parent.Father.DivHeader + Parent.Mother.DivEnd; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;population.push(child1); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;population.push(child2); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <br>}&nbsp; <br></font><strong>小结:</strong> <br>遗传算法中最重要的过程就是选择和交叉。 <br>选择要能够合理的反映"适者生存"的自然法则，而交叉必须将由利的基因尽量遗传给下一代(这个算法很关键！) <br>还有就是编码的过程要能够使编码后的染色体能充分反映个体的特征并且能够方便计算。 </p>
<img src ="http://www.blogjava.net/coundy/aggbug/109616.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coundy/" target="_blank">Coundy</a> 2007-04-10 13:57 <a href="http://www.blogjava.net/coundy/articles/109616.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>功能丰富的 Perl: 遗传算法仿真多细胞机体</title><link>http://www.blogjava.net/coundy/articles/109178.html</link><dc:creator>Coundy</dc:creator><author>Coundy</author><pubDate>Sat, 07 Apr 2007 15:51:00 GMT</pubDate><guid>http://www.blogjava.net/coundy/articles/109178.html</guid><wfw:comment>http://www.blogjava.net/coundy/comments/109178.html</wfw:comment><comments>http://www.blogjava.net/coundy/articles/109178.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coundy/comments/commentRss/109178.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coundy/services/trackbacks/109178.html</trackback:ping><description><![CDATA[　作者:bitsCN整理 &nbsp;&nbsp;<strong>来源:</strong>ChinaITLab&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我的前两篇关于使用 Perl 实现遗传算法（GA）的文章（参阅 参考资料）讲述的是个体细胞的变异与生命周期，它的适合度（fitness）完全依赖于它们自己的 DNA。本文将介绍如何仿真一个多细胞机体。具体的应用程序将会生成由其复杂性和正确性决定的字谜（letter puzzles）。要获得 GA 的背景知识，您应该去参考先前的两篇文章。<br>　　<br>　　个体细胞是字谜中的字母块（letter tiles）。它们的适合度将取决于它们与所有其他细胞的组合，所以，在应用于上下文之前，细胞 DNA 本身没有意义。而且， DNA 必须较长，但并不复杂。它只是要告诉我们任意一个特定细胞可能会连接到哪些字母，当然，它也会告诉我们这个特定细胞的字母（也可能是一个空块）。<br>　　<br>　　那么，让我们来开始设计!<br>　　<br>　　<strong>仿真设计</strong><br>　　有两方面基本设计。首先是个体细胞的设计，其次是细胞间交互的设计。我将从个体细胞开始讲起。<br>　　<br>　　本质上，每个细胞都是纵横拼字谜（crossword puzzle）中的一个字母。那将是 DNA 的一个片段。而且， DNA 将决定一个细胞与其他字母的适合程度。这样，对英文纵横字谜来说，&#8220;an&#8221;和&#8220;he&#8221;将是合适的组合，而&#8220;xz&#8221;将不是。这并不是说&#8220;xz&#8221;不可能出现，而只是说使用它生成的纵横字谜没有多高的价值。我将使用一个词典，这个词典默认位于 GNU/Linux 系统的 /usr/share/dict/words 中（至少在我的 Debian 系统中是这样 —— 否则，可以使用 whereis 或 locate 命令来找到它，并相应地修改 $words_file）。<br>　　<br>　　细胞之间的交互将发生在一个 N 乘 N 的字谜中，其中 N 在命令行中给定，默认为 10。在任何时刻都会有 N^2 个细胞被选中，留下 N*2 个细胞（所以，在一个 10x10 字谜的循环周期中，总共有 120 个细胞）。这些数字是任意的，不太重要，只不过，一个大的&#8220;无限制&#8221;的池将使细胞选择的值的适合度降低，而一个小的池将限制元素的机会。<br>　　<br>　　您应该记住，这里的目标不是生成&#8220;正确&#8221;的解决方案 —— 没有这样的解决方案。目标是仿真细胞之间的交互，特别要注意平衡字母细胞所需要的空块细胞。<br>　　<br>　　从初始细胞池中对细胞的选择由字母关联性来完成。如果在字谜板（puzzle board）上没有其他细胞，那么任何细胞都是可以的。不过，如果程序正在为一个与&#8220;A&#8221;和&#8220;Q&#8221;相邻的块来选择细胞，那么&#8220;A&#8221;和&#8220;Q&#8221;的细胞关联性就有关系了。因此，细胞关联性是 DNA 的一个基本部分，和细胞的字母一样受到变异的影响。细胞关联性的范围是 0 到 255，所以可以方便地由 DNA 的一个字节来描述它。<br>　　<br>　　最后，我将缓存细胞所构成的词。我不会采用这种简单的方法：选出每个块并指出它构成哪些词。您想知道为什么吗？因为我试过那种方法，为了得到正确的方法，浪费了好多个小时的时间，而且它并不快!<br>　　<br>　　我的方法是，从左到右，从上到下对谜板进行扫描（两遍，这是为了得到垂直方向和水平方向的词）。当找到一个词后，我会记住构成那个词的细胞，然后将那个词添加到所有那些细胞的词缓存中。词缓存是一个数组，不是散列表，反映出事实上同一个词可以出现在水平方向上，也可以出现在垂直方向上，但细胞只能归于一个这样的词。对于微不足道的细胞来说，那将是极其不公平的。<br>　　<br>　　对于谜板而言，它是一个简单的散列表。我尝试过使用嵌套数组来仿真一个矩阵，不过没有必要那么麻烦。我只需要使用一个具有 x y 键的简单散列表就可以完成仿真。唯一所需要的映射是 xy2index() 函数；我编写了一个名为 index2xy() 的反向映射函数，但是没必要使用它。<br>　　<br>　　<strong>与先前文章的不同之处</strong><br>　　本文中的程序是我先前撰写的两篇遗传算法文章中 GA 仿真程序的改进版本。基于读者 Matt Neuberg 的建议，以及我本人的经验，我做了一些修改。<br>　　<br>　　select_parents() 是不严格的，因为它将不适合的亲本（parents）留在种群（population）中，即使它们的适合度为 0，不可能被选择。为了纠正那一点，我添加了一个额外的 grep() 调用。<br>　　<br>　　recombine() 函数<br>　　我应该提醒您，基于亲本的适合度，亲本种群包含有对亲本的多个引用。亲本越适合，在亲本种群中出现的次数就会多，因而就会有更多机会繁殖下去。<br>　　<br>　　recombine() 使用 List::Util shuffle() 函数来随机组合亲本种群。这样做的效果好于选择随机亲本并保持对哪些已经是亲本的追踪。另外，以前第二个亲本是随机选择出来的，而且我认为这样是对演化的相当准确的描述，但是我改变了那种方法，通过将它们从亲本种群中选择出来然后再插入回去的方式，基于它们的适合度来选择第二个亲本。<br>　　<br>　　清单 1. recombine() 函数<br>　　<br>　　sub recombine<br>　　{<br>　　my $population = shift @_;<br>　　my $pop_size = scalar @$population; # population size<br>　　my @parent_population;<br>　　my @new_population;<br>　　<br>　　my $total_parent_slots = 0;<br>　　<br>　　$total_parent_slots += $_-&gt;{parent}<br>　　foreach @$population;<br>　　<br>　　my $position = 0;<br>　　<br>　　foreach my $individual (@$population)<br>　　{<br>　　foreach my $parenting_opportunity (1 .. $individual-&gt;{parent})<br>　　{<br>　　push @parent_population, $individual;<br>　　}<br>　　$individual-&gt;{parent} = 0;<br>　　}<br>　　<br>　　@parent_population = shuffle @parent_population;<br>　　<br>　　while (1)<br>　　{<br>　　# this could result in a parent breeding with itself, which is not a big deal<br>　　my $parent = shift @parent_population;<br>　　my $parent2 = shift @parent_population;<br>　　my $out_of_parents = 0;<br>　　<br>　　# when we're out of parents...<br>　　unless (defined $parent2)<br>　　{<br>　　$parent2 = $parent;<br>　　$out_of_parents = 1;<br>　　}<br>　　<br>　　my $child = { survived =&gt; 1, parent =&gt; 0, fitness =&gt; 0, dna =&gt; 0 };<br>　　<br>　　# this is breeding!<br>　　my $dna1 = $parent-&gt;{dna};<br>　　my $dna2 = $parent2-&gt;{dna};<br>　　<br>　　# note we do operations on BYTES, not BITS.　This is because bytes<br>　　# are the unit of information (and preserving them is the faster<br>　　# breeding method)<br>　　foreach my $byte (1 .. $dna_byte_length)<br>　　{<br>　　# get one byte from either parent (the parent choice is random) and add it to the child<br>　　vec($child-&gt;{dna}, $byte-1, 8) = vec(((rand() &lt; 0.5) ? $dna1 : $dna2), $byte-1, 8);<br>　　}<br>　　<br>　　push @new_population, $child; # the child is now a part of the new generation<br>　　push @parent_population, $parent2; # use the second parent again, but at the tail end<br>　　last if $out_of_parents;<br>　　}<br>　　<br>　　return \@new_population;<br>　　}<br>　　<br>　　注意，如果最后一个亲本恰好是自亲本种群中获得的，应该如何去设置 $out_of_parents；那是跳出亲本选择循环的唯一途径。<br>　　<br>　　<strong>构建字谜</strong><br>　　字谜网格由相应的名为 build_puzzle() 的函数来构建。种群中的每一个个体细胞都在内部存储了一个网格位置，所以，当我想要找到某个细胞的网格位置时，不必搜索网格或者维持一个外部散列表。每一个个体还拥有一个&#8220;单词&#8221;数组引用，在这个数组中保持有在衍生过程中那个个体生成的单词。<br>　　<br>　　另外，我为每个细胞赋予了一个 ID 属性，不过只是使用它来检查算法的正确性。<br>　　<br>　　在 build_puzzle() 中，所有的个体都安置于 @puzzle_population。我做了一个 @puzzle_population 的拷贝，这样我可以从它里面去删除个体，以使得对个体的改变不会是永久的 —— 它是一个浅拷贝（shallow copy）。选择块的顺序是随机的，再次使用了 List::Util::shuffle()。注意，所有的位置都存储在一个 [x,y] 数组中。那样，数据可以像单一的参数一样传递，而不是多个参数。<br>　　<br>　　清单 2. build_puzzle() 函数<br>　　<br>　　sub build_puzzle<br>　　{<br>　　my $population = shift @_;<br>　　<br>　　my @puzzle_population = @$population; # make a local copy we can alter<br>　　<br>　　my $i = 0;<br>　　foreach (@puzzle_population)<br>　　{<br>　　$_-&gt;{id} = $i++;<br>　　$_-&gt;{position} = undef;<br>　　$_-&gt;{words} = [];<br>　　}<br>　　<br>　　my $puzzle = {};<br>　　<br>　　my @positions;<br>　　<br>　　foreach my $row (0 .. $size-1)<br>　　{<br>　　foreach my $column (0 .. $size-1)<br>　　{<br>　　push @positions, [$row, $column];<br>　　}<br>　　}<br>　　<br>　　foreach my $p (shuffle @positions)<br>　　{<br>　　my $row　　= $p-&gt;[0];<br>　　my $column = $p-&gt;[1];<br>　　<br>　　my $cell =　choose_tile(\@puzzle_population, $puzzle, $p);<br>　　$cell-&gt;{position} = $p;<br>　　$puzzle-&gt;{xy2index($p)} = $cell;<br>　　}<br>　　<br>　　return $puzzle;<br>　　}<br>　　<br>　　注意，上面的 recombine() 和 build_puzzle()中，以及程序所有其他位置，都没有类似于 $i 的计数器。由于 Perl 没有内存分配问题，所以对我来说缺陷的最主要来源就是追踪计数器变量的错误（错误的初始化，错误的增量，或者错误的边界）。这并不是说我在编写 Perl 程序的时候出现了很多缺陷，只是我发现计数器变量会增加使用时出现缺陷的可能性。<br>　　<br>　　现在登场的是 choose_tile()。字谜中的每一个网格位置都会调用它来选择一个将成为字谜块的细胞。在为网格 <br>
<img src ="http://www.blogjava.net/coundy/aggbug/109178.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coundy/" target="_blank">Coundy</a> 2007-04-07 23:51 <a href="http://www.blogjava.net/coundy/articles/109178.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>