List of random numbers in range / Loops

List of random numbers in range / Loops

surfer96
Advocate Advocate
2,121 Views
9 Replies
Message 1 of 10

List of random numbers in range / Loops

surfer96
Advocate
Advocate

All members of a list of random numbers (varying from 0 to 10) should be within a range from 4 to 6.

So the smallest number should be larger than 4, the largest number smaller than 6.

 

What's wrong with the code above?

 

(vl-load-com)
(defun c:randlist_range_while (/ NoL bot_rg top_rg rd_nr rdnr_lst a b time_start time_end time_el)

(setq time_start (getvar "MILLISECS")); set start time

(defun LM:rand ( / a c m )
    (setq m   4294967296.0
          a   1664525.0
          c   1013904223.0
          $xn (rem (+ c (* a (cond ($xn) ((getvar 'date))))) m)
    )
    (/ $xn m)
)

(setq bot_rg 4); bottom range
(setq top_rg 6); top range
(setq rdnr_lst '(0 10)); start list for random numbers
(setq a (car rdnr_lst))
(setq b (last rdnr_lst))
(setq n 5); number of list elements

(while (or (< a bot_rg) (> b top_rg))

(setq NoL (+ NoL 1)); loop counter

(repeat n
  (setq rd_nr (* (LM:rand) 10)); random number
  (setq rdnr_lst (cons rd_nr rdnr_lst)); add random number to list
)  

(setq rdnr_lst (vl-sort rdnr_lst '<))
(setq a (car rdnr_lst))
(setq b (last rdnr_lst))

); end while

(setq time_end (getvar "MILLISECS")); set end time
(setq time_el (- time_end time_start)); time elapsed

(princ (strcat "\nNumber of Loop Executions: " (rtos NoL 2 0)))
(princ (strcat "\nTime Elapsed in Millisecs: " (rtos time_el 2 0)))

(setq temp (vl-princ-to-string rdnr_lst))
(princ (strcat "\nEntire List: " temp))
(princ (strcat "\nSmallest in List: " (rtos a 2 2)))
(princ (strcat "\nLargest in List: " (rtos b 2 2)))
(princ)
); end defun
0 Likes
Accepted solutions (1)
2,122 Views
9 Replies
Replies (9)
Message 2 of 10

surfer96
Advocate
Advocate

Just saw that the variable "a" is defined twice:

(setq m 4294967296.0
a 1664525.0 ; in random function

(setq a (car rdnr_lst)); in list

Changed its name from "a" to "a1" but it still doesn't work...

0 Likes
Message 3 of 10

ronjonp
Mentor
Mentor

You're using Lee's function incorrectly if you want a random number within a range.

 

Use the the lm:randrange function in conjunction with lm:rand

(defun LM:randrange ( a b )
    (+ (min a b) (fix (* (LM:rand) (1+ (abs (- a b))))))
)
;; Usage
(LM:randrange 4 6)

 

 

0 Likes
Message 4 of 10

surfer96
Advocate
Advocate

I know  Lee's (LM:randrange) but that's not what solves the issue.

 

My starting random number list derives from another process and will always contain elements outside the range.

I need a while loop as a second random operation so all the members of the list will be within the range after looping.

The number of elements in the list must not change.

 

There's probably an error in my loop but I can't find it...

0 Likes
Message 5 of 10

ronjonp
Mentor
Mentor
Accepted solution

Give this a try if I'm understanding you correctly. Comments in code.

(vl-load-com)
(defun c:randlist_range_while (/ nol bot_rg top_rg rd_nr rdnr_lst a b time_start time_end time_el)
  (setq time_start (getvar "MILLISECS")) ; set start time
  (defun lm:rand (/ a c m)
    (setq m   4294967296.0
	  a   1664525.0
	  c   1013904223.0
	  $xn (rem (+ c
		      (* a
			 (cond ($xn)
			       ((getvar 'date))
			 )
		      )
		   )
		   m
	      )
    )
    (/ $xn m)
  )
  (setq bot_rg 4)			; bottom range
  (setq top_rg 6)			; top range
  (setq rdnr_lst '(0 10))		; start list for random numbers
  (setq a (car rdnr_lst))
  (setq b (last rdnr_lst))
  (setq n 5)
  ;; RJP » added 'nol' value for counter otherwise code will bomb below
  (setq nol 0)				; number of list elements
  (while (or (< a bot_rg) (> b top_rg))
    ;; RJP » reset 'rdnr_lst' before repeat.
    (setq rdnr_lst nil)
    (setq nol (+ nol 1))		; loop counter
    (repeat n
      (setq rd_nr (* (lm:rand) 10))	; random number
      (setq rdnr_lst (cons rd_nr rdnr_lst)) ; add random number to list
    )
    (setq rdnr_lst (vl-sort rdnr_lst '<))
    (setq a (car rdnr_lst))
    (setq b (last rdnr_lst))
  )					; end while
  (setq time_end (getvar "MILLISECS"))	; set end time
  (setq time_el (- time_end time_start)) ; time elapsed
  (princ (strcat "\nNumber of Loop Executions: " (rtos nol 2 0)))
  (princ (strcat "\nTime Elapsed in Millisecs: " (rtos time_el 2 0)))
  (setq temp (vl-princ-to-string rdnr_lst))
  (princ (strcat "\nEntire List: " temp))
  (princ (strcat "\nSmallest in List: " (rtos a 2 2)))
  (princ (strcat "\nLargest in List: " (rtos b 2 2)))
  (princ)
)					; end defun

 Command: RANDLIST_RANGE_WHILE
Number of Loop Executions: 4952
Time Elapsed in Millisecs: 266
Entire List: (4.13191 4.74185 4.7738 4.95089 5.1682)
Smallest in List: 4.13
Largest in List: 5.17

 

What is the purpose of this?

Message 6 of 10

Kent1Cooper
Consultant
Consultant

@surfer96 wrote:

All members of a list of random numbers (varying from 0 to 10) should be within a range from 4 to 6.

So the smallest number should be larger than 4, the largest number smaller than 6.

....


 

Without analyzing too closely, it looks like what that does is to get some random numbers between 0 and 10, and check whether they're all between 4 and 6, and if not [which would very often be the case, just as a matter of probability], try repeatedly to get a set of numbers that fit within the range, until it eventually does, however long that may take.  If that's what's happening, it seems a waste of effort, since this can certainly be done without  a however-long try-try-again approach.

 

Subtract the smallest allowable number from the largest allowable number for the range [in this case, 2].  Have a random number generated between zero and 1  [lots of routines around to do that].  Multiply that by the range, then add that result to the smallest allowable number.

 

You need to do that only as many times as you want numbers in your resulting list, and never any more -- all will be guaranteed to be within the range.

 

[Also, I don't understand the point of the 0-to-10 overall range when the 4-6 target range "says it all."]

Kent Cooper, AIA
Message 7 of 10

surfer96
Advocate
Advocate

It works!

 

 

I had found out about the missing loop counter definition in the meantime.

But I think the main error was not resetting the list within the loop before repeat, so the loop would add 5 more elements to the list for every new round making it 5+5+5+... elements. Is that right?

 

The purpose is to calculate the areas of randomly designed polylines or regions. By forcing them to be within a range of e. g. 4 to 6 m² you gain some control over the outcome. I' ll go on with it...

 

Thanks a lot so far.

0 Likes
Message 8 of 10

ronjonp
Mentor
Mentor

@surfer96 wrote:

...

more elements to the list for every new round making it 5+5+5+... elements. Is that right?

 

The purpose is to calculate the areas of randomly designed polylines or regions. By forcing them to be within a range of e. g. 4 to 6 m² you gain some control over the outcome. I' ll go on with it...

 

Thanks a lot so far.


You are correct it kept adding more items to the list. Even worse, if the first iteration failed you'd get stuck in an endless loop.

 

And you're welcome (y).

Message 9 of 10

surfer96
Advocate
Advocate

The violation (0 to 10) of the  condition (4 to 6) will always have occured first so I can't use an alternative solution "instead". I will always need a second loop to repair the violation. @ronjonp found the error in the while loop.

0 Likes
Message 10 of 10

ronjonp
Mentor
Mentor

As Kent pointed out, you could also get a fixed list of numbers to choose from within a range like so:

(defun c:foo (/ a mn mx)
  (defun lm:rand (/ a c m)
    (setq m   4294967296.0
	  a   1664525.0
	  c   1013904223.0
	  $xn (rem (+ c
		      (* a
			 (cond ($xn)
			       ((getvar 'date))
			 )
		      )
		   )
		   m
	      )
    )
    (/ $xn m)
  )
  (setq	mn 4
	mx 6
  )
  (repeat 100 (setq a (cons (+ mn (* (- mx mn) (lm:rand))) a)))
  (mapcar 'print (vl-sort a '<))
  (princ)
)

 

 

0 Likes