The Construction of Bit Mixers
jonkagstrom.com
jonkagstrom.com
Many people have done brute-force searches for bit mixers with a good balance of several quality criteria and performance, the latter being somewhat hardware dependent. If you restrict yourself to basic ALU operations, you pretty much always end up in the same part of the bit mixer phase space even allowing for moderate differences in selection function.
There are two design elements of interest that are more difficult to search for using brute force, though possible in principle. The first is taking advantage of ALU parallelism, which can have a large performance impact but is more challenging to model. The second, particularly in the context of hashing, is taking into account that the input may be partially pre-mixed. A generally optimal bit mixer may be slower than one that is specifically optimized from the assumption that the inputs have been partially pre-mixed by a specific algorithm.
https://www.gkbrk.com/wiki/avalanche-diagram/
I guess the next step is to evolve / optimize good constants to improve them further, and rewrite everything in C to make evaluation faster.
Take a look at the PCG paper for a strong family of mixers, including most of what you've tried:
https://www.pcg-random.org/paper.html
Multiplication is very good for mixing low to high, and xorshift moves the bits back to low. PCG also has tricks like "random" xor-shift, where the shift is determined by the top bits.
Additionally, you might want to try varying the word size as in PCG- as you get into strong mixers, they'll all pass PractRand (or take excessively long to fail), so to make things harder you can force them to have less state to work with.
Oh, to convert these invertible mixers into RNGs, you might not want to output the entire result, or you'll fail a birthday test after 2^32 outputs or so. You could do something like squish two halves of the output together, or perhaps even xor the counter into the output like in ChaCha.
-Austin, murmur3 author