表示人的最自然的数据结构是循环缓冲区。我的解决方案创建一个链接列表,将列表的尾部绑回头部,然后围绕缓冲区重复计数到下一个要执行的人,从缓冲区中删除该人,并继续直到缓冲区的尾部指向自身。
(define (cycle xs)
(set-cdr! (last-pair xs) xs) xs)
(define (josephus n m)
(let loop ((k (- m 1)) (alive (cycle (range 0 n))) (dead '()))
(cond ((= (car alive) (cadr alive))
(reverse (cons (car alive) dead)))
((= k 1)
(let ((dead (cons (cadr alive) dead)))
(set-cdr! alive (cddr alive))
(loop (- m 1) (cdr alive) dead)))
例如:
> (josephus 41 3)
(2 5 8 11 14 17 20 23 26 29 32 35 38 0 4 9 13 18 22 27 31 36
40 6 12 19 25 33 39 7 16 28 37 10 24 1 21 3 34 15 30)
您可以在我的博客上阅读更全面的解释,其中给出了三种不同的解决方案。或者,您可以在 http://programmingpraxis.codepad.org/RMwrace2 运行该程序。