A theory I have come up with and have also explained in a Facebook post (not in english though so I it's not worth linking that here) as well is that pokemon on server have a "state" which consist of data such as location, pokemon id (= which pokemon this is), timestamp (for knowing when to de-spawn), but also its IV values
(if what VisualJae said here is true which seems to be the case), and some random number X.
When you encounter a pokemon, the server responds with this data entry or state stored for the pokemon. Before the battle starts, the game does the following calculation (or at least something similar to)
Code:
1 + (X % (2 * (TrainerLV + 0.5))) / 2
which gives a value that can be anything in set (1, 1.5, 2, 2.5, ..., TrainerLV + 1.5). Here % is the
modulo operator.
Now why this may work is because the result of
X % (2 * (TrainerLV + 0.5)) varies a lot depending on what your TrainerLV is. For example if X is 1243 (which is a very small number - this is normally something way bigger if it's a randomly generated integer for instance), the above calculation
(under code-tags) would give value
10.5 when TrainerLV is
25. This simply means you would encounter the pokemon at lv 10.5 if you were at level 25 yourself. On the other hand, for example trainer at level
19 would encounter the pokemon at level
18.0 whileas for trainer at level
32 would encounter the pokemon only at level
5.0.
This kind of equation gives equal probability for all levels between level 1 and your "TrainerLV + 1.5" for large, unknown value of X, and thus the equation fits well for the purpose Niantic was looking forward to. What happens next is that the game simply calculates the CP for the encountered pokemon based on its IV values (stored on server), and the level this equation gave. The equation used for this is found right
here.