Constrained random points on a circle: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|Perl 6}}: formatting, omit useless semicolons)
(Attempted a clearer task description)
Line 1: Line 1:
{{task|Probability and statistics}}
{{task|Probability and statistics}}
The task is to generate a stream of 100 uniformly distributed random points (x,y integer pairs) that lie in a circular disc at 10 to 15 units from its center; and then display/plot them to show a fuzzy circle. The actual number plotted may be less than 100, because some will overlap.
Generate 100 <x,y> coordinate pairs such that x and y are integers sampled from the uniform distribution with the condition that <math>10 \leq \sqrt{ x^2 + y^2 } \leq 15 </math>. Then display/plot them. The outcome should be a "fuzzy" circle. The actual number of points plotted may be less than 100, given that some pairs may be generated more than once.


There are several possible approaches, depending on your language. One is simply to generate random pairs of integers and filter out those that don't satisfy the equation
There are several possible approaches to accomplish this. Here are two possible algorithms.


1) Generate random pairs of integers and filter out those that don't satisfy this condition:
: <math>10 \leq \sqrt{ x^2 + y^2 } \leq 15</math>


:<math>10 \leq \sqrt{ x^2 + y^2 } \leq 15 </math>.
Another is to precalculate the set of all possible points (there are 404 of them) and select from this set. Yet another is to use real-valued polar coordinates then snap to integer Cartesian coordinates. I'm sure there are others.

2) Precalculate the set of all possible points (there are 404 of them) and select randomly from this set.


=={{header|D}}==
=={{header|D}}==

Revision as of 00:18, 10 September 2010

Task
Constrained random points on a circle
You are encouraged to solve this task according to the task description, using any language you may know.

Generate 100 <x,y> coordinate pairs such that x and y are integers sampled from the uniform distribution with the condition that . Then display/plot them. The outcome should be a "fuzzy" circle. The actual number of points plotted may be less than 100, given that some pairs may be generated more than once.

There are several possible approaches to accomplish this. Here are two possible algorithms.

1) Generate random pairs of integers and filter out those that don't satisfy this condition:

   :.

2) Precalculate the set of all possible points (there are 404 of them) and select randomly from this set.

D

From the Python version. <lang d>import std.stdio, std.random, std.math, std.algorithm;

void main() {

   int[2][] possible;
   foreach (x; -15 .. 16)
       foreach (y; -15 .. 16)
           if (10 <= abs(x + y * 1i) && abs(x + y * 1i) <= 15)
               possible ~= [x, y];
   int[int[2]] world;
   foreach (_; 0 .. 100)
       world[randomSample(possible, 1).front]++;
   foreach (x; -15 .. 16) {
       string line;
       foreach (y; -15 .. 16) {
           int[2] p = [x, y];
           line ~= (p in world) ? '0'+min(9, world.get(p, 0)) : ' ';
       }
       writeln(line);
   }

}</lang>

                               
          1     11             
        1     1     1          
            11        1        
     1  1 1 1   1 12           
             1    1 2  2       
       1             2         
       2                  1    
   11                     1    
                       1 11    
   11 1                 21   1 
 1 1                           
    1                        1 
                               
  1                         1  
1                              
  111                          
                           1   
    1                       1  
  1                            
 2  1                      1   
     1                     11  
                       1 11    
    12  1                11    
   1 2    2         1          
    1     1          1   1     
       11     1      1   1     
         1211          1       
                 1  2 1        
          1         1          
               1               

Haskell

Using Knuth Shuffle <lang haskell>import Data.List import Control.Monad import Control.Arrow import Rosetta.Knuthshuffle

task = do

 let blanco = replicate (31*31) "  "
     cs = sequence [[-15,-14..15],[-15,-14..15]] :: Int
     constraint = uncurry(&&).((<= 15*15) &&& (10*10 <=)). sum. map (join (*))

-- select and randomize all circle points

 pts <- knuthShuffle $ filter constraint cs

-- 'paint' first 100 randomized circle points on canvas

 let canvas = foldl (\cs [x,y] -> replaceAt (31*(x+15)+y+15) "/ " cs ) blanco (take 100 pts)

-- show canvas

 mapM_ (putStrLn.concat). takeWhile(not.null). map (take 31) $ iterate (drop 31) canvas</lang>

