Depends on what you are using it for. For game noise and even hashing for hash tables, you want something fast and good enough. For simulation you want something fast, and good statistical properties. For crytography you want it to be as close as practical to a random oracle model and one way. Speed is a distant 3rd.

Rule one: for cryto get an official one. Never do your own.

Otherwise doing your own is quite practical but perhaps a little bit of a waste of time. For simulation my rng that i posted here, is many times faster than MT( a crap rng for simulations seriously) and has extra statistical properties over both MT and java.util.Random(), and faster than both. I said that already.

For games you can get away with a lot. A common one for 2 numbers is something like

with primes a,b,c. This can be extended for any number of numbers. This will give some "patterns" but for the most part is not too bad. The * are not really that expensive compared to moving all the data around. Then you can just use bitshift mixing functions(aka the wang example), both conditional and non conditional shifts. There are lots around the web for that, with different values of good. Most are for hash table stuff but work non the less. There are some that are optimized for the old Shader langs as well. These often use small lookup tables combined with the above examples. Some use nested lookups

1
| results=randTable[(randTable[x&0xff]+y)&0xff]; |

. But i have never bothered since there is a small number space for that.

A bad function would be

Since we get a linear relationship between both values, and close input results in close output. Typically we want it to be more random.