浅议约瑟夫问题

简介:

什么是约瑟夫问题,约瑟夫问题是据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到留下一个人都自杀身亡为止。

解决这个问题,有多少种方法。

①最常用的方法是使用循环链表的解决。首先, 将这39 个犹太人构成一个循环链表,每个犹太人等同与一个结点,这个结点有后继结点相连,最后的结点与第一个结点相连。我实现的思路如下所示:

结点

翻译成源代码如下:


 public class Node
        {
            private Node preNode;
            private Node nextNode;

            private int id;
            private uint password;

            public Node PreNode
            {
                get {
                    return preNode;
                }
                set {
                    preNode = value;
                }
            }

            public Node NextNode {
                get {
                    return nextNode;
                }
                set {
                    nextNode = value;
                }
            }

            public int ID
            {
                get {
                    return id;
                }
                set {
                    id = value;
                }
            }

            public uint Password
            {
                get {
                    return password;
                }
                set {
                    password = value;
                }
            }
            
        }

 循环链表

翻译成源代码如下:


private Node firstNode = null;
        private Node lastNode=null;
        private Node nextNode = null;
        private int count = 0;
       

        public int Count
        {
            get {
                return count;
            }
            set {
                count = value;
            }
        }

        public Circle()
        {
         
        }

        public void Add(int id,uint password)
        {
            count++;
             Node node = new Node();
             node.ID = id;
             node.Password = password;

             this.Add(node);
        }

        public void Add(int id)
        {
            count++;
            Node node = new Node();
            node.ID = id;
            this.Add(node);
            
        }

        private void Add(Node node)
        {
            if (firstNode == null)
            {

                firstNode = node;
                lastNode = firstNode;

                lastNode.NextNode = firstNode;
                lastNode.PreNode = firstNode;

                firstNode.NextNode = lastNode;
                firstNode.PreNode = lastNode;
            }
            else
            {
                lastNode.NextNode = node;

                node.PreNode = lastNode;
                node.NextNode = firstNode;

                firstNode.PreNode = node;
                lastNode = node;
            }
        }

        public Node NextNode()
        {
            Node node=new Node();
            if (nextNode == null)
            {
                node = firstNode;
                nextNode = firstNode.NextNode;
               
            }
            else
            {
                node = nextNode;
                nextNode = node.NextNode;
            }
            return node;
        }

        public void RemoveNode(Node node)
        {
            count--;
            Node _preNode = node.PreNode;
            Node _nextNode = node.NextNode;
            _preNode.NextNode = _nextNode;
            _nextNode.PreNode = _preNode;
        }

把每个循环链表初始化,相应的源代码如下:


List<Node> outList = new List<Node>();
            int index = 0;
            int n = 7;
            uint m = 20;
            List<Node> nodeList = new List<Node>();
            Node nd = new Node();
            nd.ID = 1;
            nd.Password = 3;
            nodeList.Add(nd);

            nd = new Circle.Node();
            nd.ID = 2;
            nd.Password = 1;
            nodeList.Add(nd);

            nd = new Circle.Node();
            nd.ID = 3;
            nd.Password = 7;
            nodeList.Add(nd);

            nd = new Circle.Node();
            nd.ID = 4;
            nd.Password = 2;
            nodeList.Add(nd);

            nd = new Circle.Node();
            nd.ID = 5;
            nd.Password = 4;
            nodeList.Add(nd);

            nd = new Circle.Node();
            nd.ID = 6;
            nd.Password = 8;
            nodeList.Add(nd);

            nd = new Circle.Node();
            nd.ID = 7;
            nd.Password = 4;
            nodeList.Add(nd);

 

            Circle c = new Circle();
            foreach (Node node in nodeList)
            {
                c.Add(node.ID, node.Password);
            }

  这是把所有的数据添加到泛型数组中。

 在进行点名,出列。相应的源代码如下:


while (c.Count > 0)
            {
                index++;
                nd = c.NextNode();
                if (index == m)
                {
                    c.RemoveNode(nd);
                    outList.Add(nd);
                    index = 0;
                    m = nd.Password;
                }
            }

