import math


def crowding_replacement(random, population, parents, offspring, args):
    """Performs crowding replacement as a form of niching.
    
    This function performs crowding replacement, which means that
    the members of the population are replaced one-at-a-time with
    each of the offspring. A random sample of `crowding_distance`
    individuals is pulled from the current population, and the
    closest individual to the current offspring (where "closest"
    is determined by the `distance_function`) is replaced by that
    offspring, if the offspring is better. It is possible for one 
    offspring to replace an earlier offspring in the same generation, 
    given the random sample that is taken of the current survivors 
    for each offspring.
    
    .. Arguments:
       random -- the random number generator object
       population -- the population of individuals
       parents -- the list of parent individuals
       offspring -- the list of offspring individuals
       args -- a dictionary of keyword arguments

    Optional keyword arguments in args:    
    
    - *distance_function* -- a function that accepts two candidate 
      solutions and returns the distance between them (default 
      Euclidean L2 distance)
    - *crowding_distance* -- a positive integer representing the 
      number of closest solutions to consider as a "crowd" (default 2)
       
    """
    def distance(x, y):
        return math.sqrt(sum([(a - b)**2 for a, b in zip(x, y)]))
    try:
        distance_function = args['distance_function']
    except KeyError:
        distance_function = distance
        args['distance_function'] = distance_function
    crowding_distance = args.setdefault('crowding_distance', 2)
    survivors = population
    for o in offspring:
        pool = random.sample(survivors, crowding_distance)
        closest = min(pool, key=lambda x: distance_function(o.candidate, x.candidate))
        if o > closest:
            survivors.remove(closest)
            survivors.append(o)
    return survivors