- 論壇徽章:
- 0
|
本帖最后由 aef25u 于 2020-01-06 19:21 編輯
仿照python用perl6實現了最簡單的遺傳算法,但感覺臃腫,請大牛幫精簡或改寫地更perler一些,只改部分代碼均行。
其中:1、dot函數自已實現的只適用簡單情況,不知perl6有沒有現成的或哪個模塊能提供?
2、np.random.choice(python版函數),我用自己想的方法進行模擬(位于select()函數內),求改寫地更簡潔或更好的版本
- my @p=@fitness.map: {$_/sum(@fitness)};
- my %h=(0..@p.elems-1).map: {$_=>($POP_SIZE* @p[$_]*10).round(1)+1};
- my @sample;
- for %h.kv -> $key, $value {
- #say("$key=>$value");
- push(@sample,$key) for (1..$value);
- }
- my @samshuffle=@sample.sort: { rand };
- my @idx = @samshuffle.roll($POP_SIZE);
復制代碼
- my $DNA_SIZE = 10; # DNA 長度
- my $POP_SIZE = 100; # 種群總數
- my $CROSS_RATE = 0.8; # 交叉配對比率 (DNA crossover)
- my $MUTATION_RATE = 0.003; # 變異率 mutation probability
- my $N_GENERATIONS = 200; # 繁殖代數
- my @X_BOUND = (0, 5); # x坐標范圍
- sub dot(@a,@b) {
- fail "Vector @a length not equal to @b!" unless +@a == +@b;
- my @c = @a <<*>> @b;
- }
- # 查找某函數在X_BOUND范圍內的最大值
- sub F(@x) {return @x.map:{sin(10*$_)*$_ + cos(2*$_)*$_}}
- #適應度函數(y值占所有y值合計的占比越大越有優勢)
- #如果函數會產生負值,將橫坐標軸下移,令所有y值均大于0,
- #因遺傳選擇時select(),@p=@fitness.map: {$_/sum(@fitness)};不能為負值且分母不能為0
- sub get_fitness(@pred) { return @pred.map: {$_ + 1e-3 - min(@pred)}}
- # DNA翻譯規則:即x,二進制表示的DNA轉為十進制,并將其歸一化至(0, 5)
- sub translateDNA(@pop){return @pop.map: { [+](dot($_,(0..^$DNA_SIZE).reverse.map: { 2** $_} )) / (2**$DNA_SIZE-1)*@X_BOUND[1]}}
- #遺傳選擇函數,適者生存,不適者淘汰
- #按編號返回優勢者個體列表;$p參數:表示選擇標準(此例中按照比例來選擇,適應度得分高的p有越大的概率被留下來)
- sub select(@pop, @fitness){
- my @p=@fitness.map: {$_/sum(@fitness)};
- my %h=(0..@p.elems-1).map: {$_=>($POP_SIZE* @p[$_]*10).round(1)+1};
- my @sample;
- for %h.kv -> $key, $value {
- #say("$key=>$value");
- push(@sample,$key) for (1..$value);
- }
- my @samshuffle=@sample.sort: { rand };
- my @idx = @samshuffle.roll($POP_SIZE);
- return @pop[@idx];
- }
- #繁衍,優勢個體作為父親,按交叉配對比率CROSS_RATE在優勢種群中選擇母親,生成小孩
- sub crossover($parent, @pop) {
- if (rand < $CROSS_RATE) {
- my $i=(0..^$POP_SIZE).roll(1);
- say("PARENT<=>MOTHER:["~$parent~"]<=>["~ @pop[$i]~"]");
- my @cross_points =Bool.roll($DNA_SIZE);
- say(@cross_points);
- @cross_points[$_] ?? ($parent[$_]=@pop[$i][$_]) !! $parent[$_] for (0..@cross_points.elems-1);
- say("CHILD:["~$parent~"]");
- }
- return $parent;
- }
- #變異(在DNA_SIZE中的每一位(0/1)基因根據變異率MUTATION_RATE突變為(1/0))
- sub mutate($child) {
- for 0..^$DNA_SIZE ->$point {
- if rand < $MUTATION_RATE {
- ($child[$point] == 0) ?? ($child[$point] = 1) !! 0;
- }
- }
- return $child;
- }
- sub Pop{
- my @pop;
- @pop[$_]= roll($DNA_SIZE, ^2).Array for ^$POP_SIZE;
- return @pop;
- }
- ### 01INPUT:初始化種群DNA
- my @pop=Pop();
- #say(@pop);
- for (1..$N_GENERATIONS) {
- #二進制DNA轉為十進制并輸入函數,得出y值
- my @F_values = F(translateDNA(@pop));
-
- ### 02PROCESS:遺傳選擇過程
- #計算適應度得分,評價個體的優劣
- my @fitness = get_fitness(@F_values);
- my $midx=@fitness.first: * == max(@fitness), :k;
- my $MF_pop=@pop[$midx];
- say("Most fitted DNA: ["~$MF_pop~"]"~translateDNA(Array($MF_pop)));
- #say(@fitness[$midx] ~"="~max(@fitness));
- #模擬自然環境的優勝劣汰,輸入種群與適應度得分,返回優勢種群
- my @pop_sel = select(@pop, @fitness);
- my @pop_copy=@pop_sel;
- for @pop_sel -> $parent is rw {
- #在優勢種群中找到母親,并與父親進行繁衍
- my $child = crossover($parent, @pop_copy);
- ###03OUTPUT:產生更多優勢個體
- #變異
- $child = mutate($child);
- #小孩列表更換為父親列表
- $parent= $child;
- say("Most fitted CHILD=>["~$parent~"]"~translateDNA(Array($parent)));
- }
- }
復制代碼
|
|