Skip to main content

graphics - how to find circles that intersect a square


I use the following (based on ideas and functions I got as ansers in [1], [2] by, repsectively, @ybeltukov and @J.M.) in order to find random circles with centers inside a unit square that intersect this square.


findPoints = 
Compile[{{n, _Integer}, {low, _Real}, {high, _Real}, {minD, _Real}},
Block[{data = RandomReal[{low, high}, {1, 2}], k = 1, rv, temp},

While[k < n, rv = RandomReal[{low, high}, 2];
temp = Transpose[Transpose[data] - rv];
If[Min[Sqrt[(#.#)] & /@ temp] > minD, data = Join[data, {rv}];
k++;];];
data]];

npts = 150;
r = 0.03;
minD = 2.2 r;
low = 0;

high = 1;

SeedRandom[159];
Timing[pts = Select[findPoints[npts, low, high, minD],
Area[ImplicitRegion[((x - #[[1]])^2 + (y - #[[2]])^2 <=
r^2) && ! (0 < x < 1 && 0 < y < 1), {x, y}]] != 0 &];]
(* {8.68383, Null}*)
pts // Length
(*33*)
g2d = Graphics[{FaceForm@Lighter[Blue, 0.8],

EdgeForm@Directive[Thickness[0.004], Black], Disk[#, r] & /@ pts},
Background -> Lighter@Gray, Frame -> True,
PlotRange -> {{low, high}, {low, high}}]

enter image description here


As we notice the function that finds pts is not the best one regarding time efficiency (this is in contrast with the clever original approach of J.M.).


1) Any suggestion in order to decrease this timing?


2) As a second question, how we can depict only the part of the circles that lie within the square?


Thanks.



Answer




Since you are discarding all circles strictly in the interior, substantial time is spent generating them so that they do not intersect other circles and later determining that they are strictly interior to the boundary.


Better is to only generate circles that intersect the boundary. This can be done by generating an x value between low and high, a y value between -radius and radius, and a random integer between 0 and 3 that determines which side it lands on. Then adjust accordingly. I remark that this is not fully correct insofar as it messes up at corners (we do not allow the center to go in the four regions diagonal to the corners). But I believe that issue was already present in the original code. Also it's not hard to fix this should it be important. (Method: generate the first value between low-radius and high+radius, and discard any resulting ones that go too far outside and fail to hit a corner.)


To determine which need discarding it is much more efficient to generate a bunch at a time, create a NearestFunction, and discard any newcomers that have a neighbor within the specified minimal separation. There is a tuning issue of how many should be in a "bunch". I doubt I have this optimal, but 5/4 times the number needed at any given step seems to do reasonably well.


I have not tried to put this through Compile. Possibly it could be made faster that way. But my guess is most time is spent in "fast" evaluator calls to the nearest function and that won't change with Compile, so I'm not too optimistic.


findPoints2[n_, low_, high_, rad_, minD_] := Module[
{pts = {}, pts1, pts2, sides, len = Ceiling[5*n/4], nf, nbrs},
While[Length[pts] < n,
posns1 = RandomReal[{low, high}, len];
posns2 = RandomReal[{-rad, rad}, len];
pts1 = Transpose[{posns1, posns2}];

sides = RandomInteger[3, len];
pts2 = Table[
Switch[sides[[j]],
0, pts1[[j]],
1, Reverse[pts1[[j]]],
2, {pts1[[j, 1]], high - pts1[[j, 2]]},
3, {high - pts1[[j, 2]], pts1[[j, 1]]}]
, {j, len}];
nf = Nearest[Join[pts, pts2]];
nbrs = Map[nf[#, {Infinity, minD}] &, pts2];

pts2 = Select[nbrs, Length[#] == 1 &][[All, 1]];
pts = Join[pts, pts2];
frac = 5/4*(n - Length[pts])/n;
len = Ceiling[n*frac];
];
Take[pts, n]
]

The example:


r = 0.03;

minD = 2.2 r;
low = 0;
high = 1;

We can get 50 without much trouble.


Timing[pts = findPoints2[50, low, high, r, minD];]

(* Out[166]= {0.062400, Null} *)

Obtaining 58 or so is another matter entirely since they start getting tightly packed by then. For that sort of packing one might need to change the method to only generate within regions that are mostly viable. This would take real work though.



Comments

Popular posts from this blog

functions - Get leading series expansion term?

Given a function f[x] , I would like to have a function leadingSeries that returns just the leading term in the series around x=0 . For example: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x)] x and leadingSeries[(1/x + 2 + (1 - 1/x^3)/4)/(4 + x)] -(1/(16 x^3)) Is there such a function in Mathematica? Or maybe one can implement it efficiently? EDIT I finally went with the following implementation, based on Carl Woll 's answer: lds[ex_,x_]:=( (ex/.x->(x+O[x]^2))/.SeriesData[U_,Z_,L_List,Mi_,Ma_,De_]:>SeriesData[U,Z,{L[[1]]},Mi,Mi+1,De]//Quiet//Normal) The advantage is, that this one also properly works with functions whose leading term is a constant: lds[Exp[x],x] 1 Answer Update 1 Updated to eliminate SeriesData and to not return additional terms Perhaps you could use: leadingSeries[expr_, x_] := Normal[expr /. x->(x+O[x]^2) /. a_List :> Take[a, 1]] Then for your examples: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x), x] leadingSeries[Exp[x], x] leadingSeries[(1/x + 2 + (1 - 1/x...

How to thread a list

I have data in format data = {{a1, a2}, {b1, b2}, {c1, c2}, {d1, d2}} Tableform: I want to thread it to : tdata = {{{a1, b1}, {a2, b2}}, {{a1, c1}, {a2, c2}}, {{a1, d1}, {a2, d2}}} Tableform: And I would like to do better then pseudofunction[n_] := Transpose[{data2[[1]], data2[[n]]}]; SetAttributes[pseudofunction, Listable]; Range[2, 4] // pseudofunction Here is my benchmark data, where data3 is normal sample of real data. data3 = Drop[ExcelWorkBook[[Column1 ;; Column4]], None, 1]; data2 = {a #, b #, c #, d #} & /@ Range[1, 10^5]; data = RandomReal[{0, 1}, {10^6, 4}]; Here is my benchmark code kptnw[list_] := Transpose[{Table[First@#, {Length@# - 1}], Rest@#}, {3, 1, 2}] &@list kptnw2[list_] := Transpose[{ConstantArray[First@#, Length@# - 1], Rest@#}, {3, 1, 2}] &@list OleksandrR[list_] := Flatten[Outer[List, List@First[list], Rest[list], 1], {{2}, {1, 4}}] paradox2[list_] := Partition[Riffle[list[[1]], #], 2] & /@ Drop[list, 1] RM[list_] := FoldList[Transpose[{First@li...

front end - keyboard shortcut to invoke Insert new matrix

I frequently need to type in some matrices, and the menu command Insert > Table/Matrix > New... allows matrices with lines drawn between columns and rows, which is very helpful. I would like to make a keyboard shortcut for it, but cannot find the relevant frontend token command (4209405) for it. Since the FullForm[] and InputForm[] of matrices with lines drawn between rows and columns is the same as those without lines, it's hard to do this via 3rd party system-wide text expanders (e.g. autohotkey or atext on mac). How does one assign a keyboard shortcut for the menu item Insert > Table/Matrix > New... , preferably using only mathematica? Thanks! Answer In the MenuSetup.tr (for linux located in the $InstallationDirectory/SystemFiles/FrontEnd/TextResources/X/ directory), I changed the line MenuItem["&New...", "CreateGridBoxDialog"] to read MenuItem["&New...", "CreateGridBoxDialog", MenuKey["m", Modifiers-...