1. 彈簧秤稱球問題算法分析及程序模擬

        共 5125字,需瀏覽 11分鐘

         ·

        2022-05-30 09:40

        一、拋出問題

        ???????? 昨天紹興越州中學(xué)趙軍老師在QQ群里發(fā)了一個有趣的問題,引起了眾多老師的熱烈討論。問題如下:
        6只球外觀完全一樣,但5球重量一樣,僅有一只重量不同,不知是輕還是重。問:使用一只彈簧稱,其可以稱出任意多個球的重量,最多稱重三次,如何求出每個球重量?

        一開始大家都以為是傳統(tǒng)的無砝碼天平稱球問題,想用分治算法來解決。在趙軍老師的提示之后,才發(fā)現(xiàn)彈簧秤和無砝碼天平的區(qū)別:彈簧秤是可以稱量出小球的具體重量的,而且題目要求求出每個球的具體重量。

        我看了一下題目和大家的討論,一時半會也想不出什么辦法來,就備課去了。
        二、驚現(xiàn)妙解
        過了一會兒,發(fā)現(xiàn)QQ群里又多了不少消息,其中桐鄉(xiāng)高級中學(xué)朱海寧老師的發(fā)言引起了我的注意。

        朱老師把小球依次編號1-6,分成3次稱量,第一次將球1,2,3合在一起稱,第二次將球2,3,4,5合在一起稱,第三次將球3,4合在一起稱,可以表示為:3x=123,4y=23452z=34。

        ?一開始我沒怎么看懂,發(fā)現(xiàn)朱老師提供的表達式中沒有出現(xiàn)6號球,心里就產(chǎn)生了疑問,萬一6號球是異球呢?不稱量它怎么得出異球的重量?于是隨口說了一句:@桐鄉(xiāng)高級中學(xué)朱海寧?6個球哦。

        朱老師耐心地回答我:可以推出來的,比如x=y,那就是6號球有問題,單獨稱一下就好。

        聽了這句話,我感覺有點明白了,其實第三次稱量不一定是將球3,4合在一起稱。若前兩次稱量結(jié)果證明了x=y,則說明6號球是異球,那么第三次就直接稱量6號球,再根據(jù)前兩次稱量結(jié)果,可以計算出其他球的重量。

        我又繼續(xù)思考了x!=y的情形,發(fā)現(xiàn)可以輕松地推導(dǎo)出異球為15的情形,即當(dāng)y=z時,異球為1;當(dāng)x=z時,則異球為5

        但如果異球不是15呢?用純數(shù)學(xué)推導(dǎo)的方法似乎難以實現(xiàn)。我一下子又沒有思路了。
        三、靈光一現(xiàn)
        突然我靈光一現(xiàn)——為什么一定要采用數(shù)學(xué)推導(dǎo)的方式呢?計算思維,利用計算機來幫忙解決問題,不正是我們信息技術(shù)學(xué)科的核心素養(yǎng)嗎?
        模擬算法是最容易想到的方法,給定一些測試數(shù)據(jù)去模擬計算結(jié)果。本問題只有6個小球,問題規(guī)模并不大,我們沒有必要直接編程解題,可以先在腦海中模擬一下。
        我先假設(shè)2號球是異球且偏輕,假設(shè)普通球的重量均為3,可設(shè)置2號球的重量為2,這樣就能計算出x = 8/3,y = 11/4,z = 6/2,從而得到關(guān)系式x < y < z。
        同理,若3號球是異球且偏輕,則有z < x < y;若4號球是異球且偏輕,則有z < y < x。
        由此我們就得出了當(dāng)異球偏輕時的各種可能性,而且可以根據(jù)x,yz的值計算出所有小球的重量。
        當(dāng)異球偏重時可以采用同樣的辦法解決,無非是不等號的方向變化了而已。
        四、算法梳理
        ???????? 現(xiàn)在我們來把整個算法梳理一下:
        1.先取出任意3個球(編號1,2,3)一起稱,得到表達式3x=123;
        2.從已稱量的3個球中任意拿出2個(假設(shè)是2,3,屬于A組),再從未稱量的球中任意拿出2個(編號4,5,屬于B組),合在一起稱,得到表達式4y=2345
        3.若x==y,則說明球6為異球,稱量球6;
        4.若x!=y,則從A組拿出任意一個球(假設(shè)是3),從B組拿出任意一個球(假設(shè)是4),合在一起稱,得到表達式2z=34;
        5.根據(jù)xy,z的大小關(guān)系,可以得知異球:若y=z,則說明異球在x中,且異球為1;若x=z,則說明異球在y中,且異球為5。
        其他情況可以將數(shù)據(jù)代入進一步計算和推導(dǎo)。
        當(dāng)異球偏輕時:若x,則異球為1;若x,則異球為2;若z,則異球為3;若z,則異球為4;若y,則異球為5。
        當(dāng)異球偏重時:若x>y=z,則異球為1;若x>y>z,則異球為2;若z>x>y,則異球為3;若z>y>x,則異球為4;若y>x=z,則異球為5。
        當(dāng)然,第三次稱量的兩個球也不一定非得是34,只要保證從A,B組中各取一個就行了。其組合可以是24、25、3435。
        我把上述算法梳理發(fā)到了QQ群里,獲得了老師們的認可,趙老師和朱老師都為我點贊。

        ?

        五、程序模擬

        事情到這里似乎已經(jīng)結(jié)束了。但只提出算法,不編寫程序不是我的風(fēng)格。不把事情完全做好我是不會罷休的。于是我決定編寫程序?qū)崿F(xiàn)算法功能。

        這是我的第一版程序:

        def fun_1(a):    x = sum(a[:3])  # 第一次稱重:3x=123    y = sum(a[1:5]) # 第二次稱重:4y=2345    if x/3 == y/4: # 前面5個球等重,則球6為異球        z = a[5] # 第三次稱重:稱量異球6        print(f"異球為6,其重量為{z},其他球重量為{x//3}")    else:        z = a[2] + a[3] # 第三次稱重:2z=34        if y/4 == z/2: # 異球為1            print(f"異球為1,其重量為{x-z},其他球重量為{z//2}")        elif x/3 == z/2: # 異球為5            print(f"異球為5,其重量為{y-x},其他球重量為{z//2}")        elif x/3 < y/4 < z/2 or x/3 > y/4 > z/2: # 異球為2            print(f"異球為2,其重量為{y-3*z//2},其他球重量為{z//2}")        elif z/2 < y/4 < x/3 or z/2 > y/4 > x/3: # 異球為4            print(f"異球為4,其重量為{z-x//3},其他球重量為{x//3}")        else: # 異球為3            print(f"異球為3,其重量為{2*z-x},其他球重量為{x-z}")import random       for i in range(16):    a = [3] * 6 # 初始化每個小球重量均為3    num = random.randint(0, 5) # 選擇任意一個小球為異球    a[num] = a[num] + random.choice([-1,1]) #異球重量可輕可重    print(a)????fun_1(a,?2,?4)
        ???????? 程序完全按照前面的算法來編寫,自認為應(yīng)該是完美無缺的。但是程序運行結(jié)果卻讓我傻了眼——第一個數(shù)據(jù)就錯了,明明是4號球偏輕,程序卻說異球為2,而且各個球的重量都算錯了。

        我增加循環(huán)次數(shù),又測了好幾次,發(fā)現(xiàn)其他小球為異球時都是對的,就是4號球出錯。

        問題出在哪里呢?
        我陷入了沉思中。
        六、找出BUG

        為什么4號球總是出錯呢?我又看了看測試結(jié)果,發(fā)現(xiàn)程序總是把4號球錯認為2號球。

        這說明什么問題呢?

        會不會是因為先判斷2號球,再判斷4號球的緣故?我嘗試著把多分支語句中elif語句的位置換了一下,讓程序先判斷4號球,再判斷2號球。

        再次運行程序,觀看測試結(jié)果。果然不出所料,現(xiàn)在程序把2號球錯認為是4號球了。
        原來問題出在這里!
        再仔細看看代碼,發(fā)現(xiàn)這兩條elif語句竟然是等效的,僅僅是寫法變了一下而已!

        原來問題出在這里!

        那該怎么修改呢?看上去似乎沒有辦法。難道我的算法本身就是錯的?
        我再一次陷入了沉思。
        七、問題解決

        2號球和4號球到底誰才是異球?條件表達式怎么會一模一樣呢?是我漏掉了什么東西嗎?

        再仔細看看算法分析。咦,我好像發(fā)現(xiàn)了點什么。

        當(dāng)異球為2時,4號球是普通球,則z應(yīng)該為整數(shù);同理,當(dāng)異球為4時,2號球是普通球,則x應(yīng)該為整數(shù)。

        妙?。∵@是屬于我的尤里卡時刻!

        好了,現(xiàn)在只需要加上兩個限制條件就行了,第二版程序如下:

        def fun_2(a):    x = sum(a[:3])  # 第一次稱重:3x=123    y = sum(a[1:5]) # 第二次稱重:4y=2345    if x/3 == y/4: # 前面5個球等重,則球6為異球        z = a[5] # 第三次稱重:稱量異球6        print(f"異球為6,其重量為{z},其他球重量為{x//3}")    else:        z = a[2] + a[3] # 第三次稱重:2z=34        if y/4 == z/2: # 異球為1            print(f"異球為1,其重量為{x-z},其他球重量為{z//2}")        elif x/3 == z/2: # 異球為5            print(f"異球為5,其重量為{y-x},其他球重量為{z//2}")        elif z/2 == z//2 and (x/3 < y/4 < z/2 or x/3 > y/4 > z/2): # 異球為2            print(f"異球為2,其重量為{y-3*z//2},其他球重量為{z//2}")        elif x/3 == x//3 and (z/2 < y/4 < x/3 or z/2 > y/4 > x/3): # 異球為4            print(f"異球為4,其重量為{z-x//3},其他球重量為{x//3}")        else: # 異球為3            print(f"異球為3,其重量為{2*z-x},其他球重量為{x-z}")
        運行程序,測試結(jié)果完美無瑕。

        ?

        八、精益求精

        現(xiàn)在,問題應(yīng)該說得到完美解決了。但解決問題從來都沒有最好,只有更好。

        第三次稱量的方式有4種,我只考慮了其中一種。另外3種的結(jié)果是不是和前面一致?我還沒有編程驗證。這件事情不做好,就不算完。

        于是,秉著精益求精的精神,追求完美的我又給出了第三版程序:

        def fun_3(a, i, j):    x = sum(a[:3])  # 第一次稱重:3x=123    y = sum(a[1:5]) # 第二次稱重:4y=2345    if x/3 == y/4: # 前面5個球等重,則球6為異球        z = a[5] # 第三次稱重:稱量異球6        print(f"異球為6,其重量為{z},其他球重量為{x//3}")    else:        z = a[i-1] + a[j-1] # 第三次稱重:2z=24或25或34或35        if y/4 == z/2: # 異球為1            print(f"異球為1,其重量為{x-z},其他球重量為{z//2}")        elif x/3 == z/2: # 異球為9-j            print(f"異球為{9-j},其重量為{y-x},其他球重量為{z//2}")        elif z/2 == z//2 and (x/3 < y/4 < z/2 or x/3 > y/4 > z/2): # 異球為5-i            print(f"異球為{5-i},其重量為{y-3*z//2},其他球重量為{z//2}")        elif x/3 == x//3 and (z/2 < y/4 < x/3 or z/2 > y/4 > x/3): # 異球為j            print(f"異球為{j},其重量為{z-x//3},其他球重量為{x//3}")        else: # 異球為i            print(f"異球為{i},其重量為{2*z-x},其他球重量為{x-z}")
        import random for i in range(16): a = [3] * 6 # 初始化每個小球重量均為3 num = random.randint(0, 5) # 選擇任意一個小球為異球 a[num] = a[num] + random.choice([-1,1]) #異球重量可輕可重 print(a) fun_3(a, 2, 4) fun_3(a, 2, 5) fun_3(a, 3, 4) fun_3(a, 3, 5) print("#" * 20)
        程序運行結(jié)果如下:

        終于搞定了,又可以愉快地玩耍上課了。


        需要本文源代碼和word文檔的,可以加入“Python算法之旅”知識星球參與討論和下載文件,Python算法之旅”知識星球匯集了數(shù)量眾多的同好,更多有趣的話題在這里討論,更多有用的資料在這里分享。

        我們專注Python算法,感興趣就一起來!

        相關(guān)優(yōu)秀文章:

        閱讀代碼和寫更好的代碼

        最有效的學(xué)習(xí)方式

        Python算法之旅文章分類

        瀏覽 140
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 狂野欧美做受XXXX高潮 | 一区二区经典 | 日女人的逼 | 国产一级大黄片 | 成人午夜爱爱3p高潮影院 |