8行代码解决约瑟夫问题
来源:互联网 发布:网络app销售彩票合法吗 编辑:程序博客网 时间:2024/05/22 01:30
首先是问题描述:约瑟夫斯问题(有时也称为约瑟夫斯置换),是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。有个囚犯站成一个圆圈,准备处决。首先从一个人开始,越过个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决? --摘自维基百科这么说可能比较抽象,我们举个例子:当n=5,k=3时,我们假设这5个人的序号分别为1、2、3、4、5。那么依次被杀掉的人是3、1、5、2,最后活下来的是4。 接下来我们分析一下问题:我们先来做些准备工作:1. 我们把这个问题记为 f 。 f(n, k) 为问题的解。其中参数 n 和 k 的含义与问题描述中的一致。n 个人的序号分别为1、2、3...n。我们会得到最后剩下那个人的序号。2. 因为这个问题操作起来是环形的,所以我们可以这么认为: 序号 0 和序号 n 是一样的、序号 -1 和序号 (n-1) 是一样的、 序号 -2 和序号 (n-2) 是一样的... 同样,序号 (n+1) 和序号 1 是一样的、 序号 (n+2) 和序号 2 是一样的... 也就是说,对 n 求模相等的数指向的是同一个人。但在给出最终结果时我们会进行处理,将结果映射在整数区间[1, n]内 。3. 对于给定的 n 和 k,第一个被杀掉的人是 k 。(k 有可能大于 n,但根据“准备工作2”,k 同样会指向正确的那个人)4. f(1, 1) = 1。5. 将一个数 m, 按照“准备工作2”中的规则映射到整数区间[1, n]内可以这样操作: (m - 1)%n + 1 准备工作完毕。 下面开始正式分析:我们可以根据 f(n-1, k)来快速得到 f(n, k): 我们固定 k 值不变。 假设 f(n-1, k) = r, 也就是说有 (n-1) 个人时, 活下来的那个人到起点的距离是 (r-1)。(起点那个人序号为1) 那么,当人数为 n 时, 我们可以先杀掉一个人让人数变为 (n-1)。根据准备工作中的第三条,我们第一步先杀掉 k。这时新的起点变为了 (k+1),人数变为了 (n-1),活下来的那个人为 (k+1) + (r-1), 也就是 (f(n-1, k) + k)。 我们还要把这个结果处理一下,根据“准备工作5”,处理后的结果为: (f(n-1, k) + k - 1)%n + 1 最终得到的是: f(n, k) = (f(n-1, k) + k - 1)%n + 1分析完毕。 最后用一种你喜欢的语言来实现你的分析结果:f(1, 1) = 1; f(n, k) = (f(n-1, k) + k - 1)%n + 1;这个就很简单了,你可以用递归,也可以用循环。用递归写起来简单,但执行起来比较消耗资源;循环相反。我在这里用scheme的尾递归实现。简单提一下scheme的尾递归,它书写上是递归的形式,但执行时解释器做了优化,不会保存上次的调用栈(因为这在尾递归中是不必要的),所以采用尾递归可以既写起来简单又不会太消耗资源。
#lang scheme(define (f n k) (define (f-iter result counter) (if(>counter n) result (f-iter (add1 (modulo (+ result k -1) counter)) (add1 counter)))) (f-iter 1 2))
运行示例:> (f 5 3)
4 短短的几行代码就把问题解决了。这也告诉了我们在写代码前要先做两件事: 1. 将问题分析透彻,减少不必要的计算量,也就是降低时间复杂度,节约计算机的时间。比如这个问题你也可以不用分析,直接让计算机去傻瓜式地挨个数,一直数到只剩最后一个。可能问题规模不大时,这种方法你还能忍受,一旦问题规模加大,线性时间复杂度和指数时间复杂度的区别还是挺大的。 2. 选择一门合适的计算机语言,尽量快地完成任务,也就是节约自己的时间。同样的问题你也可以选择c语言,java或者其它,但我认为在这个问题的解决上scheme无疑是最优秀的。 PS:也许对上面的结果还不太满意,因为上面只给出了最后剩下的人,你可能还想知道这些人的死亡顺序。我可以给出我写的升级版程序:
#lang scheme(define (advanced-f prisoners k) (define (new-list lst p) (append (list-tail lst (add1 p)) (take lst p))) (let*((len (length prisoners)) (pos (modulo (- k 1) len))) (if(= len 1) (display prisoners) (begin (display (list-ref prisoners pos)) (display"") (advanced-f (new-list prisoners pos) k)))))
运行示例:> (advanced-f '(1 2 3 4 5) 3)
3 1 5 2 (4)同样用的scheme,同样只用了简短的几行代码就解决了问题。至于采用了什么方法,我只简单说几句就不再详细描述了。advanced-f 函数每次只杀掉一名囚犯并打印出来,然后以这名囚犯为界将囚犯列表分为头尾两部分,头部接到尾部上生成新的囚犯列表。将新列表带入下一次操作。直到列表只剩一个人。 结束。 如果程序有什么错误,请大家不吝指出,我也及时更正。
#lang scheme(define (f n k) (define (f-iter result counter) (if(>counter n) result (f-iter (add1 (modulo (+ result k -1) counter)) (add1 counter)))) (f-iter 1 2))
运行示例:> (f 5 3)
4 短短的几行代码就把问题解决了。这也告诉了我们在写代码前要先做两件事: 1. 将问题分析透彻,减少不必要的计算量,也就是降低时间复杂度,节约计算机的时间。比如这个问题你也可以不用分析,直接让计算机去傻瓜式地挨个数,一直数到只剩最后一个。可能问题规模不大时,这种方法你还能忍受,一旦问题规模加大,线性时间复杂度和指数时间复杂度的区别还是挺大的。 2. 选择一门合适的计算机语言,尽量快地完成任务,也就是节约自己的时间。同样的问题你也可以选择c语言,java或者其它,但我认为在这个问题的解决上scheme无疑是最优秀的。 PS:也许对上面的结果还不太满意,因为上面只给出了最后剩下的人,你可能还想知道这些人的死亡顺序。我可以给出我写的升级版程序:
#lang scheme(define (advanced-f prisoners k) (define (new-list lst p) (append (list-tail lst (add1 p)) (take lst p))) (let*((len (length prisoners)) (pos (modulo (- k 1) len))) (if(= len 1) (display prisoners) (begin (display (list-ref prisoners pos)) (display"") (advanced-f (new-list prisoners pos) k)))))
运行示例:> (advanced-f '(1 2 3 4 5) 3)
3 1 5 2 (4)同样用的scheme,同样只用了简短的几行代码就解决了问题。至于采用了什么方法,我只简单说几句就不再详细描述了。advanced-f 函数每次只杀掉一名囚犯并打印出来,然后以这名囚犯为界将囚犯列表分为头尾两部分,头部接到尾部上生成新的囚犯列表。将新列表带入下一次操作。直到列表只剩一个人。 结束。 如果程序有什么错误,请大家不吝指出,我也及时更正。
0 0
- 8行代码解决约瑟夫问题
- java 解决约瑟夫问题
- 约瑟夫问题Java解决
- 解决约瑟夫问题
- php解决约瑟夫问题
- JAVA解决约瑟夫问题
- 约瑟夫问题的解决
- 10行python代码实现约瑟夫问题
- “约瑟夫问题”实现代码
- 约瑟夫问题Java代码
- 约瑟夫问题的代码
- 约瑟夫问题 java代码
- 10行Python代码解决约瑟夫环(模拟)
- 解决下约瑟夫环问题
- 约瑟夫问题 大家一起来解决
- 向量表解决约瑟夫问题
- 数组解决约瑟夫环问题
- 数组解决约瑟夫环问题
- Linux下安装Nginx
- Fashion Shows
- ACM HDOJ 1316 (How Many Fibs?)
- 第一次写博客
- WSGI、flup、fastcgi、web.py的关系
- 8行代码解决约瑟夫问题
- ACM HDOJ 1715 (大菲波数)
- C语言语法&单片机
- LDA原论文的部分解读
- pageContext、request、session和application区别
- web.py的安装说明
- 折半插入排序
- 2014年网络将何去何从
- Hibernate 中getCurrentSession 与 openSession() 的区别