一、 题目描述:
约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
二、 算法出现问题:
要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。我们注意到原问题仅仅是要求出最后的胜利者的序号,而不是要读者模拟整个过程。因此如果要追求效率,就要打破常规。
三、 思路:
n个人(编号0~(n-1)),从0开始报数,报到m-1的退出,剩下的人继续从0开始报数。求胜利者的编号。我们知道第一个人(编号一定是(m-1)%n) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始):
k k+1 k+2 ... n-2,n-1,0,1,2,... k-2
并且从k开始报0。
现在我们把他们的编号做一下转换:
k --> 0
k+1 --> 1
k+2 --> 2
...
...
k-3 --> n-3
k-2 --> n-2
序列1:0,1,2,3 … n-2,n-1
序列2:0,1,2,3 … k-2,k,…,n-2,n-1
序列3:k,k+1,k+2,k+3,…,n-2,n-1,1,2,3,…,k-2,
序列4:0,1,2,3 …,5,6,7,8,…,n-3,n-2
变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解,假如x是最终的胜利者,那么根据上面这个表把这个x变回去刚好就是n个人情况的解。公式为:
∵ k=m%n; ∴ x' = x+k = x+ m%n ; 而 x+ m%n 可能大于n ∴x'= (x+ m%n)%n = (x+m)%n 得到 x‘=(x+m)%n |
如何知道(n-1)个人报数的问题的解?只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 。
这显然就是一个倒推问题!得到下面写递推公式:
令f表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]. 递推公式: f[1]=0; f[i]=(f[i-1]+m)%i; (i>1) |
下一步要做的就是从1-n顺序算出f的数值,最后结果是f[n]。因为实际生活中编号总是从1开始,我们输出f[n]+1。
四、 代码:
/* 约瑟夫环递推公式:令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n] 递推公式 f[1]=0; f[i]=(f[i-1]+m)%i; (i>1) */ #include "stdio.h" #include "stdlib.h" int main(void) { int n, m,i, f[20]={0}; scanf("%d %d",&n,&m); for(i=2;i<=n;i++) { f[i]=(f[i-1]+m)%i; printf("%d个人报数,报到%d的出列,最后的胜者下标为%d\n", i,m,f[i]); } printf("The winner is %d\n", f[n]+1); system("pause"); }
优化一下:
#include "stdio.h" #include "stdlib.h" int main(void) { int n, m,i, s=0; scanf("%d %d",&n,&m); for(i=2;i<=n;i++) { s=(s+m)%i; } printf("The winner is %d\n", s+1); system("pause"); }
相关推荐
约瑟夫环问题,也称为约瑟夫环序列或约瑟夫问题,是一个著名的理论问题,源自古...在实际编码过程中,还可以考虑优化算法效率,如使用链表代替数组以减少删除元素的时间复杂度,或者利用并发和异步编程进一步提高性能。
这个算法的时间复杂度为O(n),空间复杂度也为O(n),因为我们需要存储所有囚犯的状态。虽然可以通过优化降低空间复杂度,例如仅保留存活的囚犯,但这样会增加算法的复杂性,不易理解。 总的来说,约瑟夫环问题的C#...
- **测试与分析**:展示实验结果,分析算法的时间复杂度和空间复杂度,以及可能存在的优化方向。 - **结论与展望**:总结实验的主要发现,讨论未来研究的方向或可能的应用场景。 综上所述,约瑟夫环实验报告不仅...
约瑟夫环(Josephus Problem)是一个著名的理论问题,...这种解决方案的时间复杂度为 O(n),空间复杂度也为 O(n),因为需要存储所有人的初始位置和出列顺序。尽管存在更优化的解决方案,但此实现简洁明了,易于理解。
这个算法的时间复杂度是O(n),因为它只需要遍历一次循环即可计算出结果,空间复杂度也是O(n),因为需要一个大小为n的数组来存储中间结果。这个解决方案有效地解决了约瑟夫环问题,避免了重复剔除的过程,大大提高了...
- 时间复杂性:由于我们遍历数组的每个元素,最坏情况下需要进行n次操作(n为人数),所以时间复杂度为O(n)。 - 空间复杂性:为了存储所有参与者,顺序表需要的空间与人数n成正比,因此空间复杂度也为O(n)。 在实际...
2. **时间复杂度**:创建链表的时间复杂度为O(n),每次删除操作的时间复杂度为O(m-1),总的时间复杂度取决于游戏的持续时间,大致为O(n*m)。 3. **空间复杂度**:除了链表本身的存储外,没有额外的空间消耗,所以...
数据结构——约瑟夫环 约瑟夫环是一种典型的数据结构问题,它是指在一个环形链表中,删除第 m 个节点的过程。这里,我们将深入探讨...约瑟夫环的时间复杂度和空间复杂度都为 O(n),其中 n 是环形链表中的节点数。
递归算法的时间复杂度为 O(n),空间复杂度为 O(n)(考虑递归调用栈的深度)。在实际应用中,如果问题规模过大,可能会导致栈溢出,这时可以考虑使用循环或其他数据结构优化算法。 总的来说,理解和掌握递归方法对于...
设编号为1,2,•••,n的n个人围坐一圈,约定编号为k(1≤k≤n)的人从1开始报数,数到m的那个人出列,他的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列...
- **时间复杂度**:循环顺序表实现的时间复杂度主要由每次报数后的出列操作决定,每次操作涉及到元素的移动,因此时间复杂度较高,大约为 \(O(n^2)\)。 - **空间复杂度**:无论是循环顺序表还是循环链表实现,都需要...
1. **时间复杂度**:O(n^2),其中n是参与游戏的人数。 2. **空间复杂度**:O(n),主要由存储参与者的数组占用的空间决定。 #### 五、扩展思考 1. **如何优化算法?** 可以考虑使用更高效的数据结构如链表来降低...
- 插入和删除操作的时间复杂度为O(1),只需要修改节点之间的指针即可完成操作。 - **循环链表的实现细节**: - 为了维护循环链表的完整性,每次插入新节点时都需要更新节点之间的指针关系。 - 在删除节点时,...
这个算法的时间复杂度为O(n),空间复杂度也为O(n),因为我们需要存储所有人的出列顺序。尽管这个问题可以使用更高效的数据结构(如链表)来解决,但在这个C#实现中,数组已经足够处理这个问题。 约瑟夫环问题的解决...
时间复杂度方面,如果采用迭代方法,约瑟夫环问题的时间复杂度为O(n),因为每个节点最多只会被访问一次。而如果采用递归方法,虽然在最坏情况下时间复杂度也是O(n),但由于递归的开销,实际运行效率可能会低于迭代...
约瑟夫环问题的解决方法可以进一步优化,例如,通过使用更高效的数据结构或算法减少时间复杂度。此外,对于大规模的n和m,可能需要考虑使用动态规划或递归等方法。在实际应用中,理解并实现这类问题有助于提升对数据...
此外,还可以利用矩阵快速幂或者位运算进行优化,尤其当n非常大时,这些高级技巧能大大降低时间复杂度。例如,利用模意义下的矩阵乘法,可以将问题转化为寻找矩阵的幂次,从而快速得到结果。 在实际应用中,约瑟夫...
- 插入和删除操作效率高:在链表中插入或删除节点的时间复杂度为O(1),而数组需要移动大量元素来完成同样的操作。 综上所述,用数组实现约瑟夫环问题是一个既实用又高效的解决方案,尤其是在已知参与人数且人数固定...
### 约瑟夫环问题解析 ...例如,可以考虑使用链表或其他数据结构来优化算法的时间复杂度和空间复杂度,提高程序的效率。此外,在解决这类问题时还需要注意边界条件和异常情况的处理,以确保程序的健壮性和稳定性。