也是放入新的泛型数组中去。

把所显示的结果最终显示的源代码:


foreach (Circle.Node node in outList)
            {
                Console.WriteLine(node.ID);
            }


最终运行的结果如下:

②基于位图算法的思想,

这里做法是建立一个数组来存储相应的数据,建立一个布尔变量来编辑是不是是不是进行了出列的标志,没出列一次,把相应的布尔的变量就置为假,一次循环操作,直到满足题意。这就基本思路,相应的流程图如下:

那实现的源代码就非常简单了,①对数据进行赋值的数组,一个布尔变量标记的数组。②判断布尔变量只有一个为真的方法。③依次报数,当报到相应的数字就进行了出列。④相应的索引大于数列的总长度又归零,直到布尔数组中一个为真的方法。源代码如下:


   public static bool CheckArray(bool[] array)
        {
            int temp = 0;
            for (int i = 0; i < array.Length; i++)
            {
                if (array[i])
                {
                    temp++;
                }
            }
            return temp == 1;
        }
            int[] array = new int[M];
            ;
            bool[] blarray = new bool[M];
            for (int i = 0; i < M; i++)
            {
                array[i] = (i + 1);
                blarray[i] = true;
            }
            int count = 0;
            int index = 0;

            while (!CheckArray(blarray))
            {


                if (blarray[index])
                {
                    count++;

                }
                if (count == N)
                {
                    Console.WriteLine("数字" + array[index] + "出列");
                    count = 0;
                    blarray[index] = false;
                }
                index++;
                if (index == M)
                {
                    index = 0;
                }

            }

运行结果如下:

两种方法互有千秋。比较如下:

2

这就是我对约瑟夫环问题的一点点心得,请大家多多指教。

协后感,不要看这个算法,我以前在做一个callcenter的项目时候,也是完全依据这个算法解决的问题,在做一个操作系统调度的时候,也是这个思想.所以,他的实用价值蛮大,所以像腾讯,华为,度娘面试的时候也经常考这个试题或者基于这个试题的变种。



目录
相关文章
|
8月前
每日一题(珠玑妙算,两数之和)
每日一题(珠玑妙算,两数之和)
71 1
|
8月前
|
容器
每日一题——接雨水(单调栈)
每日一题——接雨水(单调栈)
|
存储 算法 C语言
从古迷题到现代奇迹:神奇的约瑟夫环(C语言)
从古迷题到现代奇迹:神奇的约瑟夫环(C语言)
349 0
|
人工智能 BI
《蓝桥杯每日一题》并查集·AcWing1249. 亲戚
《蓝桥杯每日一题》并查集·AcWing1249. 亲戚
61 0
PTA猴子选大王(约瑟夫环问题)
PTA猴子选大王(约瑟夫环问题)
136 1
LeetCode每日一题(11)——太平洋大西洋水流问题(递归,深度优先遍历实例)
大西洋太平洋水流问题 1.题目 2.示例 3.思路 理解题目 解题思路 4.代码
152 0
LeetCode每日一题(11)——太平洋大西洋水流问题(递归,深度优先遍历实例)
|
人工智能 移动开发
【蓝桥杯集训·每日一题】AcWing 3805. 环形数组
文章目录 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 三、知识风暴 线段树
106 0
|
人工智能 BI
【蓝桥杯集训·每日一题】Acwing 3729. 改变数组元素
文章目录 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 三、知识风暴 一维差分 区间合并
92 0
LeetCode每日一题——1823.找出游戏的获胜者(约瑟夫环问题)
共有 n 名小伙伴一起做游戏。小伙伴们围成一圈,按 顺时针顺序 从 1 到 n 编号。确切地说,从第 i 名小伙伴顺时针移动一位会到达第 (i+1) 名小伙伴的位置,其中 1 <= i < n ,从第 n 名小伙伴顺时针移动一位会回到第 1 名小伙伴的位置。
166 0
LeetCode每日一题——1823.找出游戏的获胜者(约瑟夫环问题)