本文目录一览:
- 1、有N个人围成一环形圈,第一个人从1开始报数,报道M的人出列,直到最后一个同学,请写出算法。
- 2、约瑟夫环的介绍
- 3、求解约瑟夫环的思路分析
- 4、约瑟夫环问题的数学解
- 5、约瑟夫环问题
- 6、什么是约瑟夫问题
- 7、求约瑟夫环问题的解法
- 8、约瑟夫问题的一般形式
- 9、关于约瑟夫环问题的实验报告书中详细设计
- 10、数学上的约瑟夫问题怎么解
有N个人围成一环形圈,第一个人从1开始报数,报道M的人出列,直到最后一个同学,请写出算法。
建一个单循环链表类,结点类
将n个人连成单循环链表circle,每个结点信息为人名 例如:char name
for(int i=1;i<=n;i++)
circle.InsertBefore(i,name);
调用约瑟夫求解函数
Joseph(CirList
{
ListNode
current=circle.NextElem(); //指向下一结点
for(int i=1;i<=n-1;i++) //删除过程循环n-1次
{
for(int j=1;j<=m-1;j++) //将current往后移m-1次
current=circle.NextElem(current);
p=circle.Delete(current);//删除当前节点并返回地址
cout<<"出列顺序:"<
}
current=circle.NextElem();
p=circle.NextElem(current);
cout<<"出列顺序:"<
经典的约瑟夫环问题
设n个人围成一圈,标号为0..n-1,从第一个人开始依次从1到k循环报数,当报到k的
时候此人出圈。设J(n, k, i)表示第i个出圈的人的标号。
定理一:
J(n, k, 1) = (k-1) MOD n, (n>=1, k>=1) ………… (1)
证明:由定义直接得证。
定理二:
J(n+1, k, i+1) = (k + J(n, k, i)) MOD (n+1), (n>=1, k>=1, 1<=i<=n) ………
… (2)
证明:
设g = J(n, k, i),因此如果有n个人,从0开始报号,第i个出圈的标号为g。现在考
虑J(n+1, k, i+1),因为J(n+1, k, 1) = (k-1) MOD (n+1),即第一步的时候删除数
字(k-1) MOD (n+1),第二步的时候从数字k开始数起。因而问题变为了找到剩下的n
个数字中从k开始数起被删除的第i个数字(注意这时(k-1) MOD (n+1)已经被删除了)
,而这恰好就是(g+k) MOD (n+1),(2)成立。
根据(2),很容易求得n个数里面第i个出圈的数。
就根据这个定理递推计算吧!
我的一个已测试程序:不妨一看。
http://hi.baidu.com/shy2850/blog/item/0ca9293c43aef50abaa16788.html
建一个单向循环链表
经典的约瑟夫环问题
设n个人围成一圈,标号为0..n-1,从第一个人开始依次从1到k循环报数,当报到k的
时候此人出圈。设J(n,
k,
i)表示第i个出圈的人的标号。
定理一:
J(n,
k,
1)
=
(k-1)
MOD
n,
(n>=1,
k>=1)
…………
(1)
证明:由定义直接得证。
定理二:
J(n+1,
k,
i+1)
=
(k
+
J(n,
k,
i))
MOD
(n+1),
(n>=1,
k>=1,
1<=i<=n)
………
…
(2)
证明:
设g
=
J(n,
k,
i),因此如果有n个人,从0开始报号,第i个出圈的标号为g。现在考
虑J(n+1,
k,
i+1),因为J(n+1,
k,
1)
=
(k-1)
MOD
(n+1),即第一步的时候删除数
字(k-1)
MOD
(n+1),第二步的时候从数字k开始数起。因而问题变为了找到剩下的n
个数字中从k开始数起被删除的第i个数字(注意这时(k-1)
MOD
(n+1)已经被删除了)
,而这恰好就是(g+k)
MOD
(n+1),(2)成立。
根据(2),很容易求得n个数里面第i个出圈的数。
就根据这个定理递推计算吧!
约瑟夫环的介绍
约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。通常解决这类问题时我们把编号从0~n-1,最后1结果+1即为原问题的解。
求解约瑟夫环的思路分析
约瑟夫环
是一个数学的应用问题:
已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
这个就是约瑟夫环问题的实际场景,有一种是要通过输入n,m,k三个正整数,来求出列的序列。这个问题采用的是典型的循环链表的数据结构,就是将一个链表的尾元素指针指向队首元素。 p->link=head
解决问题的核心步骤:
1.建立一个具有n个链结点,无头结点的循环链表
2.确定第1个报数人的位置
3.不断地从链表中删除链结点,直到链表为空
void JOSEPHUS(int n,int k,int m) //n为总人数,k为第一个开始报数的人,m为出列者喊到的数
{
/* p为当前结点 r为辅助结点,指向p的前驱结点 list为头节点*/
LinkList p,r,list;
/*建立循环链表*/
for(int i=0,i
p=(LinkList)malloc(sizeof(LNode));
p->data=i;
if(list==NULL)
list=p;
else
r->link=p;
r=p;
}
p>link=list; /*使链表循环起来*/
p=list; /*使p指向头节点*/
/*把当前指针移动到第一个报数的人*/
for(i=0;i
r=p;
p=p->link;
}
/*循环地删除队列结点*/
while(p->link!=p)
{
for(i=0;i
r=p;
p=p->link;
}
r->link=p->link;
printf("被删除的元素:%4d ",p->data);
free(p);
p=r->link;
}
printf("\n最后被删除的元素是:%4d",P->data);
}
约瑟夫环问题的数学解
下午和朋友聊天的时候,有朋友提到了约瑟夫环问题。你和另外 n-1 个人围成一个圈,按 1,2,...,n 依次编号。第一个人从 1 开始报数,数到 k 的人会被杀掉,然后下一个人重新从 1 开始报数。如此往复,直到最后只剩下一个人。问题是,你应该如何选择自己的初始位置,才能保证最后不被杀掉呢?
速度越快的算法当然越好,毕竟这是一个生死攸关的问题。下面你将会看到,我们如何通过一些基本的数学推导最终得到这个问题的递推解。
考虑这样一种相对简单的情况:总共有 5 个人,数到 3 的人被杀掉。那么,死亡过程如下图所示:
经过一番模拟之后,我们已经对约瑟夫环问题有了一个大概的直观理解。下面,尝试使用数学语言来描述这个问题。
哦对了,假如圆圈里只有你自己,那么你肯定就是最后的幸存者,这是一个极其有用的特殊情况。
这些是我们的 已知条件 :
这个是我们的 未知数 :
现在考虑一种泛化情形:总共有 n 个人,数到 k 的人被杀掉,其中 n >= k。幸存者的位置为 p n 。
显而易见,初始位置为 k 的人将会第一个被杀掉。此时,经过重新排序之后,问题变成了 n-1 个人的情形。幸存者的位置为 p n-1 。如果能够找到从 p n-1 到 p n 的递推关系,那么问题就解决了。
重新排序之后,每个人的位置发生了下面这些变化:
很容易我们能得到这样一个关系式:p n = (p n-1 + k) % n 。再加上刚才讨论的特例,只有一个人的情况时,p 1 = 1 。递推式就已经很明显了。
当然上文只讨论了 n>=k 的情形,实际上对于 n
约瑟夫环问题
约瑟夫环有好多种解法,
但是大体的可以分为两类:
1. 将符合出圈要求的人进行标注,但是不出圈,只是下次再轮到此人时,直接跳过,不参加报数
2. 将符合出圈要求的人直接出圈(从环中删除),剩下的人继续报数
这里列的是第二种方法
出圈的处理在这里: p[i-1]=i;
由于已经有人出圈,所以编号也就得-1了:(s1+m-1)%i
for(j=s1;j<=i-1;j++)
p[j-1]=p[j];
看到了么,这段代码已经把原来的第11号人移到现在的10号位置了
什么是约瑟夫问题
来源是智取奖品问题:
许多人围成一个圈报数,报到一个特定的数的人退出,一支循环下去。
约瑟夫就是猴子选大王,猴子报数,最后选出大王。
约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的人的序号为5,4,6,2,3。最后剩下1号。
假定在圈子里前K个为好人,后K个为坏人,你的任务是确定这样的最少M,使得所有的坏人在第一个好人之前被杀掉。
求约瑟夫环问题的解法
#include
using namespace std;
struct Josephus{
int a;
int num;
struct Josephus *next;
};
struct Josephus *head;
void InitList( int n ){
struct Josephus *p;
int i;
p = (struct Josephus *)malloc( sizeof(struct Josephus) );
head = p;
for( i = 0; i < n; i++ ){
struct Josephus *p2;
scanf( "%d", & p->a );
p->num = i + 1;
if( i != 6){
p2 = (struct Josephus *)malloc( sizeof(struct Josephus) );
p->next = p2;
p = p->next;
}
}
p->next = head;
}
void Exclude(){
struct Josephus *p,*prev;
int m = 20, i = 1;
p = head;
while (1){
prev = p;
p = p->next;
i++;
if( i == m ){
cout << p->num << ' ';
m = p->a;
prev->next = p->next;
i = 0;
if( p->next == p ){
free( p );
return;
}
free( p );
p = prev;
}
}
}
int main(){
int n = 7;
InitList(n);
Exclude();
}
是约瑟夫森环吗?
自己写的 C++程序
希望对你有帮助
/*约瑟夫环 Joseph
是一个数学的应用问题:
已知n个人(以编号1,2,3...n分别表示)
围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;
依此规律重复下去,直到圆桌周围的人全部出列。
例如:n = 9, m = 5
【解答】 出局人的顺序为5, 1, 7, 4, 3, 6, 9, 2, 8。*/
#include
using namespace std;
struct list
{
struct list *last;
int data;
int xh;
struct list *next;
};
int main()
{
int n, m;
cin>>n>>m;
int i;
list *h=NULL,*q=NULL,*d=NULL;
for(i=1;i<=n;i++)
{
list *p=new list;
p->last=NULL;
p->data=0;
p->xh=i;
p->next=NULL;
if(h==NULL)
{
h=p;
q=h;
}
else
{
q->next=p;
p->last=q;
q=p;
}
}
q->next=h;
h->last=q;
q=h;
i=1;
while(q->last!=q)
{
if(i>m)
i=i%m;
q->data=i;
if(q->data==m)
{
cout<
q->last->next=q->next;
q->next->last=q->last;
d=q;
q=q->next;
delete d;
}
else
q=q->next;
i++;
}
cout<
system("pause");
return 0;
}
约瑟夫问题的一般形式
约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3,1。分析:(1)由于对于每个人只有死和活两种状态,因此可以用布朗型数组标记每个人的状态,可用true表示死,false表示活。(2)开始时每个人都是活的,所以数组初值全部赋为false。(3)模拟杀人过程,直到所有人都被杀死为止。pascal代码var a:array [1..20] of integer;n,m,i,j,k,n1,m1:integer;beginreadln(m,n);for i:=1 to m doa[i]:=i;m1:=m;n1:=1;while m1>0 dobeginj:=(n+n1-1-1) mod m1 +1;n1:=j;m1:=m1-1;writeln(a[j]);for k:=j to m1 doa[k]:=a[k+1];end;end.C++代码: #include
关于约瑟夫环问题的实验报告书中详细设计
约瑟夫(Joeph)问题的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序求出出列顺序。
1.需求分析和说明
分析约瑟夫问题:n个人围成圈,每人各持一个密码,任选一个正整数作为报数上限值m,从第一个人开始,数到第m个人,删除并以出列者密码作为新的m值,从下一个人开始进行第二轮操作,直到所有人都出列。例如n=6, m=3, 处理过程下图。
2.设计
n个人围圈,形成线性关系;处理为逐个删除,故用链式结构合适;又人员围成圆圈,所以此链式结构采用循环方式较好;排号按照一个方向进行,故数据结构采用带头结点的单向循环链表。假设人员以首次的编号命名,对每个人员采用编号和密码加以描述。利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。
存储结构:
typedef struct Node
{
ElemType date; //编号
int mima; //密码
struct Node *next;
}Node,*LinkList;
单向循环链表:
LinkList H; //表头指针
Node *Q,*Pre;//*Q指新建结点,*pre指向当前工作结点
Q=(Node*)malloc(sizeof(Node));
构造函数:
void InitList(LinkList *H); //初始化循环链表
void InPut(LinkList H,int *A);//插入结点
void DelList(LinkList H,int *x, int*a); //删除结点
数学上的约瑟夫问题怎么解
在M比较小的时候 ,可以用笔算的方法求解,
M=2
即N个人围成一圈,1,2,1,2的报数,报到2就去死,直到只剩下一个人为止。
当N=2^k的时候,第一个报数的人就是最后一个死的,
对于任意的自然数N 都可以表示为N=2^k+t,其中t
于是就求出了当M=2时约瑟夫问题的解:
求出不大于N的最大的2的整数次幂,记为2^k,最后一个去死的人是2(N-2^k)+1
M=3
即N个人围成一圈,1,2,3,1,2,3的报数,报到3就去死,直到只剩下一个人为止。
此时要比M=2时要复杂的多
我们以N=2009为例计算
N=2009,M=3时最后被杀死的人记为F(2009,3),或者可以简单的记为F(2009)
假设现在还剩下n个人,则下一轮将杀死[n/3]个人,[]表示取整,还剩下n-[n/3]个人
设这n个人为a1,a2,...,a(n-1),an
从a1开始报数,一圈之后,剩下的人为a1,a2,a4,a5,...a(n-n mod 3-1),a(n-n mod 3+1),..,an
于是可得:
1、这一轮中最后一个死的是a(n-n mod 3),下一轮第一个报数的是a(n-n mod 3+1)
2、若3|n,则最后死的人为新一轮的第F(n-[n/3])个人
若n mod 3≠0 且f(n-[n/3])<=n mod 3则最后死的人为新一轮的第n-[n/3]+F(n-[n/3])-(n mod 3)人
若n mod 3≠0 且f(n-[n/3])>n mod 3则最后死的人为新一轮的第F(n-[n/3])-(n mod 3)人
3、新一轮第k个人对应原来的第 3*[(k-1)/2]+(k-1)mod 2+1个人
综合1,2,3可得:
F(1)=1,F(2)=2,F(3)=2,F(4)=1,F(5)=4,F(6)=1,
当f(n-[n/3])<=n mod 3时 k=n-[n/3]+F(n-[n/3])-(n mod 3),F(n)=3*[(k-1)/2]+(k-1)mod 2+1
当f(n-[n/3])>n mod 3时 k=F(n-[n/3])-(n mod 3) ,F(n)=3*[(k-1)/2]+(k-1)mod 2+1
这种算法需要计算 [log(3/2)2009]次 这个数不大于22,可以用笔算了
于是:
第一圈,将杀死669个人,这一圈最后一个被杀死的人是2007,还剩下1340个人,
第二圈,杀死446人,还剩下894人
第三圈,杀死298人,还剩下596人
第四圈,杀死198人,还剩下398人
第五圈,杀死132人,还剩下266人
第六圈,杀死88人,还剩下178人
第七圈,杀死59人,还剩下119人
第八圈,杀死39人,还剩下80人
第九圈,杀死26人,还剩下54人
第十圈,杀死18人,还剩36人
十一圈,杀死12人,还剩24人
十二圈,杀死8人,还剩16人
十三圈,杀死5人,还剩11人
十四圈,杀死3人,还剩8人
十五圈,杀死2人,还剩6人
F(1)=1,F(2)=2,F(3)=2,F(4)=1,F(5)=4,F(6)=1,
然后逆推回去
F(8)=7 F(11)=7 F(16)=8 f(24)=11 f(36)=16 f(54)=23 f(80)=31 f(119)=43 f(178)=62 f(266)=89 f(398)=130
F(596)=191 F(894)=286 F(1340)=425 F(2009)=634
-----来自百度