约瑟夫斯序列
Josephus sequence
描述:有人站成一圈等待处决。计数从圆中的某个点开始,并沿固定方向绕圆进行。在每一步中,都会跳过一定数量的人,然后执行下一个人。清除工作围绕着这个圆圈进行(随着被处决的人被清除,这个圆圈越来越小(,直到只剩下最后一个人,他得到了自由。
我在谷歌上搜索了这个"约瑟夫斯问题",维基百科上的热门文章给了我一个动态编程解决方案:f(n,k)=((f(n-1,k)+k-1) mod n)+1, with f(1,k)=1
,但这只会产生最后一个幸存者。我怎样才能得到被处决的人的顺序?假设p(5,3(={3,1,5,2,4}。
此外,是否存在O(nlogn)
解决方案而不是O(nk)
解决方案?
要获得被处决者和最后幸存者的序列,只需要从一开始就模拟整个过程。考虑到程序的描述,这将是一项相当容易的任务。你们提出的公式是检查谁能幸存下来并快速获得答案的唯一捷径。
关于如何在O(n log n(中使用范围树进行此操作的说明如下:http://pl.scribd.com/doc/3567390/Rank-Trees
更详细的分析可以在这里找到:http://www.imt.ro/romjist/Volum12/Number12_1/pdf/02-MCosulschi.pdf
表示人员的最自然的数据结构是循环缓冲区。我的解决方案创建了一个链表,将列表的尾部绑回到头部,然后在缓冲区周围重复计数,直到下一个要执行的人,从缓冲区中删除那个人,然后继续,直到缓冲区的尾部指向它自己。
(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.
人员将存储在大小为n的数组中。如果索引i
处的人员现在被执行,则下一个人员将由(i+k)%m
给出,其中m是剩余人数。在每次迭代之后,数组大小将减小1,并且剩余元素将相应地移位。
输入:人员[0..n-1],n,k,i(=第一个执行人的索引(
伪代码类似于:
Print People[i]
While (n > 1)
do
n = n - 1
for j = i to n-1
People[j] = People[j+1]
i = (i+k) % n
print People[i]
done
要刺激程序,您可以使用一个包含玩家名称的结构和一个标记,如果玩家处于活动状态或未处于活动状态,该标记将保持跟踪。每次在新一轮中,你都会跳过特定数量的玩家,所以使用循环和条件语句,这样所有退出游戏的玩家都会被忽略,只计算游戏中的玩家。当然,还可以添加printf语句来打印当前状态。
要回答输出执行序列的问题,需要进行模拟。这意味着O(nk(复杂性。在寻找O(nlogn(时间复杂度的同时,不可能得到执行序列[即O(n(]。因为你必须输出每个要执行的人,这就是O(n(。
kkonrad对Range Trees的引用产生了一个很好的O(nlogn(解决方案。正如其他人所指出的,循环链表是解决这个问题的有效数据结构。我发现CodeReview中的200_success Java解决方案非常优雅且可读。
public class CircularGunmenIterator<T> implements Iterator<T> {
private List<T> list;
private Iterator<T> iter;
public CircularGunmenIterator(List<T> list) {
this.list = list;
this.iter = list.iterator();
}
@Override
public boolean hasNext() {
// Continue as long as there is a shooter and a victim
return this.list.size() >= 2;
}
@Override
public T next() {
if (!this.iter.hasNext()) {
// Wrap around, creating the illusion of a circular buffer
this.iter = this.list.iterator();
}
return this.iter.next();
}
@Override
public void remove() {
this.iter.remove();
}
public static void main(String[] args) {
// Create the gunmen
List<Integer> gunmen = new LinkedList<Integer>();
for (int i = 1; i <= 100; i++) {
gunmen.add(i);
}
// Shootout!
Iterator<Integer> ringIter = new CircularGunmenIterator<Integer>(gunmen);
while (ringIter.hasNext()) {
Integer shooter = ringIter.next();
Integer victim = ringIter.next();
System.out.printf("%2d shoots %2dn", shooter, victim);
ringIter.remove(); // Bang!
}
System.out.println("Last one alive: " + gunmen.get(0));
}
}
维基百科上有关于这个约瑟夫斯问题(k=2(的更多细节。
http://en.wikipedia.org/wiki/Josephus_problem
- 为什么指标有时效果很好,有时效果不佳?写下霍夫曼代码
- 斯堪夫不接受输入
- C++ 获取"控件可能会到达约翰逊-特罗特代码上的非空函数的末尾
- MPI 归约操作中的求和顺序
- 大通列夫德克的原子存储
- 卡夫卡消费者投票最新消息
- 问题 (std::bad_alloc) 通过 QThread 中的 QSqlQuery 将大图像(约 36 MB)保存到
- C++ 犰狳和OpenMp:外积求和的并行化 - 定义犰狳矩阵的约简
- 如何存储霍夫曼转换后的二进制代码?
- 我如何将大量输入长度约为100000位
- 霍夫曼压缩机/解压缩器
- 关于广义霍夫变换代码
- 霍夫曼编码 c++
- Opencl ClgetPlatFormids给出了约230个Valgrind Memcheck错误
- 阿坦夫给了我错误的答案
- 霍夫曼解码功能反复反复压缩一个字符
- 霍夫曼解码压缩文件
- 实现霍夫曼树
- 正在处理约瑟夫斯问题,节点函数不起作用
- 约瑟夫斯序列