约瑟夫环的递归思想解

原文地址:http://blog.youkuaiyun.com/u014613043/article/details/50905475

今天做约瑟夫环的题目,对于递归解法不太了解。在网上找了一篇比较好理解的文章。

本文为学习《剑指offer》的记录。因其原理在原作者博客上找不到,所以,只能自己编写记录,如有不当之处,欢迎指正。

题目描述: 
n个数,编号为 0 , 1, ……, n-1 排成一个圆圈,从数字 0 开始,每次从这个圆圈中删除第 m 个数,请问最后一个剩下的数是多少?

推导过程 
定义一个函数 f(n, m) 为 n 个数中取 m 最后剩下的编号。 
第一个被删除的数是 (m-1)%n, 记为 k 。此时因为序列已经不连续,函数可以定义为 f’ ( n-1, m) 。 
明显,最后剩下的编号相等,于是: f(n,m) = f’(n-1, m)

然后我们对剩下的数做一个简单的映射:

k+1 –> 0 
k+2 –> 1 


n-1 –> n-k-2 
0 –> n-k-1 
1 –> n-k 


k-1 –> n-2

这个映射可以记为 
p(x) = (x-k-1)%n 
其中 x 为映射前的数 (可以自己推导一下)

那么逆映射就是: 
p-1(x) = (x + k + 1 ) %n 
(还是建议自己推导一下)

那么,对于上面的函数 f’(n-1, m),就有 
f’(n-1, m) = p-1[f(n-1, m)] = [f(n-1, m) + k +1]%n = f(n, m)

将 k= (m-1)%n 代入: 
f(n, m) = [f(n-1, m) + m]%n

这就是我们要的通项公式。 
很明显,当 n = 1 时,f = 0; 
于是:

f(n, m) = 0, n = 1 
f(n, m) = [f(n-1, m) + m]%n, n>1

使用循环就可以轻松解决这个问题了。

–END–



下面是我的代码:

#include <stdio.h>
#include <string.h>
#define M 10
int a[M] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int n = 0;
void swap(int *a, int *b)
{
int c = 0;
c = *a;
*a = *b;
*b = c;
}
void fun1()
{
int number = M;
int xianyou  = number;
int i = 0;
int count = 1;
while(number != 0)
{
if (i > number)
{
i = i % xianyou;
}
if (a[i] != 0)
{
if (count == n)
{
printf("%d ", a[i]);
a[i] = 0;
count = 1;
i++;
number--;
}
else
{
i++;
count++;
}
}
else
{
i++;
}
}
}
int fun2(int a, int b)
{
if (a == 1)
{
return 0;
}
else
{
return ((fun2(a - 1, b) + b) % a);
}
}
int main(int argc, char const *argv[])
{
printf("Please input num :\n");
scanf("%d", &n);
int count = 1;
int m = M;
/*while(a[0] != 0)
{
for (int i = 0; a[i] != 0; ++i, ++count)
{
if (count == n)
{
printf("%d ", a[i]);
a[i] = 0;
for (int j = i; j < M; ++j)
{
swap(&a[i], &a[i + 1]);
}
}
}
}*/
// fun1();
printf("%d\n", fun2(m, n) + 1);
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值