惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Schneier on Security
有赞技术团队
有赞技术团队
T
The Blog of Author Tim Ferriss
F
Fortinet All Blogs
D
DataBreaches.Net
F
Full Disclosure
腾讯CDC
博客园 - 【当耐特】
MyScale Blog
MyScale Blog
Stack Overflow Blog
Stack Overflow Blog
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
Last Week in AI
Last Week in AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
爱范儿
爱范儿
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
SegmentFault 最新的问题
The Register - Security
The Register - Security
WordPress大学
WordPress大学
博客园 - 聂微东
雷峰网
雷峰网
J
Java Code Geeks
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Privacy International News Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
A
Arctic Wolf
Scott Helme
Scott Helme
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Tor Project blog
博客园 - 三生石上(FineUI控件)
Know Your Adversary
Know Your Adversary
AWS News Blog
AWS News Blog
G
Google Developers Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
C
CERT Recently Published Vulnerability Notes
O
OpenAI News
Project Zero
Project Zero
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Application and Cybersecurity Blog
Application and Cybersecurity Blog
云风的 BLOG
云风的 BLOG
N
News and Events Feed by Topic
MongoDB | Blog
MongoDB | Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Microsoft Security Blog
Microsoft Security Blog
Cisco Talos Blog
Cisco Talos Blog
P
Palo Alto Networks Blog
Schneier on Security
Schneier on Security

博客园 - I,Robot

短网址的简单实现 怎样在前端Javascript中调用C#方法(4)验证授权 怎样在前端Javascript中调用C#方法(3)使用特性Attribute 怎样在前端Javascript中调用C#方法(2)传递参数(附源码+高手勿入) 怎样在前端Javascript中调用C#方法(1)简单实现(附源码) 闲来无事,写了段js仿google首页动画,附源码下载 终于请了个客服值夜班了,嘿嘿,不是18K月薪 我的编程之路 说再见的那一刻,我都不敢回头多看你们一眼… 终于解决了”SQL多表连接查询C#化”这的问题 提交备案申请4年后,网站备案号终于下来了,可惜网站跟域名早已不见了 我的山寨HTC(5)-电池 我的山寨HTC(4)-U盘 我的山寨HTC(3)-WIFI 我的山寨HTC(2)-GPS 我的山寨HTC(1)-购机 eBay中国上海总部之行20091020 eBay中国2009开发者论坛上海之行 在一个没有爱心的城市,谈何文明,谈何和谐
趣味算法:国王和100个囚犯(据说是腾讯的面试题)
I,Robot · 2010-08-10 · via 博客园 - I,Robot

在其它地方看到一道题目,估计有不少园友也已经看过了,也有同学解了,但本人比较愚,当时看到这个题目,我都快蒙了,还好有好心人给了些思路,于是,慢慢摸索着,用我们伟大的面向对象的思想来解这道题(虽然这道题跟面向对象没有半点关系)。
题目如下:

    国王招来100个囚犯,对他们说:你们犯的是死罪,但我给你们一次求生的机会。15分钟以后,你们将被关进一个有100间隔离牢房的监狱里,每人一间牢房,都与外界隔绝,什么也听不见看不到,连时间都没法计算,无法获得外界的任何信息。 
    这所监狱有一个院子,每天只少随机(注意是完全随机)打开一间牢房的门,让一个囚犯到院子里来放风。院子里有一盏灯,放风的囚犯可以控制它的开关,将它打开或是关闭。除囚犯之外,其他人都不会去碰开关。这盏灯会永远有充足的能源供应,灯泡和电路不会出故障。 
    除了开关这盏灯,放风的囚犯放风时留下的任何其它痕迹都会在夜晚被清除干净(包括在灯上作的任何记号)。牢房是完全封闭的,院子里的灯光在牢房里看不到。只有放风到院子里的人才能看到。 

    好了现在我向你们提出一个要求,只要你们做到了,就可以全部获得释放: 
    给你们15分钟商量你们的方案。15分钟以后,你们将被关进我刚才说的那个监狱,永远无法再交流,被关若干天后,你们中间如果任何一个人能够向我证明你们每个人都至少放风了一次,我就把你们放了,不然永远别想再出来。 
    好吧!这样吧,如果你们有谁现在可以告诉我这个方法,也就是能够证明你们每人至少放风一次的方法,我马上放掉你们! 

    其中一个囚犯想了几分钟,回答了这个问题,国王听后,如自己所说的把他们全部给放了。请问那个囚犯是用什么方法证明的?

大致的解题思路(建议思考后再看): 

代码


囚犯们指定一人为计数人(A),A以外的囚犯每次出来放风时,如果看到灯是关闭的,则将灯打开,但如果已经开过一次灯,则不理会,当A出来放风时,如果灯是开着的,则将灯关掉,关一次则表示有一个人出来了一次,计数至100,完成。

分析一下,这里面参与求解的东西无非就是监狱、灯、囚犯(计数员)。

