diff --git a/evolve_text.py b/evolve_text.py index e0202d2..a202356 100644 --- a/evolve_text.py +++ b/evolve_text.py @@ -11,6 +11,14 @@ https://sites.google.com/site/sd15spring/home/project-toolbox/evolutionary-algorithms """ + +""" +Completed by Kevin Zhang + +Software Design Spring 2016 + +""" + import random import string @@ -92,8 +100,32 @@ def get_text(self): # Genetic operators #----------------------------------------------------------------------------- -# TODO: Implement levenshtein_distance function (see Day 9 in-class exercises) -# HINT: Now would be a great time to implement memoization if you haven't + +dicts = {} + +def levenshtein_distance(a, b): + """ + The levenshtein distance between two strings, using memoization + """ + + if (a,b) in dicts: + return dicts[a,b] + else: + if len(a)==0: + return len(b) + elif len(b)==0: + return len(a) + elif a[0] == b[0]: + option1 = levenshtein_distance(a[1:],b[1:]) + else: + option1 = 1 + levenshtein_distance(a[1:],b[1:]) + option2 = 1 + levenshtein_distance(a[1:],b) + option3 = 1 + levenshtein_distance(a,b[1:]) + + minimum = min(option1,option2,option3) + + dicts[a,b] = minimum + return minimum def evaluate_text(message, goal_text, verbose=VERBOSE): """ @@ -120,18 +152,32 @@ def mutate_text(message, prob_ins=0.05, prob_del=0.05, prob_sub=0.05): (legal) character """ - if random.random() < prob_ins: - # TODO: Implement insertion-type mutation - pass - - # TODO: Also implement deletion and substitution mutations - # HINT: Message objects inherit from list, so they also inherit - # useful list methods - # HINT: You probably want to use the VALID_CHARS global variable + if random.random() < prob_ins: #inserting a character + spot = random.randint(0, len(message)-1) + message.insert(spot, random.choice(VALID_CHARS)) + if random.random() < prob_del: #deleting a character + spot = random.randint(0, len(message)-1) + message.remove(message[spot]) + if random.random() < prob_sub: #subsituting a character + spot = random.randint(0, len(message)-1) + message[spot] = random.choice(VALID_CHARS) return (message, ) # Length 1 tuple, required by DEAP +def crossover(parent1, parent2): + """ + Uses a genetic crossover to mate two parents and create two offspring + Parent 1 and 2 are Message objects, and the returned tuple will also be Messages + """ + cross1 = random.randint(0, min(len(parent1), len(parent2))-2) #creating the first crossing point + cross2 = random.randint(cross1+1, min(len(parent1), len(parent2))-1) #creating the second crossing point + + offspring1 = Message(parent1[0:cross1] + parent2[cross1:cross2] + parent1[cross2:]) + offspring2 = Message(parent2[0:cross1] + parent1[cross1:cross2] + parent2[cross2:]) + + return offspring1, offspring2 #length 2 tuple, which matches thet output of the original method used + #----------------------------------------------------------------------------- # DEAP Toolbox and Algorithm setup #----------------------------------------------------------------------------- @@ -149,7 +195,7 @@ def get_toolbox(text): # Genetic operators toolbox.register("evaluate", evaluate_text, goal_text=text) - toolbox.register("mate", tools.cxTwoPoint) + toolbox.register("mate", crossover) toolbox.register("mutate", mutate_text) toolbox.register("select", tools.selTournament, tournsize=3) @@ -170,7 +216,7 @@ def evolve_string(text): # Get configured toolbox and create a population of random Messages toolbox = get_toolbox(text) - pop = toolbox.population(n=300) + pop = toolbox.population(n=1000) # Collect statistics as the EA runs stats = tools.Statistics(lambda ind: ind.fitness.values) diff --git a/results.txt b/results.txt new file mode 100644 index 0000000..d8cc5ce --- /dev/null +++ b/results.txt @@ -0,0 +1,17 @@ +Evolutionary Toolbox + +Changing population size + +Population size has a big effect on the results in terms of its progression. Having a larger population size means having a larger starting pool and thus more chances of getting the target from the beginning. More Messages means more possible mutations and crossovers, thus creating a better chance for obtaining the goal. A smaller populations means that within the given number of generations there are less Messages to attempt to get the goal. This was confirmed with an experiment of using a starting population of 1000 and of 10. The 1000 population got the goal by the 150th generation, where the 10 population was still a distance of 13 away from the goal by the 500 generation. Population is very important when trying to evolve, as evidently larger populations evolve faster. + +Changing generation size + +Generation size is quite similar to population in that it has a big effect on the progress of the goal. A larger generation window means more time to evolve towards the goal, and a smaller one means less time. A population with a longer generation window will most likely evolve and develop farther than a similar population with a shorter time window. This was confirmed with a population of 100 given a generation of 10 and 1000. The 10 generation population only made it to a distance of 12 away from the goal, whereas the 1000 generation population managed to get down to 2. The inability to reach the goal also shows that population still plays a factor in the generation perspective and that the two do not fulfill the same niche in affecting evolution. + +Changing the crossing over probability + +It appears that changing the crossing over probability does not severely affect the evolution process. This might seem counter-intuitive, as mating would allow for better chances to find the goal. In all honesty, I would agree with that statement. But apparently my algorithm says otherwise. When testing a probability of both .1 and .9, the results were about the same. A 0.1 probability for crossing over got the population to the goal within a distance of 2, and a 0.9 probability for crossing over got the population to the goal within a distance of 3. Perhaps given the size of the population which was 300 or the number of generations was which 500, as long as the probability wasn't 0 or 1.0 it might be about the same given the size. + +Changing the mutation probability + +Changing the mutation probability seems to require a fine balance. Mutations are needed for the population to evolve and find individuals that are closer to the goal, but too many might be damaging to the evolution process and prohibit the ability of the population to make it to the goal in a smooth fashion. This was more or less proven with a mutation probability of .01 and of .9. Both populations ended up about a distance of 3-5 from the goal. However, a probability of .05 or .3 got the population to a distance of 1. It is apparent that mutation must then be in the middle ground, with not too much but not too little.