约瑟夫环是一个经典计算机问题
约瑟夫(Joseph)问题的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈,
每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序求出出列顺序。
1.问题分析
使用数组实现约瑟夫环较链表来说,逻辑上较为复杂一些,但实现起来与链表相比较为轻松。
使用两个数组来存储数据,一个模拟各个同学的编号,一个来模拟同学持有的密码。通过对数组数据的计算来实现约瑟夫环的模拟过程。
2、主要变量设计
为便于我们测试最终算法,我们先使用一些已经既定的数据来模拟约瑟夫环的操作。
我们假设总共有7名同学,每位同学的密码分别3、1、7、2、4、8、4。第一次报数次数为20。
因此我们设计两个数组:
num[7] = { 1,2,3,4,5,6,7 };
code[7] = { 3,1,7,2,4,8,4};
接下来是一些变量:
int count = 7; // 环内剩余人数
int m = 20; // 初始报数次数
int temp = 0; // 临时变量,储存上一名出局的人的位置(新的循环从这里开始)
3、主要算法介绍
首先,为模拟约瑟夫环中大家围坐一圈的场景,我们使用数组下标取模的操作来构造一个“循环数组”:
目标出局的人的数组下标 = (报数次数+本轮报数起始的人的下标-1) % 剩余人员总数
找到本轮待出局的人时,使剩余人数变量count - 1,表示此人出局。
然后将报数次数m赋值为该出局成员的密码值。
temp变量储存出局成员的下标位置,方便我们在数组中继续移动剩余成员的位置。输出出局成员的编号num[temp]。接下来,使用一个循环语句使得被删除成员后面的所有数组元素向前移动即可。
若count = 0,表示所有人出局,程序结束。
4、程序源代码
1. #include <iostream>
2.
3. int main() {
4. int num[] = { 1,2,3,4,5,6,7 }; //同学序号
5. int code[] = {3,1,7,2,4,8,4 }; //所持密码
6. int count = 7; //环内剩余人数
7. int m = 20; //当前需报数次数
8. int i = 0;
9. int temp = 0; //临时变量,存储上一轮出局同学序号
10.
11. while (count != 0) {
12. i = (m + temp-1) % count; //找到目标同学
13. count--; //环内人数减一
14. m = code[i]; //新的循环次数
15. temp = i;
16. std::cout << num[i] << " ";
17.
18. for (int j = i; j < count; j++) {//数组删除数据操作
19. code[j] = code[j + 1];
20. num[j] = num[j + 1];
21. }
22. }
23. }
另,无论提前固定人数还是使用用户可以自定义的数据等,实现方法的是非常相似的。这里便不再赘述。