patternpythonMinor
Simple evolutionary string matching
Viewed 0 times
simplestringmatchingevolutionary
Problem
This is based on a very similar question I asked, found here.
The intent of this program is to start with a set number of random strings of zeros and ones, and eventually evolve them to find one specific string (
It fulfils its purpose, but what do you think, and how can I improve it?
```
import random as r
import operator
import time
import os
target = '111111'
class String(object):
def __init__(self, chromo):
self.chromo = chromo
self.fitness = 5
self.age = 0
def Fitness(string):
for char in string.chromo:
if char == '1':
string.fitness += 1
else:
pass
def SelectParents(strings):
parents = []
parents.append(max(strings, key=operator.attrgetter('fitness')))
strings.remove(parents[0])
parents.append(max(strings, key=operator.attrgetter('fitness')))
strings.append(parents[0])
return parents
def PartStringRand():
string_complete = ''
for _ in range(4):
rand = r.randint(0, 1)
string_element = str(rand)
string_complete = ''.join([string_complete, string_element])
return string_complete
def CreateChild(strings, parents):
for children in range(2):
string_complete = PartStringRand()
string_complete += (parents[0].chromo[r.randint(0, 5)] + parents[1].chromo[r.randint(0, 5)])
child = String(string_complete)
strings.append(child)
def KillOld(strings):
for _ in strings:
if _.age > 2:
strings.remove(_)
#First strings
strings = []
for _ in range(5):
string_complete = ''
for _ in range(6):
rand = r.randint(0, 1)
string_element = str(rand)
string_complete = ''.join([string_complete, string_element])
string = String(string_complete)
strings.append(string)
if string.chromo == "111111":
strings.remove(string)
for string in strings:
print string.chromo
raw_input()
#Start incremental mutation
c = 0
for _ in range(50
The intent of this program is to start with a set number of random strings of zeros and ones, and eventually evolve them to find one specific string (
'111111'). It fulfils its purpose, but what do you think, and how can I improve it?
```
import random as r
import operator
import time
import os
target = '111111'
class String(object):
def __init__(self, chromo):
self.chromo = chromo
self.fitness = 5
self.age = 0
def Fitness(string):
for char in string.chromo:
if char == '1':
string.fitness += 1
else:
pass
def SelectParents(strings):
parents = []
parents.append(max(strings, key=operator.attrgetter('fitness')))
strings.remove(parents[0])
parents.append(max(strings, key=operator.attrgetter('fitness')))
strings.append(parents[0])
return parents
def PartStringRand():
string_complete = ''
for _ in range(4):
rand = r.randint(0, 1)
string_element = str(rand)
string_complete = ''.join([string_complete, string_element])
return string_complete
def CreateChild(strings, parents):
for children in range(2):
string_complete = PartStringRand()
string_complete += (parents[0].chromo[r.randint(0, 5)] + parents[1].chromo[r.randint(0, 5)])
child = String(string_complete)
strings.append(child)
def KillOld(strings):
for _ in strings:
if _.age > 2:
strings.remove(_)
#First strings
strings = []
for _ in range(5):
string_complete = ''
for _ in range(6):
rand = r.randint(0, 1)
string_element = str(rand)
string_complete = ''.join([string_complete, string_element])
string = String(string_complete)
strings.append(string)
if string.chromo == "111111":
strings.remove(string)
for string in strings:
print string.chromo
raw_input()
#Start incremental mutation
c = 0
for _ in range(50
Solution
One thing that immediately jumped out:
If you are using the variable, give it a better name (even just
Next, your mutation seems odd, as it generates four new random characters then picks two from the parents. Instead, I would have done:
i.e. for each position, pick randomly between the two parents.
Note that the function returns the new
is clearer than adding to
You can simplify
to:
(don't use
(Note that you define
Rather than calculate the
make the function
Your
This also decouples the fitness from the
You could also generally improve
This sorts
def KillOld(strings):
for _ in strings:
if _.age > 2:
strings.remove(_)_ is used by convention to indicate "I won't be using this variable", e.g.:for _ in range(5):
do_something()If you are using the variable, give it a better name (even just
s). Also, functions' names should be lowercase_with_underscores per PEP-0008.Next, your mutation seems odd, as it generates four new random characters then picks two from the parents. Instead, I would have done:
def create_child(parents):
return String("".join(r.choice(c) for c in zip(*(p.chromo for p in parents))))i.e. for each position, pick randomly between the two parents.
Note that the function returns the new
String, rather than adding it to strings. I would say that:strings.append(create_child(parents))is clearer than adding to
strings as a side-effect. Similarly, I would probably rewrite KillOld to return a new list, rather than mutate its argument (and call it kill_old).You can simplify
string_complete = ''
for _ in range(6):
rand = r.randint(0, 1)
string_element = str(rand)
string_complete = ''.join([string_complete, string_element])
string = String(string_complete)to:
s = String("".join(map(str, (r.randint(0, 1) for _ in range(6)))))(don't use
string as a variable name; you may want to use that module later). And rather than append then remove it, check first:if s.chromo != target:
strings.append(s)(Note that you define
target then don't use it - replace all '111111' with target and you can change it much more easily in future!)Rather than calculate the
s.fitness for each s, then have a complex key:parents.append(max(strings, key=operator.attrgetter('fitness')))make the function
fitness that takes a single String and returns the fitness score:def fitness(s):
return sum(c == t for c, t in zip(s.chromo, target))Your
key is then e.g.:parents.append(max(strings, key=fitness))This also decouples the fitness from the
String object itself; you no longer need the s.fitness attribute.You could also generally improve
SelectParents; remove and append again is not efficient.def select_parents(strings):
return sorted(strings, key=fitness)[-2:]This sorts
strings in ascending fitness, then returns a sliced list of the last (up to) two items.Code Snippets
def KillOld(strings):
for _ in strings:
if _.age > 2:
strings.remove(_)for _ in range(5):
do_something()def create_child(parents):
return String("".join(r.choice(c) for c in zip(*(p.chromo for p in parents))))strings.append(create_child(parents))string_complete = ''
for _ in range(6):
rand = r.randint(0, 1)
string_element = str(rand)
string_complete = ''.join([string_complete, string_element])
string = String(string_complete)Context
StackExchange Code Review Q#50927, answer score: 5
Revisions (0)
No revisions yet.