May 5, 2014

humanRandom

Sometimes you can improve randomness by making it less random.

When generating user-facing content true randomness is often problematic.

>>> from random import randint >>> print(''.join(u'●■◆▲'[randint(0,3)] for _ in range(0,50))) "●■◆■■●●●●◆▲▲●▲●◆●▲▲▲■■■■▲▲■■◆◆◆●▲■●▲▲▲◆▲◆●■■■◆●●■●"

Consider using this sequence to layout a series of test images, select a random quote or to populate some "fill" data.

Something doesn't look right, they'll say. I don't like it. Doesn't feel right. Why?

●■◆■■●●●●▲▲●▲●◆●▲▲▲■■■■▲▲■■◆◆◆●▲■●▲▲▲◆▲◆●■■■●●■●

Look at all that contiguous repetition. "Four s in a row?! That's not random!"

Of course it is random — indeed, the very definition of random — but people instinctively hate it.

Improve your randomness by making it less random.

Let's track the state of previous random choices and ensure that we don't generate the same result too often.

from math import ceil, sqrt humanRandomState = {} def humanRandomRange(key, lo, hi): remember = min(ceil(sqrt(max(hi - lo, 0))), 10) tries = max(remember * 2, 1) state = humanRandomState.get(key) or [] r = None for i in range(0, tries): r = randint(lo, hi) if r not in state: break state.insert(0, r) if len(state) > remember: state.pop() humanRandomState[key] = state return r

Is it worth it? The results:

>>> print(''.join(u'●■◆▲'[humanRandomRange('shapes',0,3)] for _ in range(0,50))) "■▲■◆▲◆▲●◆■◆▲◆▲●▲●■◆●■▲●◆●▲●▲■●●◆▲●◆●◆▲■◆●▲●■●◆■▲●■"

Only a single dupe:

"■▲■◆▲◆▲●◆■◆▲◆▲●▲●■◆●■▲●◆●▲●▲■●●◆▲●◆●◆▲■◆●▲●■●◆■▲●■"

Ahh, that looks better. Much more random, they'll say.

References

Comments

Ryan Flynn is a programmer and problem solver.