关于 约瑟夫问题,报道m值的数出列。

约瑟夫问题:

用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。

#include<stdio.h>
#include<stdlib.h>

typedef struct ringnode{
    int pos;
    struct ringnode *next;
}ring,*Ring;

void create(Ring Head,int count)
{
    Ring cur = NULL, tail = NULL;
    int i = 1;
    tail = Head;
    while(--count > 0)
    {
        cur = (Ring)malloc(sizeof(ring));
        i++;
        cur -> pos = i;
        tail -> next = cur;
        tail = cur;
    }
    tail -> next = Head;
}

void play(Ring Head,int m)
{
    Ring cur,tail;
    int i = 1;
    cur = tail = Head;
    while(cur!=NULL)
    {
        if(i == m)
        {
            printf("\n %d",cur->pos);
            tail -> next = cur -> next;
            free(cur);
            cur = tail -> next;
            i = 1;
        }
        tail = cur;
        cur = cur->next;
        if(tail == cur)
        {
            printf("\n %d",cur->pos);
            free(cur);
            break;
        }
        i++;
    }
}

int main(void)
{
    Ring head = NULL;
    int m = 0, n = 0;
    printf("-------------------the game start-------------------\n");
    printf("N(person count) = ");
    scanf("%d",&n);
    printf("M(out count) = ");
    scanf("%d",&m);
    if(n <= 0 || m <= 0)
    {
        printf("input error!\n");
        system("pause");
        return 0;
    }

    head = (Ring)malloc(sizeof(ring));
    head->pos = 1;
    head->next = NULL;
    create(head,n);
    printf("\norder:");
    play(head,m);
    printf("\n");
    system("pause");
    return 0;
}

对问题的简化,若只是求出最后胜利者。

思想:归纳为数学性问题。原文说的很好,还是直接Copy吧,因为搜索半天也没有找到原作者,所以无法添加引用地址了,如果这位大哥看到这里,请告知与我,小弟立刻加入引用链接:)

无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。我们注意到原问题仅仅是要求出最后的胜利者的序号,而不是要读者模拟整个过程。因此如果要追求效率,就要打破常规,实施一点数学策略。

为了讨论方便,先把问题稍微改变一下,并不影响原意:
问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。

我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的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-2   --> n-2
k-1   --> n-1
变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x‘=(x+k)%n

如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 ---- 这显然就是一个倒推问题!好了,思路出来了,下面写递推公式:

令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]

递推公式
f[1]=0;
f[i]=(f[i-1]+m)%i;  (i>1)

有了这个公式,我们要做的就是从1-n顺序算出f[i]的数值,最后结果是f[n]。因为实际生活中编号总是从1开始,我们输出f[n]+1
由于是逐级递推,不需要保存每个f[i],程序也是异常简单:

借鉴如下:

#include <stdio.h>
int main()
{
    int n, m, i, s = 0;
    printf ("N M = ");
    scanf("%d%d", &n, &m);
    for (i = 2; i <= n; i++)
    {
        s = (s + m) % i;
    }
    printf ("\nThe winner is %d\n", s+1);
}

这个算法的时间复杂度为O(n),相对于模拟算法已经有了很大的提高。算n,m等于一百万,一千万的情况不是问题了。可见,适当地运用数学策略,不仅可以让编程变得简单,而且往往会成倍地提高算法执行效率。

相比之下,解法二的优越性不言而喻,同时说明数学确实很重要。

时间: 2024-10-12 08:50:21

关于 约瑟夫问题,报道m值的数出列。的相关文章

给定一个整数N,找出一个比N大且最接近N,但二进制权值与该整数相同 的数

1,问题描述 给定一个整数N,该整数的二进制权值定义如下:将该整数N转化成二进制表示法,其中 1 的个数即为它的二进制权值. 比如:十进制数1717 的二进制表示为:0000 0110 1011 0101 故它的二进制权值为7(二进制表示中有7个1) 现在要求一个比N大,且最靠近N的数,且这个数的二进制权值与N相同.(这里不考虑Integer.MAX_VALUE 和负数情形.) 对于有符号的32位整数而言:它们的补码如下: Integer.MAX_VALUE= 0111 1111 1111 11

