def random_walk_path(size, distribution=None, start_points=None):
    """
    Performs random walks as a markov chain with an arbitrary distribution and start points
    :param size: The size of the image as (width, height)
    :param distribution: A hash of {(dx, dy): probability} the distribution
    that describes how the random walk moves to neighboring states. This does not need to be normalized to sum to 1.
    Both dx and dy should be one of [-1, 0, 1].
     By default, this is a uniform distribution over all adjacent states.
    :param start_points: A set of starting points for each random walk.
    By default, these are 10 random points in the image.
    :return: An iterator of paths, with each path being an iterator of (x,y) pixels
    """

    # TODO allow non-adjacent neighbors also?

    def random_walk_iter(start_pt):
        x, y = start_pt
        while True:
            dx, dy = weighted_random_choice(iter(distribution.items()))
            x += dx
            y += dy
            if x < 0 or x >= width or y < 0 or y >= height:
                return
            yield (x, y)

    width, height = size
    neighbors = [(dx, dy) for dx in [-1, 0, 1] for dy in [-1, 0, 1] if dy != 0 or dx != 0]
    if distribution is None:
        distribution = {n: 0.125 for n in neighbors}
    else:
        for n in neighbors:
            if n not in distribution:
                distribution[n] = 0
        neighbor_sum = sum(distribution[n] for n in neighbors)
        if neighbor_sum <= 0:
            raise ValueError("Distribution must be positive, nonzero for adjacent neighbors")
        else:
            distribution = {n: p / float(neighbor_sum) for n, p in distribution.items()}
    if start_points is None:
        start_points = [(int(random() * width), int(random() * height)) for _ in range(10)]

        # # by default, just start at each pixel on the left edge of the image
        # start_points = [(0, y) for y in xrange(height)]

    for x0, y0 in start_points:
        yield random_walk_iter((x0, y0))