Hi George,
By the way, you seem to need weighted randomization func, and I have one of it, it's very easy to weight each value.
I wrote this code by my self though I forgot where I found this underlying argorythm, I made it in 2017.
This function doesn't require the sum of probability values to be 100 or 1.0 !
It is very intuitive to use, especially, it's very conveinient and prctical in live coding music.
(define w-rnd
(lambda (lis)
(let ((sum-w (apply + (map (lambda (x) (cadr x))
lis))))
(let loop ((a-lis lis)
(rd (random sum-w))) ; rd is from 0 to (sum-w - 1), this is point !
(if (< rd (cadar a-lis)) ; (cadar a-lis) is weight number of target
(caar a-lis) ; (caar a-lis) is target that has above weighted number
(loop (cdr a-lis) (- rd (cadar a-lis)))))))) ; random number minus weighted number
(w-rnd '((a 5)(b 1) )) ; -> a, a, b, a, a, a, a, b, ....
;; test function for doing w-rnd many times
(define my-test
(lambda (arg1 n-times)
(let ((ans '()))
(dotimes (i n-times)
(set! ans (cons (eval arg1) ans)))
(cl:sort (my-count ans '() '())
(lambda (x y) (string<? (atom->string (car x)) (atom->string (car y))))))) )
;; my-test calls my-count
(define my-count
(lambda (lis anslis sub)
(cond ((null? lis) anslis)
((set! sub (assoc (car lis) anslis))
(my-count (cdr lis)
(cons (list (car sub) (+ (cadr sub) 1))
(remove sub anslis))
'()))
(else (my-count (cdr lis)
(cons (list (car lis) 1) anslis)
'())))))
(my-count '(a a b a b c) '() '())
;; -> ((c 1) (b 2) (a 3))
;; w-rnd tests
(my-test '(w-rnd '((a 10) (b 100) (c 2) (d 1) )) 1000) ;1000times, use 5000 if you want.
;; ((a 76) (b 906) (c 10) (d 8)), ((a 80) (b 890) (c 20) (d 10)), ....
(my-test '(w-rnd '((a 10) (b 20) (c 70) )) 1000)
;; ((a 103) (b 198) (c 699)), ((a 105) (b 197) (c 698))
;; random tests
(my-test '(random '(.1 . a) '(.2 . b) '(.7 . c)) 1000)
;; ((a 101) (b 204) (c 695)), ((a 106) (b 172) (c 722)) ....
(my-test '(w-rnd '((a 10) (b 20) (c 70) (d 20))) 1000)
;; ((a 85) (b 188) (c 556) (d 171)), ((a 96) (b 165) (c 582) (d 157)) ...
(my-test '(random '(.1 . a) '(.2 . b) '(.7 . c) '(.2 . d)) 1000)
;; ((a 107) (b 205) (c 688)), ((a 111) (b 200) (c 689))
;; there is no 'd', maybe due to over 1.0
(my-test '(random '(.6 . a) '(.4 . b) '(.7 . c) '(.2 . d)) 1000)
;; ((a 602) (b 398))
;; there is no 'c' nor 'd', maybe due to over 1.0
(my-test '(w-rnd '((a 6) (b 4) (c 7) (d 2))) 1000)
;; ((a 294) (b 222) (c 364) (d 120))
(my-test '(random '(.1 . a) '(.2 . b) '(.3 . c) ) 1000)
;; error
(random '(.1 . a) '(.2 . b) '(.3 . c) )
;; c, b, a or error, maybe due to under 1.0
(my-test '(w-rnd '((a 1) (b 2) (c 3) )) 1000)
;; ((a 148) (b 351) (c 501)), ((a 170) (b 333) (c 497))
;; random calls weighted-selection, so do it, too ...
(my-test '(weighted-selection '(.1 . a) '(.2 . b) '(.7 . c)) 1000)
;; ((a 110) (b 192) (c 698))
(my-test '(weighted-selection '(.1 . a) '(.2 . b) '(.3 . c)) 1000)
;; error
(my-test '(weighted-selection '(.1 . a) '(.2 . b) '(.3 . c)) 1)
;; ((c 1)), ((a 1)), ((b 1)) or error
(weighted-selection '(.1 . a) '(.2 . b) '(.3 . c))
;; c, a, b or error
(my-test '(weighted-selection '(.6 . a) '(.5 . b) '(.7 . c) '(.2 . d)) 1000)
;; ((a 592) (b 408)), ((a 595) (b 405)), ((a 620) (b 380))
;; looks like 6:4 of probability, not 6:5, maybe due to over 1.0 ....
;; no c nor d
(my-test '(w-rnd '((a 6) (b 5) (c 7) (d 2))) 1000)
;; ((a 266) (b 260) (c 374) (d 100)), ((a 295) (b 248) (c 362) (d 95))
I think this w-rnd is good enough for my purposes ...
Please feel free to use this w-rnd if you like.
2026年6月30日火曜日 11:37:03 UTC+9 Minoru: