Constrained random points on a circle: Difference between revisions
(Added Ruby version.) |
|||
Line 872: | Line 872: | ||
47414232164 |
47414232164 |
||
4 </lang> |
4 </lang> |
||
=={{header|Ruby}}== |
|||
<lang Ruby>points = (1...100).map { |
|||
# choose a random radius and angle |
|||
angle = rand * 2.0 * Math::PI |
|||
rad = rand * 5.0 + 10.0 |
|||
# convert back from polar to cartesian coordinates |
|||
[rad * Math::sin(angle), rad * Math::cos(angle)].map(&:round) |
|||
} |
|||
(-15...15).each do |row| |
|||
puts((-15...15).map { |col| points.include?([row, col]) ? "X" : " " }.join) |
|||
end</lang> |
|||
=={{header|SystemVerilog}}== |
=={{header|SystemVerilog}}== |
Revision as of 05:07, 6 December 2010
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.
ALGOL 68
- note: This specimen retains the original C coding style.
<lang algol68>PROC clrscr = VOID:
printf(($g"[2J"$,REPR 27)); # ansi.sys #
PROC gotoxy = (INT x,y)VOID:
printf(($g"["g(0)";"g(0)"H"$,REPR 27, y,x)); # ansi.sys #
MODE POINT = STRUCT(
INT x,y
);
INT radius = 15; INT inside radius = 10;
POINT center = (radius+1, radius+1);
FLEX[0]POINT set;
PROC swap with last set = (INT position,INT where last set)VOID: (
INT temp := x OF set[position]; x OF set[position]:=x OF set[where last set]; x OF set[where last set] := temp;
temp := y OF set[position]; y OF set[position]:=y OF set[where last set]; y OF set[where last set] := temp
);
PROC create set = VOID: (
set := HEAP[(2*radius+1)**2]POINT; INT x,y,i:=LWB set;
FOR x FROM -radius TO radius DO FOR y FROM -radius TO radius DO IF sqrt(x*x+y*y)>=inside radius AND sqrt(x*x+y*y)<=radius THEN x OF set[i] := x; y OF set[i] := y; i+:=1 FI OD OD;
set:=set[:i-1]
);
PROC plot fuzzy set = (CHAR ch)VOID: (
INT pos,i;
TO UPB set DO pos := ENTIER(random * UPB set) + 1;
gotoxy(x OF center + x OF set[pos],y OF center + y OF set[pos]);
print(ch);
swap with last set(pos,UPB set)
OD
);
main: (
# srand((INT)time(NIL)); #
clrscr; create set; plot fuzzy set("*"); gotoxy(2*radius+1, 2*radius+1); newline(stand in)
)</lang> Sample output:
* * ** * * * * ** ** ***** ** ***** ** ** ** * * * * *** * ******** *** *** * ** ***** ** * *** * ***** * ** ** **** * * * * **** **** * **** * ** *** * ** ** ** ** * * * **** ** * ** * **** **** ** * * ** * ** * ** * * * *** * * ****** * * ** * * ** **** * ** * *** * **** ** * ** ** *** * *** * * *** * ** *** *** * * ** ***** **** ** ******* * * * ** ** ******* * ****** * *
C
The general scheme is :
- 1. All points satisfying the inequality are enumerated.
- 2. 100 points are chosen at random from this set and plotted.
- 3. To ensure that the same point is not picked twice and hence speed up the plotting, the chosen point is swapped with the last point after plotting. The new point is then chosen from the new set minus the last element. The position of this last element moves towards the head as the loop progresses.
<lang C>
- include<stdlib.h>
- include<stdio.h>
- include<conio.h>
- include<math.h>
typedef struct{ int x,y; }points;
points set[500]; int last;
void swapWithLast(int position,int whereLast) { int temp = set[position].x; set[position].x=set[whereLast].x; set[whereLast].x = temp;
temp = set[position].y; set[position].y=set[whereLast].y; set[whereLast].y = temp; }
void createSet() { int x,y,i=0;
for(x=-15;x<=15;x++) { for(y=-15;y<=15;y++) { if(sqrt(x*x+y*y)>=10.0 && sqrt(x*x+y*y)<=15.0) { set[i].x = x; set[i].y = y; i++; } } }
last = i; }
void plotFuzzySet(char ch) { int pos,i;
for(i=0;i<100;i++) { pos = rand()%last;
gotoxy(40 + set[pos].x,30 + set[pos].y);
printf("%c",ch);
swapWithLast(pos,last-1);
last--; } }
int main() { srand((unsigned int)time(NULL));
clrscr(); createSet(); plotFuzzySet('*');
getch(); return 0; } </lang>
Output will differ from system to system. A better rendering would be to use graphics libraries like OpenGL or Turbo BGI.
* * * * ** * * *** ** * ** * ** * ** * * * * * * * ** * ** * * * * ** * ** * * * ** * * ** * ** * * * * * ** * * * * * * * ** * * * * * *** * * * ** * * * ** * * * * * * ** * **
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
Fortran
<lang fortran>program Constrained_Points
implicit none integer, parameter :: samples = 100 integer :: i, j, n, randpoint real :: r type points integer :: x, y end type
type(points) :: set(500), temp
! Create set of valid points
n = 0 do i = -15, 15 do j = -15, 15 if(sqrt(real(i*i + j*j)) >= 10.0 .and. sqrt(real(i*i + j*j)) <= 15.0) then n = n + 1 set(n)%x = i set(n)%y = j end if end do end do
! create 100 random points ! Choose a random number between 1 and n inclusive and swap point at this index with point at index 1 ! Choose a random number between 2 and n inclusive and swap point at this index with point at index 2 ! Continue in this fashion until 100 points have been selected
call random_seed do i = 1, samples call random_number(r) randpoint = r * (n + 1 - i) + i temp = set(i) set(i) = set(randpoint) set(randpoint) = temp end do
! In order to facilitate printing sort random points into ascending order ! sort x in ascending order
do i = 2, samples j = i - 1 temp = set(i) do while (j>=1 .and. set(j)%x > temp%x) set(j+1) = set(j) j = j - 1 end do set(j+1) = temp end do
! sort y in ascending order for same x
do i = 2, samples j = i - 1 temp = set(i) do while (j>=1 .and. set(j)%x == temp%x .and. set(j)%y > temp%y) set(j+1) = set(j) j = j - 1 end do set(j+1) = temp end do
! print circle
write(*,"(a,a)", advance="no") repeat(" ", set(1)%y+15), "*" do i = 2, samples if(set(i)%x == set(i-1)%x) then write(*,"(a,a)", advance="no") repeat(" ", set(i)%y - set(i-1)%y-1), "*" else n = set(i)%x - set(i-1)%x do j = 1, n write(*,*) end do write(*,"(a,a)", advance="no") repeat(" ", set(i)%y+15), "*" end if end do
end program</lang> Output
* * * * * ** ** * ** * ** * ** * ** * *** ** * * * * * * * * ** * * * * * * * * * ** *** * * * ** * * * * * * * * ** * * * * * ** ** * * * * *** * * * ** * * * * * * * * * ** * *
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 distinct 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>
Liberty BASIC
<lang lb>' RC Constrained Random Points on a Circle
nomainwin
WindowWidth =400 WindowHeight =430
open "Constrained Random Points on a Circle" for graphics_nsb as #w
- w "trapclose [quit]"
- w "down ; size 7 ; color red ; fill black"
for i =1 to 1000
do x =int( 30 *rnd( 1)) -15 y =int( 30 *rnd( 1)) -15 loop until IsInRange( x, y) =1 #w "set "; 200 +10 *x; " "; 200 - 10 *y
next
wait
function IsInRange( x, y)
z =sqr( x*x +y*y) if 10 <=z and z <=15 then IsInRange =1 else IsInRange =0
end function
[quit] close #w end</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>
OCaml
<lang ocaml>let p x y =
let d = sqrt(x ** 2.0 +. y ** 2.0) in 10.0 <= d && d <= 15.0
let () =
Random.self_init(); let rec aux i acc = if i >= 100 then acc else let x = (Random.float 40.0) -. 20.0 and y = (Random.float 40.0) -. 20.0 in if (p x y) then aux (succ i) ((x,y)::acc) else aux i acc in let points = aux 0 [] in let g = Array.init 40 (fun _ -> String.make 40 ' ') in List.iter (fun (x,y) -> let x = (int_of_float x) + 20 and y = (int_of_float y) + 20 in g.(y).[x] <- 'o' ) points; Array.iter print_endline g</lang>
o o o o oo oo oooo o o oo o oo o o o o oo o oo o oo o oo oo o o oooo o o oo o o o oo o o o o o o o o o o o o o o o oo o oo o o ooo o o o o ooo o o oo o
PARI/GP
<lang>crpc()={
my(v=vector(404),t=0,i=0,vx=vy=vector(100)); for(x=1,14,for(y=1,14, t=x^2+y^2; if(t>99&t<226, v[i++]=[x,y]; v[i++]=[x,-y]; v[i++]=[-x,y]; v[i++]=[-x,-y] ) )); for(x=10,15, v[i++]=[x,0]; v[i++]=[-x,0]; v[i++]=[0,x]; v[i++]=[0,-x] ); for(i=1,#vx, t=v[random(#v)+1]; vx[i]=t[1]; vy[i]=t[2]; ); plothraw(vx,vy)
};</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.roll(100); # or .pick(100) to get distinct points
- 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>
Turning that program completely inside-out and reducing to a single statement with a single non-parameter variable, we get this version, which also works:
<lang perl6>(say ~.map: { $_ // ' ' } for my @matrix) given do
-> [$x, $y] { @matrix[$x][$y] = '*' } for pick 100, do for ^32 X ^32 -> $x, $y { [$x,$y] when 100..225 given [+] ($x,$y X- 15) X** 2; }
</lang>
This uses, among other things, a 0-based matrix rather than a hash, a given on the first line that allows us to print the final value of the matrix straight from its initial declaration, a for statement feeding a for statement modifier, a lambda that unpacks a single x-y argument into two variables, the functional form of pick rather than the method form, a quasi-list comprehension in the middle loop that filters each given with a when, precalculated squared limits so we don't have to take the square root, use of X- and X** to subtract and exponentiate both $x and $y in parallel.
After the given do has loaded up @matrix with our circle, the map on the first line substitutes a space for any undefined matrix element, and the extra space between elements is supplied by the stringification of the list value, performed by the prefix ~ operator, the unary equivalent of concatenation in Perl 6.
At this point you would be justified in concluding that we are completely mad. :-)
PicoLisp
<lang PicoLisp>(let Area (make (do 31 (link (need 31 NIL " "))))
(use (X Y) (do 100 (until (>= 15 (sqrt (+ (* (setq X (rand -15 15)) X) (* (setq Y (rand -15 15)) Y) ) ) 10 ) ) (set (nth Area (+ 16 X) (+ 16 Y)) "#") ) ) (mapc prinl Area) )</lang>
Output:
# ## # # # # ## # # # # # # # # # # # # # # # # # # # # # # # # ## # # # # # ### # # # # ## # # # # # # ## # # # # # # ### # # ### # # # # # # # ## # # # # # # # #
Prolog
Works with SWI-Prolog <lang Prolog>:- use_module(library(clpfd)).
circle :- bagof([X,Y], init(X,Y), BL), length(BL, N), length(L, 100), maplist(choose(BL, N), L), draw_circle(L).
% point selection
choose(BL, N, V) :-
I is random(N),
nth0(I, BL, V).
% to find all couples of numbers verifying % 100 <= x^2 + y^2 <= 225 init(X1, Y1) :- X in -15..15, Y in -15..15, X*X + Y*Y #>= 100, X*X + Y*Y #=< 225, label([X,Y]), X1 is 10 * X + 200, Y1 is 10 * Y + 200.
draw_circle(L) :-
new(D, window('Circle')),
send(D, size,size(400,400)),
forall(member([X,Y], L),
( new(C, circle(4)),
send(C, fill_pattern, colour(@default, 0, 0, 0)),
send(C, center(point(X,Y))),
send(D, display, C))),
send(D, open).
</lang>
PureBasic
<lang PureBasic>CreateImage(0,31,31) StartDrawing(ImageOutput(0))
For i=1 To 100 Repeat x=Random(30)-15 y=Random(30)-15 R.f=Sqr(x*x+y*y) Until 10<=R And R<=15 Plot(x+15,y+15,#Red) 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,ImageWidth(0),ImageHeight(0),ImageID(0)) Repeat: Until WaitWindowEvent()=#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>
Ruby
<lang Ruby>points = (1...100).map {
# choose a random radius and angle angle = rand * 2.0 * Math::PI rad = rand * 5.0 + 10.0 # convert back from polar to cartesian coordinates [rad * Math::sin(angle), rad * Math::cos(angle)].map(&:round) }
(-15...15).each do |row|
puts((-15...15).map { |col| points.include?([row, col]) ? "X" : " " }.join)
end</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
- 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] }
}
}
- Make somewhere to store the counters
set ary [lrepeat 31 [lrepeat 31 0]]
- 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]}]
}
- 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