문제

Right now I"m working on a program that should be able to pick 3 people out of a list of 7 ( a b c d e f g) and assign them to be criminals. This "game" then picks 3 random peolpe out of the 7, tells you how many of those people are criminals and asks if you want to guess who the three criminals are, having one guess ( "two of these three are crimnals would you like to guess who the criminals are). However, I currently have a program that pulls 3 random criminals from the list, however the struggle I"m having is initially assigning who's a criminal or not ( randomly picking 3 out of a list and assigning them to values that can be recalled later ) and then being able to print that back out. This is my code so far and I was hoping somebody could point me in the right direction, I'm still very new to functional programming as a whole.

;allows us to use prompt to ask the user for input
(defun prompt-read (prompt)
  (format *query-io* "~a: " prompt)
  (force-output *query-io*)
  (read-line *query-io*))

;allows you to add elements in needed spots
(defun element-at (org-list pos &optional (ini 1))
  (if (eql ini pos)
      (car org-list)
      (element-at (cdr org-list) pos (+ ini 1))))

(defun element-at (lista n)
  (if (= n 1)
      (first lista)
      (element-at (rest lista) (1- n))))

;allows for the removal of unneeded elements 
(defun remove-at (org-list pos &optional (ini 1))
  (if (eql pos ini)
      (cdr org-list)
      (cons (car org-list) (remove-at (cdr org-list) pos (+ ini 1)))))

;returns a chosen  number of random elements from a list
(defun rnd-select (org-list num &optional (selected 0))
  (if (eql num selected)
      nil
      (let ((rand-pos (+ (random (length org-list)) 1)))
        (cons (element-at org-list rand-pos) (rnd-select (remove-at org-list rand-pos) num (+ selected 1))))))

;returns 3 random criminals from a list of 7
(defun rnd-criminals ()
  (rnd-select '(a b c d e f g) 3))

(defun game ()
  (prompt-for-players))

;allows for the storing of number of players
(defun num-of-players(number)
  (list :number number))

;prompts for the amount of players you want to play
(defun prompt-for-players ()
  (num-of-players
   (or (parse-integer (prompt-read "How many players are there?"
                                   :junk-allowed t) 0))))
도움이 되었습니까?

해결책

This is a sampling without replacement problem (since, I'd assume, you wouldn't want to "pick three criminals" by picking the same person from the list each time). There are lots of ways to do this. One way is to generate indices until you've got enough distinct ones. How about something like this:

(defun pick (sequence n)
  "Return n elements chosen at random from the sequence."
  (do ((len (length sequence))  ; the length of the sequence
       (indices '())            ; the indices that have been used
       (elements '()))          ; the elements that have been selected
      ((zerop n)          ; when there are no more elements to select,
       elements)          ; return the elements that were selectd.
    (let ((i (random len)))       ; choose an index at random
      (unless (member i indices)  ; unless it's been used already
        (push i indices)          ; add it to the list of used indices
        (push (elt sequence i) elements) ; and grab the element at the index
        (decf n)))))                     ; and decrement n.

If you're not so familiar with do, you could use a recursive approach, e.g., with a local recursive function:

(defun pick2 (sequence n &aux (len (length sequence)))
  (labels ((pick2 (indices elements n)
             (if (zerop n) ; if no more elements are needed, 
                 elements ; then return elements.
                 (let ((i (random len))) ; Otherwise, pick an index i.
                   ;; If it's been used before,
                   (if (member i indices) 
                       ;; then continue on with the same indices,
                       ;; elements, and n.
                       (pick2 indices elements n)
                       ;; else, continue with it in the list of
                       ;; indices, add the new element to the list of
                       ;; elements, and select one fewer elements
                       ;; (i.e., decrease n).
                       (pick2 (list* i indices)
                              (list* (elt sequence i) elements)
                              (1- n)))))))
    ;; Start the process off with no indices, no elements, and n.
    (pick2 '() '() n)))

Another approach would one based on Efficiently selecting a set of random elements from a linked list which suggests Reservoir Sampling.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top