This is not an efficient answer but it is fun to play with so I thought I'd post it. For efficiency the use of `Nearest` might provide a good starting point.
g[n_, {low_, high_}, minDist_, step_: 1] :=
Block[{data = RandomReal[{low, high}, {n, 2}], temp, happy, sdata,
hdata},
While[True,
temp = ((Nearest[data][#, 2][[-1]] & /@ data));
happy =
Boole@Thread[MapThread[EuclideanDistance, {temp, data}] > minDist];
hdata = Pick[data, happy, 1];
sdata = Pick[data, happy, 0];
If[sdata === {}, Break[]];
sdata += RandomReal[{-step, step}, {Length[sdata], 2}];
data = Join[sdata, hdata];
];
data
]
The idea is to do an initial sampling and then allow the points that are too close to "walk" somewhere else. The function takes a desired number of points `n`, a low and high value for the data range, the minimum acceptable distance between points `minDist` and a `step` argument which allows points to "walk" up to a certain distance in the x and y directions.
Its especially fun to watch dynamically.
g[150, {0, 30}, 1.5, 1]
![enter image description here][1]
[1]: https://i.sstatic.net/NsuiM.gif