• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            qinzuoyan

              C++博客 :: 首頁 :: 聯系 :: 聚合  :: 管理
              8 Posts :: 0 Stories :: 16 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(3)

            我參與的團隊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            最近一個朋友在用計算機模擬人來玩文曲星上的“猜數字”游戲,也邀我一起討論。我以前沒有玩過,不過發現這個還是蠻有趣的,于是也想寫一個能猜數據的小程序。

            通過幾日思考,終于實現了一個稍微有點聰明的。以下是C++代碼(下載源代碼)。

            首先函數頭的定義部分,其中一個重要的數據結構是各個排列之間的關系矩陣。
            我們使用 char 來表示兩個排列的關系,char值的計算方法為“A*10+B”;譬如假設 排列i 與 排列j 的關系為“1A2B”,則 table[i][j] = (char)12。
             1 #include <iostream>
             2 
             3 #define N 4           // 排列長度
             4 #define M (10*9*8*7)  // 總的可能排列數
             5 #define MATCH (N*10)  // 猜測正確時的judge()返回值
             6 
             7 #define swap(a,b) {int t=a; a=b; b=t;}
             8 
             9 int arrange[M][N];    // 所有可能排列的列表,一行一個排列
            10 char table[M][M];     // 各排列之間的關系矩陣
            11 
            12 int arr_count;        // 用于生成所有排列時使用的全局變量

            判斷兩個排列之間關系的函數:
             1 // 判斷兩個排列之間的關系
             2 // 譬如如果結果為“1A2B”,則返回值為 (char)12
             3 char judge(int v1[], int v2[])
             4 {
             5     int a = 0;
             6     int b = 0;
             7     for (int i=0; i<N; i++) {
             8         if( v1[i] == v2[i])
             9             a += 1;
            10         for (int j=0; j<N; j++) {
            11             if (v1[i] == v2[j])
            12                 b += 1;
            13         }
            14     }
            15     b -= a;
            16     return (char)(a * 10 + b);
            17 }
            18 

            初始化排列列表:
             1 // 使用遞歸方法計算排列
             2 void arr(int a[], int start)
             3 {
             4     if (start == N) {
             5         for (int i=0; i<N; i++)
             6             arrange[arr_count][i] = a[i];
             7         arr_count++;
             8         return;
             9     }
            10     for (int i=start; i<=9; i++) {
            11         swap(a[start], a[i]);
            12         arr(a, start+1);
            13         swap(a[start], a[i]);
            14     }
            15 }
            16 
            17 // 初始化所有可能排列情況的列表
            18 void init_arrange()
            19 {
            20     int a[10];
            21     arr_count = 0;
            22     for (int i=0; i<=9; i++
            23         a[i] = i;
            24     arr(a, 0);
            25 }

            初始化關系矩陣:
            1 // 初始化各個排列間的關系矩陣
            2 void init_table()
            3 {
            4     for (int i=0; i<M; i++) {
            5         for (int j=0; j<=i; j++) {
            6             table[i][j] = table[j][i] = judge(arrange[i], arrange[j]);
            7         }
            8     }
            9 }

            為了方便,這里有一些打印信息的函數:
             1 // 按照“[0 1 2 3]”的形式打印排列
             2 void print_arrange(int arr_index)
             3 {
             4     std::cout << "[";
             5     for (int i=0; i<N; i++) {
             6         if (i != 0)
             7             std::cout << " ";
             8         std::cout << arrange[arr_index][i];
             9     }
            10     std::cout << "]";
            11 }
            12 
            13 // 按照類似“0A0B”的形式打印裁判的結果
            14 void print_result(char result)
            15 {
            16     std::cout << ((int)result/10<< "A" << ((int)result%10<< "B" <<std::endl;
            17 }
            18 
            19 // 打印一次猜測與結果的信息
            20 void print_guess_info(int arr_index, char result) {
            21     print_arrange(arr_index);
            22     std::cout << " --> ";
            23     print_result(result);
            24     std::cout << std::endl;
            25 }

            便于擴展,使用抽象類。以下是裁判與玩家雙方的接口:
             1 // 玩家的抽象類
             2 class Player
             3 {
             4 public:
             5     // 開始游戲前的初始化
             6     virtual void init() = 0;
             7     // 獲取下一次猜測的排列
             8     virtual int getNextGuess() = 0;
             9     // 告訴玩家上一次猜測的結果
            10     virtual void setGuessResult(int arr_index, char result) = 0;
            11 };
            12 
            13 // 裁判的抽象類
            14 class Judgment
            15 {
            16 public:
            17     // 開始游戲前的初始化
            18     virtual void init() = 0;
            19     // 對猜測進行判斷,返回結果
            20     virtual char doJudge(int arr_index) = 0;
            21 };

            現在讓玩家玩一次游戲,直到猜成功:
             1 // 玩一次游戲,返回猜測次數
             2 int play(Player * p, Judgment * j) {
             3     int time = 0;
             4     p->init();
             5     j->init();
             6     while (1) {
             7         int guess = p->getNextGuess();
             8         char result = j->doJudge(guess);
             9         // print_guess_info(guess, result);
            10         time++;
            11         if (result == (char)MATCH)
            12             break;
            13         p->setGuessResult(guess, result);
            14     }
            15     return time;
            16 }

            尋找一個誠實的裁判:
             1 class HonestJudgment
             2     : public Judgment
             3 {
             4     int _my_arr[N];
             5 public:
             6     HonestJudgment() { }
             7     ~HonestJudgment() { }
             8 public:
             9     void init() { }
            10     // 設置裁判的正確結果
            11     void setArrange(int arr[]) {
            12         for (int i=0; i<N; i++)
            13             _my_arr[i] = arr[i];
            14     }
            15     // 這是一個誠實的裁判,所以會誠實地裁決
            16     char doJudge(int arr_index) {
            17         return judge(_my_arr, arrange[arr_index]);
            18     }
            19 };

            有了裁判,還要找一個玩家。我們先找一個頭腦簡單點的玩家:
             1 class NaivePlayer
             2     : public Player
             3 {
             4     bool _cand[M];
             5     int _cand_count;
             6 public:
             7     NaivePlayer() { }
             8     ~NaivePlayer() { }
             9 public:
            10     void init() {
            11         for (int i=0; i<M; i++)
            12             _cand[i] = true;
            13         _cand_count = M;
            14     }
            15     int getNextGuess() {
            16         // 第一次猜測總是使用第一個排列
            17         if (_cand_count == M)
            18             return 0;
            19         // 返回遇到的第一個可行排列
            20         for (int i=0; i<M; i++) {
            21             if (_cand[i]) {
            22                 return i;
            23             }
            24         }
            25     }
            26     void setGuessResult(int arr_index, char result) {
            27         // 依據返回結果進一步排除已有的可行排列
            28         for (int j=0; j<M; j++) {
            29             if (_cand[j] && table[arr_index][j] != result)
            30                 _cand[j] = false;
            31             _cand_count--;
            32         }
            33     }
            34 };

            用Factory工廠制造裁判和玩家(哈哈,批量生產):
            1 Player * player_factory()
            2 {
            3     return new NaivePlayer();
            4 }
            5 
            6 Judgment * judgment_factory()
            7 {
            8     return new HonestJudgment();
            9 }

            現在讓大家玩起來吧:
             1 int main()
             2 {
             3     int time_stat[M]; // 次數統計表
             4     Judgment *jp;
             5     Player *pp;
             6     // 獲取實例
             7     jp = judgment_factory();
             8     pp = player_factory();
             9     // 初始化次數統計表
            10     for (int i=0; i<M; i++)
            11         time_stat[i] = 0;
            12     // 初始化排列列表和關系矩陣
            13     init_arrange();
            14     init_table();
            15     // 每一種排列情況都玩一次
            16     for (int i=0; i<M; i++) {
            17         ((HonestJudgment*)jp)->setArrange(arrange[i]);
            18         int t = play(pp, jp);
            19         print_arrange(i);
            20         std::cout << " : " << t << std::endl;
            21         time_stat[t]++;
            22     }
            23     // 統計結果
            24     int total = 0;
            25     std::cout << "-------------" << std::endl;
            26     for (int i=0; i<M; i++) {
            27         if (time_stat[i]) {
            28             std::cout << i << "\t" << time_stat[i] << std::endl;
            29             total += time_stat[i]*i;
            30         }
            31     }
            32     std::cout << "Average: " << total << "/" << M << " = " << (float)total/<< std::endl;
            33     std::cout << "-------------" << std::endl;
            34     delete jp;
            35     delete pp;
            36     return 0;
            37 }


            得到的統計數據:
                NaivePlayer Algorithm:
                
            -------------
                
            1       1
                
            2       13
                
            3       108
                
            4       596
                
            5       1664
                
            6       1770
                
            7       755
                
            8       128
                
            9       5
                Average: 
            28029/5040 = 5.56131
                
            -------------
            雖然頭腦簡單,但是還都能猜出來,只是次數多了點。

            好了,現在整個框架搭起來了?,F在的任務是尋找更聰明的玩家,讓猜測次數降下來。

            還真找到了一個貪婪的玩家(算法原理見代碼注釋),且叫他 Clever1Player 吧:
             1 class Clever1Player
             2     : public Player
             3 {
             4     bool _cand[M]; // 排列是否可行的標志矩陣
             5     int _cand_count; // 當前可行排列的個數
             6 public:
             7     Clever1Player() { }
             8     ~Clever1Player() { }
             9 public:
            10     void init() {
            11         for (int i=0; i<M; i++)
            12             _cand[i] = true;
            13         _cand_count = M;
            14     }
            15     int getNextGuess() {
            16         // 第一次猜測總是使用第一個排列
            17         if (_cand_count == M)
            18             return 0;
            19         // 當只有一個可行排列時,直接返回
            20         if (_cand_count == 1) {
            21             for (int i=0; i<M; i++
            22                 if (_cand[i])
            23                     return i;
            24         }
            25         ///////////////////
            26         // 悲觀假設:
            27         //     假設無論猜什么排列,電腦總是能聰明地返回結果,
            28         //     使得依據返回結果能進一步排除掉的可行排列數目最少(即剩余的可行排列數目最多)
            29         // 貪婪算法:
            30         //     從可行排列中選擇下一次猜測的排列;
            31         //     在悲觀假設總是成立的條件下,使得每次排除掉的可行排列數目最多(即剩余的可行排列數目最少),即求最好的最壞情況;
            32         ///////////////////
            33         int res_count[MATCH+1];
            34         int min_max = M+1;
            35         int min_max_index;
            36         for (int i=0; i<M; i++) {
            37             // 對于每一個可行排列 Ri
            38             if (_cand[i]) {
            39                 // 下一次猜測排列 Ri 的時候,裁判將會返回一個結果;
            40                 // 根據悲觀假設,裁判返回的結果總是使得依據返回結果能進一步排除掉的可行排列數目最少(即剩余的可行排列數目最多)
            41                 // cur_max 記錄該最壞情況下剩余的可行排列的數目
            42                 int cur_max = 0;
            43                 for (int j=0; j<=MATCH; j++)
            44                     res_count[j] = 0;
            45                 for (int j=0; j<M; j++) {
            46                     if (_cand[j])
            47                         res_count[ (int)table[i][j] ]++;
            48                 }
            49                 for (int j=0; j<=MATCH; j++)
            50                     if (res_count[j] > cur_max)
            51                         cur_max = res_count[j];
            52                 // 如果 cur_max 小于全局 min_max,則更新 min_max,并記錄下當前排列的序號
            53                 // 這樣能找到最好最壞情況
            54                 if (cur_max < min_max) {
            55                     min_max = cur_max;
            56                     min_max_index = i;
            57                 }
            58             }
            59         }
            60         // 返回最好最壞情況下的排列序號
            61         return min_max_index;
            62     }
            63     void setGuessResult(int arr_index, char result) {
            64         // 依據返回結果進一步排除已有的可行排列
            65         for (int j=0; j<M; j++) {
            66             if (_cand[j] && table[arr_index][j] != result) {
            67                 _cand[j] = false;
            68                 _cand_count--;
            69             }
            70         }
            71     }
            72 };
            73 
            74 // 讓工廠開始生產我
            75 Player * player_factory()
            76 {
            77     return new Clever1Player();
            78 }

            好了,Clever1Player 也來玩了,看看他的成績:
                Clever1Player Algorithm:
                
            -------------
                
            1       1
                
            2       13
                
            3       108
                
            4       620
                
            5       2002
                
            6       1937
                
            7       356
                
            8       3
                Average: 
            26979/5040 = 5.35298
                
            -------------
            成績還不錯,不過最差情況下要猜8次嘛。

            有人說最多7次就可以猜出來,我們再找找。
            沒想到 Clever1Player 的兄弟 Clever2Player 也會玩,而且玩的也不錯(算法原理見代碼注釋):
             1 class Clever2Player
             2     : public Player
             3 {
             4     bool _cand[M];
             5     int _cand_count;
             6 public:
             7     Clever2Player() { }
             8     ~Clever2Player() { }
             9 public:
            10     void init() {
            11         for (int i=0; i<M; i++)
            12             _cand[i] = true;
            13         _cand_count = M;
            14     }
            15     int getNextGuess() {
            16         // 第一次猜測總是使用第一個排列
            17         if (_cand_count == M)
            18             return 0;
            19         // 當只有一個可行排列時,直接返回
            20         if (_cand_count == 1) {
            21             for (int i=0; i<M; i++
            22                 if (_cand[i])
            23                     return i;
            24         }
            25         ///////////////////
            26         // 與Clever1Player的區別:
            27         //     下一次猜測的排列不局限于可行排列
            28         ///////////////////
            29         int res_count[MATCH+1];
            30         int min_max = M+1;
            31         int min_max_index;
            32         for (int i=0; i<M; i++) {
            33             // 對于所有排列 Ri
            34             int cur_max = 0;
            35             for (int j=0; j<=MATCH; j++)
            36                 res_count[j] = 0;
            37             for (int j=0; j<M; j++) {
            38                 if (_cand[j])
            39                     res_count[ (int)table[i][j] ]++;
            40             }
            41             for (int j=0; j<=MATCH; j++)
            42                 if (res_count[j] > cur_max)
            43                     cur_max = res_count[j];
            44             if (cur_max < min_max) {
            45                 min_max = cur_max;
            46                 min_max_index = i;
            47             }
            48         }
            49         return min_max_index;
            50     }
            51     void setGuessResult(int arr_index, char result) {
            52         for (int j=0; j<M; j++) {
            53             if (_cand[j] && table[arr_index][j] != result) {
            54                 _cand[j] = false;
            55                 _cand_count--;
            56             }
            57         }
            58     }
            59 };
            60 
            61 // 讓工廠開始生產我
            62 Player * player_factory()
            63 {
            64     return new Clever2Player();
            65 }

            Clever2Player 的成績:
                Clever2Player Algorithm:
                
            -------------
                
            1       1
                
            2       2
                
            3       22
                
            4       176
                
            5       1301
                
            6       2885
                
            7       653
                Average: 
            29161/5040 = 5.78591
                
            -------------
            不錯,果然在7次之內都能猜出來。只是為什么平均成績還不如 Clever1Player 呢?
            這個問題,我得找時間問問他。

            當前Clever1Player時間復雜度為 (M*M*Time) ,其中Time為猜測次數??紤]次數總小于10,所以時間復雜度為 (M^2)。

            我曾經做過優化,即使用一個數組保存可行排列序號,這樣在 Clever1Player 的 36 和 45 行就只需要掃描數組,而不需要每次都掃描 M 次了。當然總的時間復雜度依然是 (M^2)。

            Clever2Player 與 Clever1Player 的唯一區別在于 “去掉了Clever1Player 的第 38 行”,使得“下一次猜測的排列不局限于可行排列”。
            基于的考慮是:
            -------------
            假設正確結果是 0124
            第一次猜 0123 -> 3A0B
            Clever1Player 接下來不能猜 4567 ,因為 4567 不在可行集中(已經排除)。
            但是Clever2Player 則有可能會猜 4567 得到 0A1B,直觀上說,猜測范圍就縮小到 01234567 中了,搜索空間減小很多。實際上我們人腦使用邏輯推理的時候通常就是這么考慮的,而且我們肯定是不會使用這種搜索算法的。
            至于邏輯推理法與搜索法之間有什么聯系,我也不清楚。
            -------------

            另外,該算法的關注點在于猜測次數,即在于效果而不是效率。所以為了使算法看起來清晰,我沒有過多優化。

            NaivePlayer 算法執行時間最快,大約1分鐘跑完所有5040種情況,Clever1Player大約5分鐘,Clever2Player大約10分鐘。

            另外我也曾考慮過“動態規劃”算法,遞推公式: t(F) = min{ t(F-f)+1, for all f belongs to F}, 其中 F 是當前可行集,t(F) 為該可行集 F 條件下猜出結果的次數,f 為 F 中的一個可行排列。
            但是我沒有編程實現,因為擔心復雜度太高運行不完。
            而貪心算法得到的應當不是最優解,但是近似最優解,而且從次數和執行效率上來說都是可以接受的。

            下一步做什么呢?

            我們可以找一個狡猾的裁判,他讓玩家 Clever1Player 和 Clever1Player 的“悲觀假設”變成現實。
            一邊是聰明的玩家,一邊是狡猾的裁判,哈哈,有好戲看了。
            究竟鹿死誰手,請聽下回分解。

            最后感謝東坡_居士,讓我有機會接觸這么好玩的游戲。

            posted on 2009-08-14 14:11 左言 閱讀(3162) 評論(15)  編輯 收藏 引用

            Feedback

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現 2009-08-14 14:41 東坡_居士
            Clever1Player 思路基本了解了,Clever2Player 不明白
            程序沒看,感覺看不懂c++面向對象了。。。

            還有一個問題,這個程序復雜度多少???或者說跑出這個結果要多久?  回復  更多評論
              

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現 2009-08-14 15:04 左言
            @東坡_居士

            當前Clever1Player時間復雜度為 (M*M*Time) ,其中Time為猜測次數??紤]次數總小于10,所以時間復雜度為 (M^2)。

            我曾經做過優化,即使用一個數組保存可行排列序號,這樣在 Clever1Player 的 36 和 45 行就只需要掃描數組,而不需要每次都掃描 M 次了。當然總的時間復雜度依然是 (M^2)。

            Clever2Player 與 Clever1Player 的唯一區別在于 “去掉了Clever1Player 的第 38 行”,使得“下一次猜測的排列不局限于可行排列”。
            基于的考慮是:
            -------------
            假設正確結果是 0124
            第一次猜 0123 -> 3A0B
            Clever1Player 接下來不能猜 4567 ,因為 4567 不在可行集中(已經排除)。
            但是Clever2Player 則有可能會猜 4567 得到 0A1B,直觀上說,猜測范圍就縮小到 01234567 中了,搜索空間減小很多。實際上我們人腦使用邏輯推理的時候通常就是這么考慮的,而且我們肯定是不會使用這種搜索算法的。
            至于邏輯推理法與搜索法之間有什么聯系,我也不清楚。
            -------------

            另外,該算法的關注點在于猜測次數,即在于效果而不是效率。所以為了使算法看起來清晰,我沒有過多優化。

            NaivePlayer 算法執行時間最快,大約1分鐘跑完所有,Clever1Player大約5分鐘,Clever2Player大約10分鐘。


              回復  更多評論
              

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現 2009-08-14 15:17 東坡_居士
            恩,確實可以用可行解之外的做試探,我之前也想到這點,總感覺不可思議,猶豫之下為了少算幾次就沒用了~

            找最好最壞那一步再描述一下?是否有遞歸啊?是否對算過的集合做記錄?  回復  更多評論
              

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現 2009-08-14 15:18 左言
            @東坡_居士


            回復東坡_居士:你的算法“如果這個“提問”得到的“答案”種類越多,每個答案下面的可能數字就越少”,實際上可以通過對Clever1Palyer的進行修改實現:
            34 int all_max = 0;
            35 int all_max_index;
            36 for (int i=0; i<M; i++) {
            37 // 對于每一個可行排列 Ri
            38 if (_cand[i]) { // 這一行也可以去掉
            41 // cur_max 記錄該當前行可行排列的關系的種數
            42 int cur_max = 0;
            43 for (int j=0; j<=MATCH; j++)
            44 res_count[j] = 0;
            45 for (int j=0; j<M; j++) {
            46 if (_cand[j])
            47 res_count[ (int)table[i][j] ]++;
            48 }
            49 for (int j=0; j<=MATCH; j++) // 求種類數
            50 if (res_count[j] > 0)
            51 cur_max ++;
            52 // 如果 cur_max 小于全局 all_max,則更新 all_max,并記錄下當前排列的序號
            53 // 這樣能找到種類數最多的情況
            54 if (cur_max > all_max) {
            55 all_max = cur_max;
            56 all_max_index = i;
            57 }
            58 }
            59 }

            所以我的假設是最壞假設,你的假設是等概率假設。呵呵,你把裁判想得比較善良。
              回復  更多評論
              

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現 2009-08-14 15:22 左言
            @東坡_居士
            最好最壞就是說:每一行求最大(最壞),然后對所有行的最大值中取最?。ㄗ詈茫?。所以計算過程還是 O(M^2) 的。  回復  更多評論
              

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現 2009-08-14 15:32 東坡_居士
            @左言
            恩,我理解錯了~

            我之前想的新的思路是這樣:

            因為問題的根源都在于我們怎樣評價一個狀態的好壞,我們現在都用數字的多少區評價,數字越多越糟糕。這個評價還是很初級簡單的。

            顯然,既然求最小步數,那么最準確的評價就是,這個狀態可以(平均、最壞)多少步得到解。

            但是,這個評價標準需要遞歸(太慢,我測試9876用了十幾分鐘),或者記錄狀態(太大存不下)

            我現在在想這個的優化~希望在可接受時間內跑出所有解~  回復  更多評論
              

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現 2009-08-14 15:44 左言
            @東坡_居士
            我猜測你也在考慮“動態規劃”方法。
            我考慮過“動態規劃”算法,遞推公式: t(F) = min{ t(F-f)+1, for all f belongs to F}, 其中 F 是當前可行集,t(F)為該可行集 F 條件下猜出結果的次數,f 為 F 中的一個可行排列。
            但是我沒有編程實現,因為擔心復雜度太高運行不完。但是可以保證這樣能得到最優解。
            貪心算法得到的應當不是最優解,但是近似最優解,而且也是可以接受的。  回復  更多評論
              

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現 2009-08-14 15:49 左言
            @東坡_居士
            上面公式還可以進一步修改一下:
            t(F) = min{ t( F- conf(f) ) + 1, for all f belongs to F}, 其中conf(f)指與f沖突的集合(每一種返回結果就對應一個沖突集,所以復雜度又上升了)。  回復  更多評論
              

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現 2009-08-14 15:55 東坡_居士
            @左言

            恩,是這樣,我現在覺得能存下來,不過需要hash一下,時間上應該還行,一兩個小時我也認了~呵呵~

            還有一個問題是這樣,在一個可行解集合里,每一個數的概率是一樣的吧?  回復  更多評論
              

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現 2009-08-14 16:06 左言
            @東坡_居士

            我覺得不應當考慮概率,而應當考慮在任意時刻,可行解集合里任何數都有可能是正確結果。實際上就是在一個狀態轉移圖中,任何可行的路徑都有可能沿著走下去,每選擇一個數,就走進了一個分支。
              回復  更多評論
              

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現 2009-08-15 06:54 空明流轉
            我當年就是直接枚舉法的,沒搞那么復雜。。。  回復  更多評論
              

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現 2009-08-15 11:35 凡客誠品
            沒搞那么復雜。。。   回復  更多評論
              

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現 2009-08-15 20:05 左言
            @空明流轉

            直接枚舉法,你說的是對5040種排列依次猜測嗎。。。?最壞情況下猜測次數可能5040次吧?;蛘吣阌懈玫霓k法嗎?
              回復  更多評論
              

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現 2010-05-06 18:48 zt
            通過信息熵來看,最少的次數應該是3.7次。所以還是有些開發的空間。。求大牛啊。  回復  更多評論
              

            # re: 文曲星“猜數字”游戲的計算機模擬 —— 算法分析與實現[未登錄] 2011-01-27 17:35 zz
            你這不嚴謹啊。這個問題其實就是博弈,電腦想成會變卦的電腦,猜數的步數取判決步數中最大的,判決步數取猜數字最小的  回復  更多評論
              

            久久免费美女视频| 久久婷婷国产麻豆91天堂| 久久久精品视频免费观看| 久久久一本精品99久久精品88| 久久九九久精品国产| 久久人与动人物a级毛片| 亚洲国产成人久久一区久久| 青青草原综合久久| 久久笫一福利免费导航 | 99久久精品这里只有精品| 色婷婷综合久久久中文字幕| 亚洲欧美成人综合久久久| 久久亚洲精品无码aⅴ大香| 久久亚洲国产成人影院| 中文字幕亚洲综合久久2| 亚洲综合熟女久久久30p| 久久精品国产亚洲av麻豆小说| 久久天天躁狠狠躁夜夜av浪潮| 香港aa三级久久三级老师2021国产三级精品三级在 | 麻豆AV一区二区三区久久| 久久久久久久久久免免费精品| 久久久久亚洲av无码专区导航 | 婷婷伊人久久大香线蕉AV| 色综合久久88色综合天天 | 国产精品欧美久久久久无广告| 久久综合给合综合久久| 2021精品国产综合久久| 久久久精品视频免费观看| 国产福利电影一区二区三区久久久久成人精品综合 | 2020最新久久久视精品爱| 欧美精品久久久久久久自慰| 要久久爱在线免费观看| 女人高潮久久久叫人喷水| 中文字幕精品无码久久久久久3D日动漫| 国产精品欧美久久久久天天影视| 一本一道久久精品综合| 久久久国产精品网站| 精品99久久aaa一级毛片| 久久综合亚洲色HEZYO社区 | 国产精品美女久久久久久2018| 久久影视综合亚洲|