监狱:没有什么动作需要做的。

灯:有一个状态标识, 提供开灯和关灯的方法。

囚犯:有一个是否开过灯的标识,并带有一个开灯的方法 

计数员:继承囚犯,且拥有关灯的方法。 

实例代码:

监狱:

Prison

 1 /// <summary>
 2     /// 监狱
 3     /// </summary>
 4     public class Prison
 5     {
 6         /// <summary>
 7         /// 灯
 8         /// </summary>
 9         public Light Light { getset; }
10 
11         /// <summary>
12         /// 牢房
13         /// </summary>
14         public Prisoner[] Prisoners { getset; }
15 
16         /// <summary>
17         /// 随机数生成器
18         /// </summary>
19         Random random;
20 
21         /// <summary>
22         /// 囚犯数量
23         /// </summary>
24         private int PrisonerCount { getset; }
25 
26         /// <summary>
27         /// 构造函数
28         /// </summary>
29         /// <param name="count">囚犯数量</param>
30         public Prison(int count)
31         {
32             PrisonerCount = count;
33             random = new Random(GetRandomSeed());
34 
35             //初始化灯,并将状态设置为关
36             Light = new Light() { Status = LightStatus.OFF };
37 
38             //初始化牢房数组
39             Prisoners = new Prisoner[PrisonerCount];
40             for (int x = 0; x < PrisonerCount; x++)
41             {
42                 Prisoners[x] = new Prisoner() { NO = x };
43             }
44         }
45 
46         public int Run()
47         {
48             SetCountPrisoner();
49             bool finished = false;
50             int count = 0;
51             while (!finished)
52             {
53                 count++;
54                 Helper.Print("{1}\t第 {0} 天", count, DateTime.Today.AddDays(count).ToString("yyyy-MM-dd"));
55                 int x = GetRandomNumber();
56                 var prisoner = Prisoners[x];
57                 prisoner.Release();
58                 prisoner.TrunLightOn(this.Light);
59                 if (prisoner is CountPrisoner)
60                 {
61                     //ShowMsg("囚犯({0})是计数人", x);
62                     var counter = prisoner as CountPrisoner;
63                     counter.TrunLightOff(this.Light);
64                     finished = counter.Finished;
65                 }
66             }
67             return count;
68         }
69 
70         /// <summary>
71         /// 随机设置一个囚犯作为计数人
72         /// </summary>
73         void SetCountPrisoner()
74         {
75             int num = GetRandomNumber();
76             var prisoner = Prisoners[num];
77             Prisoners[num] = new CountPrisoner(prisoner) { Total = PrisonerCount };
78             Helper.Print(true"计数人是:囚犯({0})", num);
79         }
80 
81         /// <summary>
82         /// 取得随机数
83         /// </summary>
84         /// <returns></returns>
85         int GetRandomNumber()
86         {
87             return random.Next(0, PrisonerCount);
88         }
89 
90         public int GetRandomSeed()
91         {
92             byte[] bytes = new byte[4];
93             System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
94             rng.GetBytes(bytes);
95             return BitConverter.ToInt32(bytes, 0);
96         }
97     }

再建立一个灯的类:

Light

 1 /// <summary>
 2     /// 灯
 3     /// </summary>
 4     public class Light
 5     {
 6         /// <summary>
 7         /// 灯的状态
 8         /// </summary>
 9         public LightStatus Status { getset; }
10 
11         public void TurnOn()
12         {
13             if (this.Status == LightStatus.OFF)
14             {
15                 this.Status = LightStatus.ON;
16                 Helper.Print("灯被打开了");
17             }
18         }
19 
20         public void TurnOff()
21         {
22             if (this.Status == LightStatus.ON)
23             {
24                 this.Status = LightStatus.OFF;
25                 Helper.Print("灯被关闭了");
26             }
27         }
28     }
29 
30     /// <summary>
31     /// 灯的状态枚举
32     /// </summary>
33     public enum LightStatus { ON, OFF }

接下来是囚犯:

Prisoner

 1 /// <summary>
 2     /// 囚犯
 3     /// </summary>
 4     public class Prisoner
 5     {
 6         public int NO { getset; }
 7 
 8         /// <summary>
 9         /// 是否开过灯了
10         /// </summary>
11         public bool LightOn { getset; }
12 
13         public void Release()
14         {
15             Helper.Print("囚犯({0})出来放风", NO);
16         }
17 
18         /// <summary>
19         /// 将监狱的灯打开
20         /// </summary>
21         /// <param name="light"></param>
22         public void TrunLightOn(Light light)
23         {
24             if (light.Status == LightStatus.ON)
25             {
26                 Helper.Print("灯是开着的"this.NO);
27                 return;
28             }
29             if (LightOn)
30             {
31                 Helper.Print("囚犯({0})已经开过灯了", NO);
32                 return;
33             }
34             LightOn = true;
35             Helper.Print("囚犯({0})开灯", NO);
36             light.TurnOn();
37         }
38     }

