由100盞燈想到的(一)


原題目地址:http://www.cnblogs.com/DeanChopper/p/4772593.html

  大廳里有100盞燈,每盞燈都編了號碼,分別為1-100。每盞燈由一個開關來控制。(開關按一下,燈亮,再按一下燈滅。開關的編號與被控制的燈相同。)開始時,燈是全滅的。現在按照以下規則按動開關。
第一次,將所有的燈點亮。
第二次,將所有2的倍數的開關按一下。
第三次,將所有3的倍數的開關按一下。
以此類推。第N次,將所有N的倍數的開關按一下。
問第100次按完以后,大廳里還有幾盞燈是亮的。

 

網上找了一下,此題還有其他的表述方式類似:
  禮堂里有100盞電燈,編號1-100,每盞燈由一根燈繩控制,拉一下改變狀態.開始燈全是滅的,100個同學依次進入禮堂,

第一個學生把1的倍數的燈繩拉一下,燈全亮;

接着第二個同學把2倍數的燈繩拉一下;

第三個同學把3倍數的燈繩拉一下······第100個學生把100的倍數的燈繩拉一下.最后,禮堂里哪些燈是亮的?

 

1,暴力解法:

暴力解法一般是思路上最接近題意的,按題目要求的步驟直接求解,暴力求解一般計算量也是最大的。

我一般都是先用笨辦法計算出結果,再想辦法優化。現給出C#暴力解法,得出最后的結果,代碼如下:

 1     class Program {
 2 
 3         //100盞燈,默認全是關着的
 4         static bool[] lights = new bool[100];
 5 
 6         static void Main(string[] args) {
 7             //100次開關測試
 8             for (int i = 1; i <= 100; i++) {
 9                 ChangeState(i);
10             }
11              
12             //開關完成,打印最后的結果
13             Console.WriteLine("最后亮燈的還有{0}盞。", lights.Count(r => r));
14 
15             //分別打印每盞燈
16             for (int i = 0; i < 100; i++) {
17                 if (lights[i]) {
18                     Console.WriteLine(i + 1);
19                 }
20             }
21 
22             Console.WriteLine("實驗結束!");
23             Console.ReadKey();
24         }
25 
26         /// <summary>
27         /// 開關燈
28         /// </summary>
29         /// <param name="x"></param>
30         private static void ChangeState(int x) {
31 
32             for (int i = 0; i < 100; i++) {
33                 if ((i + 1) % x == 0) {
34                     lights[i] = !lights[i];
35                 }
36             }
37 
38         }
39 
40     }

上面的解法很容易就能求出結果,各位可以先自行測試一下。

2.優化

  暴力解法可以得到結果,但這肯定是不能讓人滿意的。現在回過頭來再分析這個題。

第一次操作,每個燈的開關都被按一次;

第二次操作,2的倍數的開關都被按一次;

.....

下面列出前十次,前十個數的具體情況

  燈1 燈2 燈3 燈4 燈5 燈6 燈7 燈8 燈9 燈10
步驟1 操作 操作 操作 操作 操作 操作 操作 操作 操作 操作
步驟2   操作   操作   操作   操作   操作
步驟3     操作     操作     操作  
步驟4       操作       操作    
步驟5         操作         操作
步驟6           操作        
步驟7             操作      
步驟8               操作    
步驟9                 操作  
步驟10                   操作
操作次數 1 2 2 3 2 4 2 4 3 4

可以看到,最下面一列是操作的次數,操作次數為奇數次的,燈最后是打開的狀態,是偶數次,則為關閉。

被操作的規律如下:

燈1:1

燈2:1,2

燈3:1,3

燈4:1,2,4

...

依次寫下去,就可以發現,每個燈被操作的步驟,其實為此燈編號的因數。

如燈36被操作的步驟:1,2,3,4,6,9,12,18,36

此時題目轉化為求[1-100]100個數中,因數個數為奇數的數的數量是多少。

因為最近在學習Python,求因數的算法用python 語言給出,3.4 版本運行成功 如下:

 1 def getZYS(n):
 2     x=2
 3     zys=[]
 4     zys.extend([1,n])
 5     while x*x<=n:
 6         if n%x==0:
 7             if x*x==n:
 8                 zys.extend([x])
 9             else:
10                 zys.extend([x,n//x])
11         x+=1
12     return zys
13     
14 n=input('請輸入一個正整數:')
15 n=int(n)
16 lb=getZYS(n)
17 lb=sorted(lb)
18 print(lb)
19             


此時,題目的解法已經第一次被推廣,在一定程度上,我們已經可以解決更多類似的問題。推廣后的解法請自行試着去解決。

3.就事論事

求一個正整數的質數的過程,如上面代碼所示,其實就是對某個數平方根以下的數試着求余,但具體到本題而言,轉化一下描述為,怎樣快速判斷一個數的因數個數是否為奇數。

請看上面的代碼,第7-10行,如果能深刻理解這幾行,上面所述的題目會有更快的解法。

 

我們注意到,求一個數因數的過程,是從1開始到其平方根,除平方根外,其他的因數都是成對的出現!,也就是,只有平方數的因數是奇數個!,100以內的平方數只有10個,分別是1,4,9,16,25,36,49,64,81,100,所以上述題目的最后結果是10個燈最后是亮着的。

優化完的代碼,注釋要多一些,代碼本身將非常之少,因為100的平方根是10,所以最后亮10盞燈。

你明白了沒有?

 

附:

求一個正整數的因數的個數的研究,最初來源於一個Python群的入群題目:

求1<=i<=10**12范圍內所有d(i)的和的末12位,d(i)表示i的正約數的和,i為整數

 

這個題目可以試着解一下

 

未完待續)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM