约瑟夫问题 -- C语言

问题描述

据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。 [1] 

17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。

算法解析

        编号 为 1, 2,…, N 的 N 个人 按 顺时针 方向 围坐 一圈, 每人 持有 一个 密码( 正 整数), 一 开始 任选 一个 正 整数 作为 报数 上 限值 M, 从 第一 个人 开始 按 顺时针 方向 自 1 开始 按 顺序 报数, 报到 M 时 停止 报数。 报 M 的 人 出列, 将他 的 密码 作为 新的 M 值, 从 他在 顺时针 方 向上 的 下一 个人 开始 重新 从 1 报数, 如此 下去, 直至 所有人 全部 出列 为止。 试 设计 一个 程序 求出 出列 顺序。

         显然 当 有人 退出 圆圈 后, 报数 的 工作 要从 下一 个人 开始 继续, 而 剩下 的 人 仍然是 围成 一个 圆圈 的, 因此 可以 使用 循环 单 链 表。 由于 退出 圆圈 的 工作 对应 着 表中 结点 的 删除 操作, 对于 这种 删除 操作 频繁 的 情况, 选用 效率 较 高的 链 表 结构。 为了 程序 指针 每 一次 都 指向 一个 具体 的 代表 一个 人的 结点 而 不需要 判断, 链 表 不带 头 结点。 所以, 对于 所有人 围成 的 圆圈 所 对应 的 数据 结构 采用 一个 不带 头 节点 的 循 环链 表 来 描述。 设 头 指针 为 p, 并 根据 具体 情况 移动。

        为了 记录 退出 的 人的 先后 顺序, 采用 一个 顺序 表 进行 存储。 程序 结束 后再 输出 依次 退出 的 人的 编号 顺序。 由于 只 记录 各个 节点 的 data 值 就可以, 所以 定义 一个 整型 一 维 数组。 如 int quit[ n]; n 为 一个 根据 实际问题 定义 的 一个 足够 大的 整数。

代码实现

int JosephusLoop(st_dataNode * head, int * output, int len, int M){
	if(NULL == head || NULL == output || len < 0){
		printf("%s: param error\n",__func__);
		return PARAM_ERR;
	}	


	st_dataNode * suicide = NULL;
	st_dataNode * prev = NULL;
	st_dataNode * next = NULL;
	
	int cnt = 0, k = 0;

	prev = head;
	suicide = head->next;
	next = suicide->next;
	
	while(suicide->next != suicide){
		cnt++;
		/*报数为M,那么自杀*/
		if(0 == cnt % M){
			/*记录到输出*/
			output[k++] = suicide->data;
			/* 自杀 */
			free(suicide);
		
			/*摘掉suicide*/
			prev->next = next;
			suicide = next;
			next = suicide->next;

			/*重新计数*/
			cnt = 0;
			continue;
		}

		prev = suicide;
		suicide = next;
		next = suicide->next;		
	}

	output[k++] = suicide->data;
	free(suicide);
	
	return SUCCESS;
}


void testJosephusLoop(void){

	int len = getListLen(ghead1);
	int i = 0;
	int M = 1768 % len;
	/*存储结果*/
	int output[100] = {0};

	/*形成环*/
	st_dataNode * tail = NULL;
	tail = ghead1;
	while(NULL != tail->next){
		tail = tail->next;
	}
	tail->next = ghead1;

	JosephusLoop(ghead1, output, 1000, M);
	printf(" [ ");
	for(i = 0; i < len; i++){
		printf(" %d ", output[i]);
	}
	printf(" ] \n");

	ghead1 = NULL;
		
	return;
}

编译调试

gcc listMain.c list.c -o a.exe -DDEBUG

调试输出

========= Dump List 0x1630230 ===========
         0  1  3  4  5  6  6  7  8  9  11  19  22  28  29  32  47  53  116  119
===================================
 [  8  47  5  28  3  22  4  32  7  0  53  19  11  29  119  6  6  116  9  1  ]
发布了191 篇原创文章 · 获赞 43 · 访问量 26万+

猜你喜欢

转载自blog.csdn.net/leoufung/article/details/104364266