Randomness of Python's random

I'm using Python to generate images using dashed lines for stippling. The period of the dashing is constant, what changes is dash/space ratio. This produces something like this:

enter image description here

However in that image the dashing has a uniform origin and this creates unsightly vertical gutters. So I tried to randomize the origin to remove the gutters. This sort of works but there is an obvious pattern:

enter image description here

Wondering where this comes from I made a very simple test case with stacked dashed straight lines:

  • dash ratio: 50%
  • dash period 20px
  • origin shift from -10px to +10px using random.uniform(-10.,+10.)(*) (after an initial random.seed()

enter image description here

And with added randomness:

enter image description here

So there is still pattern. What I don't understand is that to get a visible gutter you need to have 6 or 7 consecutive values falling in the same range (says, half the total range), which should be a 1/64 probability but seems to happen a lot more often in the 200 lines generated.

Am I misunderstanding something? Is it just our human brain which is seeing patterns where there is none? Could there be a better way to generate something more "visually random" (python 2.7, and preferably without installing anything)?

(*) partial pixels are valid in that context

Annex: the code I use (this is a Gimp script):

    #!/usr/bin/env python
    # -*- coding: iso-8859-15 -*-

    # Python script for Gimp (requires Gimp 2.10)
    # Run on a 400x400 image to see something without having to wait too much
    # Menu entry is in "Test" submenu of image menubar

    import random,traceback
    from gimpfu import *

    def constant(minShift,maxShift):
        return 0

    def triangle(minShift,maxShift):
        return random.triangular(minShift,maxShift)

    def uniform(minShift,maxShift):
        return random.uniform(minShift,maxShift)

    def gauss(minShift,maxShift):
        return random.gauss((minShift+maxShift)/2,(maxShift-minShift)/2)

    variants=[('Constant',constant),('Triangle',triangle),('Uniform',uniform),('Gauss',gauss)]

    def generate(image,name,generator):
        random.seed()
        layer=gimp.Layer(image, name, image.width, image.height, RGB_IMAGE,100, LAYER_MODE_NORMAL)
        image.add_layer(layer,0)
        layer.fill(FILL_WHITE)
        path=pdb.gimp_vectors_new(image,name)

        # Generate path, horizontal lines are 2px apart, 
        # Start on left has a random offset, end is on the right edge right edge
        for i in range(1,image.height, 2):
            shift=generator(-10.,10.)
            points=[shift,i]*3+[image.width,i]*3
            pdb.gimp_vectors_stroke_new_from_points(path,0, len(points),points,False)
        pdb.gimp_image_add_vectors(image, path, 0)

        # Stroke the path
        pdb.gimp_context_set_foreground(gimpcolor.RGB(0, 0, 0, 255))
        pdb.gimp_context_set_stroke_method(STROKE_LINE)
        pdb.gimp_context_set_line_cap_style(0)
        pdb.gimp_context_set_line_join_style(0)
        pdb.gimp_context_set_line_miter_limit(0.)
        pdb.gimp_context_set_line_width(2)
        pdb.gimp_context_set_line_dash_pattern(2,[5,5])
        pdb.gimp_drawable_edit_stroke_item(layer,path)

    def randomTest(image):
        image.undo_group_start()
        gimp.context_push()

        try:
            for name,generator in variants:
                generate(image,name,generator)
        except Exception as e:
            print e.args[0]
            pdb.gimp_message(e.args[0])
            traceback.print_exc()

        gimp.context_pop()
        image.undo_group_end()
        return;

    ### Registration
    desc="Python random test"

    register(
        "randomize-test",desc,'','','','',desc,"*",
        [(PF_IMAGE, "image", "Input image", None),],[],
        randomTest,menu="<Image>/Test",
    )

    main()

Think of it like this: a gutter is perceptible until it is obstructed (or almost so). This only happens when two successive lines are almost completely out of phase (with the black segments in the first line lying nearly above the white segments in the next). Such extreme situations only happens about one out of every 10 rows, hence the visible gutters which seem to extend around 10 rows before being obstructed.

Looked at another way -- if you print out the image, there really are longish white channels through which you can easily draw a line with a pen. Why should your mind not perceive them?

To get better visual randomness, find a way to make successive lines dependent rather than independent in such a way that the almost-out-of-phase behavior appears more often.

From: stackoverflow.com/q/55934019