还要有人负责计数:

代码

 1     /// <summary>
 2     /// 负责计数的囚犯
 3     /// </summary>
 4     public class CountPrisoner : Prisoner
 5     {
 6         /// <summary>
 7         /// 构造函数
 8         /// </summary>
 9         /// <param name="prisoner"></param>
10         public CountPrisoner(Prisoner prisoner)
11         {
12             this.NO = prisoner.NO;
13             this.LightOn = prisoner.LightOn;
14         }
15 
16         /// <summary>
17         /// 关灯计数
18         /// </summary>
19         public int Count { getset; }
20 
21         public int Total { getset; }
22 
23         public bool Finished { getset; }
24 
25         /// <summary>
26         /// 将监狱的灯关闭
27         /// </summary>
28         /// <param name="light"></param>
29         public void TrunLightOff(Light light)
30         {
31             if (light.Status == LightStatus.ON)
32             {
33                 Count++;
34                 Helper.Print("囚犯({0})关灯"this.NO);
35                 light.TurnOff();
36                 if (Count < Total)
37                 {
38                     Helper.Print(true"囚犯({0})已经统计到{1}个囚犯出来放风过了"this.NO, this.Count);
39                 }
40                 else
41                 {
42                     Finished = true;
43                     Helper.Print(true"囚犯({0})已经统计到所有囚犯全部出来放风过了,任务完成了"this.NO);
44                     Console.ReadLine();
45                 }
46             }
47         }
48     }

再定义一个Helper来显示文字记录:

代码

 1     class Helper
 2     {
 3         static bool DisabledWait = true;
 4 
 5         public static void Print(string msg, params object[] args)
 6         {
 7             Print(false, msg, args);
 8         }
 9 
10         public static void Print(bool wait, string msg, params object[] args)
11         {
12             Console.WriteLine(msg, args);
13             if (wait && !DisabledWait)
14             {
15                 Console.ReadLine();
16             }
17         }
18 
19     }

测试代码:

代码

 1         static void Main(string[] args) {
 2             var prison = new Prison(100);
 3             prison.Run();
 4             int len = prison.Prisoners.Length;
 5             for (int x = 0; x < len; x++) {
 6                 if (!prison.Prisoners[x].LightOn) {
 7                     Console.WriteLine("Wrong!!!囚犯({0})还没有出来放风过", x);
 8                     Console.ReadLine();
 9                 }
10             }
11         }

 至此,所有的类已经定义完毕。执行Main方法即可得到答案。 计算结果如下(片段):

  1 2042-12-28    第 11828 天

 2 囚犯(49)出来放风
 3 灯是开着的
 4 2042-12-29    第 11829 天
 5 囚犯(75)出来放风
 6 灯是开着的
 7 2042-12-30    第 11830 天
 8 囚犯(70)出来放风
 9 灯是开着的
10 2042-12-31    第 11831 天
11 囚犯(66)出来放风
12 灯是开着的
13 2043-01-01    第 11832 天
14 囚犯(69)出来放风
15 灯是开着的
16 2043-01-02    第 11833 天
17 囚犯(51)出来放风
18 灯是开着的
19 2043-01-03    第 11834 天
20 囚犯(58)出来放风
21 灯是开着的
22 2043-01-04    第 11835 天
23 囚犯(92)出来放风
24 灯是开着的
25 2043-01-05    第 11836 天
26 囚犯(39)出来放风
27 灯是开着的
28 囚犯(39)关灯
29 灯被关闭了
30 囚犯(39)已经统计到所有囚犯全部出来放风过了,任务完成了

乐观的结果是27年左右,计数人就可以统计到所有囚犯全部出来放风一次,但前提条件是,这27年中不能有人挂掉,要不然,计数人就永远Count不到100个,也就永远出不来了。上面的代码,可以适应国王与N个囚犯,但是当囚犯达到150+的时候,基本上就都不用出来了,因为150人时已经差不多需要70年了。。。

附项目文件下载: KingAndPrisoner

其实,这里面有一个问题,在我的代码中,创建监狱实例时,我们就将灯的初始状态置为关了,就表示我们的代码只有在假定灯初始状态为关的情况下才有效,如果灯的初始状态不定,那该怎么解呢?我想了很久,没有找到答案,不知道有没有人能告诉我答案,谢谢。

最新更新解法: 

由Curry同学提供的,每人开灯两次则可以解决灯初始状态不确定的问题.

解题思路:

N=囚犯数量

计数总数 = N * 2 - 2

计数人只关灯不开灯,如果N = 100,则需要关灯的次数为 100 * 2 - 2 = 198.

假设灯初始状态为关,则计数到198时,其它的99个囚犯都已经开过两次灯.

假设灯初始状态为开,则计数到198时, 198 = 98(囚犯) * 2(次) + 1(囚犯) * 1(次) + 1(灯初始状态开) 

修改后的代码:KingAndPrisoner20100810.7z