Here is the result from an improved version of my algorithm, producing the same 750 circles with 250k attempt cap. It now does more accurate occupied tracking, by only marking grid cells as occupied when they are actually occupied (as determined by at least one of the four corners of a grid cell being within or on the circumference of a circle). Unfortunately, it still uses a bounding box template for a circle to be placed to determine whether it can be placed. This is due to the fact that the number of placement attempts, in total, is quite high and the determination of the actual cells that would be occupied quite expensive, relatively speaking. Therefore, it is only done when a circle is placed rather than during attempts of "can it be placed here."
Now at least there is more space available to place it('s bounding box).
First the new times of five runs:
750 circles have been generated in 2.97 seconds
750 circles have been generated in 6.31 seconds
750 circles have been generated in 4.33 seconds
750 circles have been generated in 3.03 seconds
750 circles have been generated in 2.68 seconds
Below is one of the images produced showing less wasted space, although one has to bear in mind that as each of the circles got placed, its bounding box was used to test for overlap with areas already occupied by (slightly exaggerated approximations of the locations of) existing circles. This resulted in some placement attempts being deemed invalid because the corners of the bounding box of a candidate circle overlapped existing geometry, but if the algorithm was smarter it would see that the circle itself did not. I will think about this more and see if I can come up with a way to improve on this and possibly get rid of the bounding box testing altogether, without greatly sacrificing the run time per test.