They&#x27;re all kind of the same idea. As you note, each has some magic mixing function to get some output that&#x27;s (hopefully) entirely uncorrelated to your input, but the specifics are a bit different.<p>When you encrypt something, the output is the same size as the input. The key is not mutated.  You have to be able to go the other way if you know the key (for symmetric encryption).<p>When you hash something, the input is variable but the output length is fixed.  Typically this is one-way - you should not be able to reconstruct the input for a given output.<p>When you have a stateful PRNG you get a fixed-length output but you have some internal state that gets changed with every call. So its inputs are generate(state) and you yield output and newstate.  Going the other way is possible in theory but not typical.<p>The state for PRNGs is usually larger than the amount of output yielded, sometimes by quite a lot. E.g. Mersenne Twister has about 2.5 KB of state but yields only 32 bits of output per iteration. You generally want to store newstate every time, because setting them up is often expensive (MT takes a lot of calls before it&#x27;s fully initialized and providing good randomness) and iterating through N-1 steps every time can take a very long time in a long-running program.<p>Usually you want PRNGs to be fast (but strongly random), but encryption and hashing should sometimes be slow (eg to protect passwords in a DB, or as a proof-of-work as in Bitcoin&#x2F;Hashcash). It all depends on what role you&#x27;re using them in.<p>You can sorta compose them into each other - like you can make a stateful PRNG by doing a hash on some internal state and then mixing some of it back into the state (eg like RDRAND).  Or by encrypting a counter with a seed to make a PRNG (like Random123).  Here be dragons, though - there&#x27;s nothing guaranteeing your hand-rolled encryption algorithm will actually resist an attack, or whatever.  So stay with a known, tested implementation.