def reproduce(self, survivors, p_crossover, two_point_crossover=False, target_size=None):
assert <NUM_LIT:0> <= p_crossover <= <NUM_LIT:1><EOL>if not target_size:<EOL><INDENT>target_size = self.orig_pop_size<EOL><DEDENT>num_survivors = len(survivors)<EOL>cdf = compute_fitness_cdf(survivors, self)<EOL>offspring = []<EOL>while num_survivors + len(offspring) < target_size:<EOL><INDENT>c1 = weighted_choice(survivors, cdf).copy()<EOL>if random.random() < p_crossover:<EOL><INDENT>c2 = random.choice(survivors).copy()<EOL>point1 = random.randrange(<NUM_LIT:0>, c1.length)<EOL>point2 = random.randrange(point1 + <NUM_LIT:1>, c1.length + <NUM_LIT:1>) if two_point_crossover else None<EOL>c1.crossover(c2, point1, point2)<EOL><DEDENT>offspring.append(c1)<EOL><DEDENT>return survivors + offspring<EOL>
Reproduces the population from a pool of surviving chromosomes
until a target population size is met. Offspring are created
by selecting a survivor. Survivors with higher fitness have a
greater chance to be selected for reproduction.

Genetic crossover events may occur for each offspring created.
Crossover mates are randomly selected from the pool of survivors.
Crossover points are randomly selected from the length of the crossed chromosomes.
If crossover does not occur, an offspring is an exact copy of the selected survivor.
Crossover only affects the DNA of the offspring, not the survivors/parents.

survivors:  pool of parent chromosomes to reproduce from
p_crossover:  probability in [0, 1] that a crossover event will
              occur for each offspring
two_point_crossover (default=False):  whether 2-point crossover is used; default is 1-point
target_size (default=original population size): target population size

return:  list of survivors plus any offspring