约瑟夫环问题求解

问题描述:已知n个人,分别以编号1,2,3,...n表示,围坐在一张圆桌周围.从编号为k的人开始报数,数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又出列:依此规律重复下去,直到圆桌周围的人全部出列,求最后一个出列人的编号. 一般性递归算法思考:n个人围成一圈,从k开始以m为步长报数,第(k+m)-1个人出列:于是转化为n-1个人围成一圈,从(k+m-1)+1开始以m为步长报数,第(k+m)+m-1个人出列:再转化为求n-2个人围成一圈,从(k+2m-1)+1开始以m为步长报数,

cmake函数參数解析

近期在迁移公司的make系统到cmake上.发现cmake的function參数非常奇怪.比如,假设我们向一个function传递list作为參数,在function中,形參会变成例如以下状况: set(SRC) list(APPEND SRC a.cpp b.cpp) list(APPEND SRC c.cpp d.cpp) function(tst_arguments src_list) message("src_list = "${src_list}) endfunction()

python中os.system()的返回值

最近遇到os.system()执行系统命令的情况,上网搜集了一下资料,整理如下,以备不时之需,同时也希望能帮到某些人. 一.python中的 os.system(cmd)的返回值与linux命令返回值(具体参见本文附加内容)的关系 大家都习惯用os.systemv()函数执行linux命令,该函数的返回值十进制数(分别对应一个16位的二进制数).该函数的返回值与 linux命令返回值两者的转换关系为:该函数的返回值(十进制)转化成16二进制数,截取其高八位(如果低位数是0的情况下,有关操作系统的

python os.system()返回值判断

最近遇到os.system()执行系统命令的情况,上网搜集了一下资料,整理如下,以备不时之需,同时也希望能帮到某些人. 一.python中的 os.system(cmd)的返回值与linux命令返回值(具体参见本文附加内容)的关系 大家都习惯用os.systemv()函数执行linux命令,该函数的返回值十进制数(分别对应一个16位的二进制数).该函数的返回值与 linux命令返回值两者的转换关系为:该函数的返回值(十进制)转化成16二进制数,截取其高八位(如果低位数是0的情况下,有关操作系统的

python可变參数调用函数问题

一直使用python实现一些想法,近期在使用python的过程中出现这样一个需求,定义了一个函数.第一个是普通參数.第二个是默认參数,后面还有可变參数,在最初学习python的时候,都知道非keyword可变參数和keyword可变參数两种,调用的方式或许多种多样,这里主要提出一个比較隐含的问题.并将各种可能出现的情况进行了探讨. 函数声明格式 python尽管不支持函数重载,可是通过对函数參数的众多特性的支持基本弥补了. 函数声明的通式例如以下: def func(argv1,argv2...

【LeetCode-面试算法经典-Java实现】【215-Kth Largest Element in an Array(数组中第K大的数)】

[215-Kth Largest Element in an Array(数组中第K大的数)] [LeetCode-面试算法经典-Java实现][所有题目目录索引] 代码下载[https://github.com/Wang-Jun-Chao] 原题 Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth d

【枚举】【权值分块】bzoj1112 [POI2008]砖块Klo

枚举长度为m的所有段,尝试用中位数更新答案. 所以需要数据结构,支持查询k大,以及大于/小于 k大值 的数的和. 平衡树.权值线段树.权值分块什么的随便呢. 1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 using namespace std; 5 typedef long long ll; 6 struct Point{int v,p;}t[100001]; 7 bool operator &

寻找数列中第K大的数

版权所有 未经允许 请勿擅自商用 转载请指明出处 最早看到这个问题是在那本Mark写的数据结构与算法分析的书中引论部分,当时就是瞅瞅,到了最近,在实际应用中,我需要查找一些列的数中第k大的数时,我才重新回顾品味这个问题.现在,实际问题中,我还暂时没有解决问题,但是这段思考过程很有意思,在这里给大家品味下. 具体的问题有点复杂,在这里就不赘述了,暂且将这个问题形式化的描述如下: 给定一个有限无序数列记做{an},从这个数列中找出第k大的数. 输入:数列{an},k 输出:第k大的数 首先,有个最简