Output (added a trailing space per 'pixel'

*Main> task
                                                              
                      /             / /                       
                          /     /       /                                       
              /                 /                                               
          /     /               /       / / /   /                               
            /                 / /       /                                       
      /     /     /                       /   /                                 
      /         /                           /                                   
        /   / /                                     /   /                       
        / / /                                 /       / /                       
                                                    /                           
    /     /                                       /                             
      / /                                             /                         
      /                                               /   /                     
        /                                                                       
/ /                                                   /     / 
                                                      /   /                     
                                                      /   /                     
      /                                                                         
      /                                             /                           
  /                                                   /       
                                                /     /                         
    /     /                                       /           
              / /                         / /         /       
        /   /       /                             /           
                          / /     / / /   / /                 
          / /             /                   /               
                            / /                               
                /   /   / / /   /       / /                   
                      /             / /

J

This version deals 100 coordinates from the set of acceptable coordinates (much like dealing cards from a shuffled deck):

<lang j>gen=: ({~ 100?#)bind((#~ 1=99 225 I.+/"1@:*:),/,"0/~i:15)</lang>

Example use (gen'' generates the points, the rest of the example code deals with rendering them as a text array): <lang j> '*' (<"1]15+gen )} 31 31$' '

         *                    
              *               
          *  *  * * * *       
    *   *      *  *   *       
                  *   ***     
   **    *         *     **   
   * **                       
      *                   **  
 * *                  *   *   
*   *                  **     
* **                       ** 
** *                      *** 
* **                    *   * 
**                        *   
   *                    **  * 
**  *                         
* **                       *  
*                           * 
   *                        * 
 *                       *    
  **  *                       
      *                  **   
     *                        
     * *            *    *    
    *       ** *     * *      
      *                *      
      **                      
        *   *       *         
           **  *      </lang>

MATLAB

Uses the Monte-Carlo method described above.

<lang MATLAB>function [xCoordinates,yCoordinates] = randomDisc(numPoints)

   xCoordinates = [];
   yCoordinates = [];
   %Helper function that samples a random integer from the uniform
   %distribution between -15 and 15.
   function nums = randInt(n)
       nums = round((31*rand(n,1))-15.5);
   end
   n = numPoints;
   while n > 0
       
       x = randInt(n);
       y = randInt(n);
       norms = sqrt((x.^2) + (y.^2));
       inBounds = find((10 <= norms) & (norms <= 15));
       
       xCoordinates = [xCoordinates; x(inBounds)];
       yCoordinates = [yCoordinates; y(inBounds)];
       
       n = numPoints - numel(xCoordinates);
   end
   
   xCoordinates(numPoints+1:end) = [];
   yCoordinates(numPoints+1:end) = [];
   

end</lang>

Output: <lang MATLAB>>> [x,y] = randomDisc(100); >> plot(x,y,'.')</lang>

Perl 6

<lang perl6>my @range = -15..16;

my @points = gather for @range X @range -> $x, $y {

   take [$x,$y] if 10 <= sqrt($x*$x+$y*$y) <= 15

} my @samples = @points.pick(100, :replace); # or .pick(100) to get distinct points

  1. format and print

my %matrix; for @range X @range -> $x, $y { %matrix{$y}{$x} = ' ' } %matrix{$_[1]}{$_[0]} = '*' for @samples; %matrix{$_}{@range}.join().say for @range;</lang>

PureBasic

<lang PureBasic>Procedure is_inrange(x,y,minlimit.f=10,maxlimit.f=15)

 Protected.f z=Sqr(x*x+y*y)
 If minlimit<=z And z <=maxlimit
   ProcedureReturn 1
 EndIf

EndProcedure

CreateImage(0,31,31) StartDrawing(ImageOutput(0)) For i=1 To 100

 Repeat
   x=Random(30)-15: y=Random(30)-15
 Until is_inrange(x,y)
 Plot(x+15,y+15,$0000FF)

Next StopDrawing()

Title$="PureBasic Plot" Flags=#PB_Window_SystemMenu OpenWindow(0,#PB_Ignore,#PB_Ignore,ImageWidth(0),ImageHeight(0),Title$,Flags) ImageGadget(0,0,0,30,30,ImageID(0)) Repeat: Event=WaitWindowEvent() Until Event=#PB_Event_CloseWindow</lang>

Python

Note that the diagram shows the number of points at any given position (up to a maximum of 9 points). <lang python>>>> from collections import defaultdict >>> from random import choice >>> world = defaultdict(int) >>> possiblepoints = [(x,y) for x in range(-15,16) for y in range(-15,16) if 10 <= abs(x+y*1j) <= 15] >>> for i in range(100): world[choice(possiblepoints)] += 1

>>> for x in range(-15,16): print(.join(str(min([9, world[(x,y)]])) if world[(x,y)] else ' ' for y in range(-15,16)))


            1     1           
         1 1                  
     11 1     1  1     1      
    111  1     1211           
     1   2    1 1    11       
     1  11         21         
    1   1            11  1    
  1  2                1 1     
                              
1  2                          
  1 1                      1  
  1 1                         
  2                      11   
 1                         1  
                        1     
                              
                              
 1                          1 
                        1     
                        2     
                           1  
    1                  1 1    
     1                2   1   
  1   3            11  2      
   11   1    1      1   2     
           1   1    2         
       1  1                   
        1      1     1        
         2 2   1              
              1               </lang>

If the number of samples is increased to 1100: <lang python>>>> for i in range(1000): world[choice(possiblepoints)] += 1

>>> for x in range(-15,16): print(.join(str(min([9, world[(x,y)]])) if world[(x,y)] else ' ' for y in range(-15,16)))


              2               
         41341421333          
       5133333131253 1        
     5231514 14214721 24      
    326 21222143234122322     
   54235153132123344125 22    
  32331432         2422 33    
  5453135           4144344   
 132595               323123  
 4 6353               432224  
5 4323                 3 5313 
23214                   41433 
42454                   33342 
332 4                   34314 
142 1                   35 53 

124211 53131

22221                   152 4 
22213                   34562 
654 4                   4 212 
24354                   52232 
544222                 283323 
 411123               453325  
 251321               124332  
  2124134           2443226   
  2 113315         64324334   
   2412452 324 32121132363    
     4222434324635 5433       
     3113333123432112633      
       2131181233  424        
         47414232164          
              4               </lang>

SystemVerilog

<lang SystemVerilog>program main;

 bit [39:0] bitmap [40];
 class Point;
   rand bit signed [4:0] x;
   rand bit signed [4:0] y;
   constraint on_circle_edge {
     (10*10) <= (x*x + y*y);
     (x*x + y*y) <= (15*15);
   };
   function void do_point();
     randomize;
     bitmap[x+20][y+20] = 1;
   endfunction
 endclass
 initial begin
   Point p = new;
   repeat (100) p.do_point;
   foreach (bitmap[row]) $display( "%b", bitmap[row]);
 end

endprogram</lang>

Piping the output through sed to improve the contrast of the output:

% vcs -sverilog -R circle.sv | sed 's/0/ /g'
                                        
                   1                    
                11 1  1                 
            1 1  1    11                
          1     1   1   11              
            1           1  1            
             1      1      1            
            1                           
       1   1                  1         
      1    1               1            
                             1          
       11                               
                                11      
         1                              
     11  1                     1 1      
         1                   1          
    1   1                    1   1      
     1                        1  1      
     11 1                       1       
                             11         
     1111                        1      
      1 111                  1          
       11 1                111  1       
       11                               
                          1  1          
            1            1   1          
                   1                    
             1  11                      
                          1             
                   1    1               
               11  1 1                  
                                        

Tcl

<lang tcl>package require Tcl 8.5

  1. Generate random point at specified distance from the centre

proc getPoint {range from to} {

   set r2 [expr {$range / 2}]
   set f2 [expr {$from ** 2}]
   set t2 [expr {$to ** 2}]
   while 1 {

set x [expr {int($range * rand())}] set y [expr {int($range * rand())}] set d2 [expr {($x-$r2)**2 + ($y-$r2)**2}] if {$d2 >= $f2 && $d2 <= $t2} { return [list $y $x] }

   }

}

  1. Make somewhere to store the counters

set ary [lrepeat 31 [lrepeat 31 0]]

  1. Generate 100 random points

for {set i 0} {$i < 100} {incr i} {

   set location [getPoint 31 10 15]
   # Increment the counter for the point
   lset ary $location [expr {1 + [lindex $ary $location]}]

}

  1. Simple renderer

foreach line $ary {

   foreach c $line {

puts -nonewline [expr {$c == 0 ? " " : $c > 9 ? "X" : $c}]

   }
   puts ""

}</lang> Example output:

               1               
           1  1                
                               
         1 1 1   2   1 1       
        11   1        1  1     
       11 1            1  1    
   1     1                     
   1    12               1     
     1 1               1       
  1 1                    1     
      1                    1   
   1                       1 1 
                          1  2 
                           1   
 1                         1   
 2   1                    2    
  2                         1  
                             1 
    1                    11    
     1                   1     
      1                        
  1                       1    
     2                 1    1  
   1                     1     
   1 1   1          11     1   
     2  1  1        11         
        11      1      1 1     
      1 2   1       11         
         121   1  1            
           1  1   1            
               1