• <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>
            隨筆 - 51, 文章 - 1, 評論 - 41, 引用 - 0
            數據加載中……

            12球的一個算法

                     12球問題表述如下:12個球,有一個球的重量與其它球的重量不一致。用一臺沒有刻度的天平稱3次把這個球找出。當然12球問題可以推廣至N個球。
                首先考慮這道題目的模式,它屬于輸入反饋的問題。每一次輸入(即稱一次)便會有一個反饋信息,分析反饋信息再次輸入,直到把問題解決。通過一次次輸入反饋、這類問題解法思路較簡單,由于是有窮狀態,狀態數目不大,可以用窮舉法解答。比較所有的輸入,找出最佳輸入(某個標準意義下)。問題是如何衡量輸入的好壞。直觀上,輸入越好,反饋的信息就越大。問題變成了對信息的量化。山農給出了信息量的定義是解決問題的關鍵。信息量是不確定性的大小。如果某個事件的概率為P,則它含有的信息量為log(1/p)。具體理論請參考關于信息論方面的書籍。(這個問題和猜數字很像,大家參考此文《猜數字的一種解法》)。

                 具體到這個12球問題。12個球從0-11編號。0號球為較輕的球可能性為1/24。這個事件的信息量log(24)。同理0號球為重球這個事件的信息量為log(24)。這樣的事件有24個,每個事件出現的概率1/24,所以整個系統的信息量24*(1/24*log(24)),即log(24)
            下面我們考慮一次具體的輸入:
            第一次輸入,天平左端放0號球,右端放1號球。反饋信息可能為平衡,左傾,右傾。
            平衡:這時01號球是標準球。根據上面的計算過程,同樣可以計算出此時系統含有的信息量20*(1/20*log(20)),即log(20)。這次輸入獲得的信息log(24)-log(20)。
            左傾:可能的情況,1號球為輕球,2號球為重球。此時系統的信息量log(2)。獲得的信息log(24)-log(2)。
            右傾:同左傾,此時系統的信息量log(2)。獲得的信息log(24)-log(2)
            這次輸入,出現平衡的概率20/24,出現左傾的概率2/24,出現右傾的概率2/24。這可以計算這次輸入能夠獲得的平均信息量為20/24*(log(24)-log(20))+2/24*(log(24)-log(2))+2/24*(log(24)-log(2)),即20/24*log(24/20)+2/24*log(24/2)+2/24*log(24/2)。

               下面就要枚舉所有可能的輸入,找出
            平均信息量最大的輸入。枚舉需要些技巧,需要把球分類,例如第一次輸入時每個球可以看成相同,左右天平放的球數為1,2,3,4,5,6,一共六情況。如果把每個球區別看待枚舉的次數將會很多。下面介紹分類方法。
            根據球的狀態,分成4類,一類:標準球即排出不標準的可能性,二類:只可能為輕球,三類:只可能為重球,四類:可能為輕球也可能為重球。
            // 把球分類
            // 輸出參數:type 表示對應球的類型,0為標準球,1可能為輕球,2可能為重球,3都有可能
            void CGuessBall::SortBalls(VectorInt
            & type)
            {
                
            for (int i=0; i<m_iNumOfBalls; i++)
                {
                    
            if (m_vPsbAns[i*2== 0)
                        type[i]
            ++;
                    
            if (m_vPsbAns[i*2+1== 0)
                        type[i] 
            += 2;
                }
            }

            // 返回四個數組,分別表示四類球
            void CGuessBall::SortBalls(VectorInt
            & type0, VectorInt& type1, VectorInt& type2, VectorInt& type3)
            {
                
            for (int i=0; i<m_iNumOfBalls; i++)
                {
                    
            if (m_vPsbAns[i*2== 0 && m_vPsbAns[i*2+1== 0)
                        type3.push_back(i);
                    
            else if (m_vPsbAns[i*2+1== 0)
                        type2.push_back(i);
                    
            else if (m_vPsbAns[i*2== 0)    
                        type1.push_back(i);
                    
            else
                        type0.push_back(i);
                }
            }
            下面函數說明如何枚舉,首先找出2*i個球,然后從中找出i個球。計算這次輸入的信息量,需要我以前寫的一篇文章《從集合中枚舉子集 》。
            // 使用分類法得到最佳輸入
            // 輸出參數:left,right左右天平球的標號
            void CGuessBall::GetBestInputSort(VectorInt
            & left, VectorInt& right )
            {
                
            double max_entropy = 0.0;
                
                
            // 把球分類
                VectorInt types(m_iNumOfBalls, 
            0);
                SortBalls(types);
                VectorInt t[
            4];
                SortBalls(t[
            0], t[1], t[2], t[3]);
                
                
            // 枚舉所有可能的輸入組合
                
            // 首先找出i個球,左右天平各方i/2個球。
                CSetIterAgent
            <int> iter(types);
                
            for (int i=2; i<=m_iNumOfBalls; i+=2)
                {
                    iter.Init(i, CSetIter::MULT_DISORDER_IN);
                    VectorInt subset(i, 
            0);
                    
            while (iter.GetNextSubset(subset, types))
                    {            
                        
            // 再從i個球中找出i/2個球
                        CSetIterAgent
            <int> iter_i(subset);
                        iter_i.Init(i
            /2, CSetIter::MULT_DISORDER_IN);
                        VectorInt subset_l(i
            /20);
                        VectorInt subset_r(i
            /20);
                        
            while (iter_i.GetNextSubset(subset_l, subset))
                        {
                            
            // subset - subset_l得到subset_r
                            
            int idx_r=0, idx_l=0, idx=0;
                            
            while (idx < subset.size())
                            {
                                
            if (idx_l >= subset_l.size() ||
                                    subset_l[idx_l] !
            = subset[idx])
                                {
                                    subset_r[idx_r
            ++= subset[idx++];
                                }
                                
            else
                                {
                                    idx_l
            ++, idx++;
                                }
                            }
                            
                            
            // 把球的類型轉化成具體的球號
                            
            int idxs[4= {0000};
                            
            left.assign(i/2-1);
                            
            for (int j=0; j<subset_l.size(); j++)
                            {
                                
            left[j] = t[subset_l[j]][idxs[subset_l[j]]++];
                            }
                            
            right.assign(i/2-1);
                            
            for (int j=0; j<subset_r.size(); j++)
                            {
                                
            right[j] = t[subset_r[j]][idxs[subset_r[j]]++];
                            }
                            
                            
            // 得到所有可能的反饋
                            VectorInt outputs(
            30);
                            
            for (int j=0; j<m_vPsbAns.size(); j++)
                            {
                                
            if (m_vPsbAns[j] == 1)
                                    continue;
                                outputs[CheckFeedback(
            rightleft, j)+1]++;        
                            }
                            
                            
            // 檢查是否是理論最優,提前返回
                             
            if (IsTheoryBestInput(outputs))
                            {
                                m_vLeft 
            = left;
                                m_vRight 
            = right;
                                return;
                            }
                            
                            
            // 比較
                            
            double entropy = CalcEntropy(outputs);
                            
            // 求最大的信息量
                            
            if (max_entropy < entropy)
                            {
                                max_entropy 
            = entropy;
                                m_vLeft 
            = left;
                                m_vRight 
            = right;
                            }
                        }
                    }
                }
                
            left = m_vLeft;
                
            right = m_vRight;
            }


                要使得到的信息量最大,就要使天平左傾,右傾,平衡的出現的概率盡可能相等。四類球,只有三類影響平衡,如果它們的個數能被3整除,左右天平各放三分之一份球。當然也有不能被3整除的情況。三類球,每一類均可能有余數0,1,2。組合起來一共有27種情況。考慮這27種情況配合標準球如何分配。得到以下的算法,先將三類球三等分,然后再考慮27種余數情況,得到第二種算法。
            // 直接法得到最佳輸入
            void CGuessBall::GetBestInputDirect(VectorInt
            & left, VectorInt& right)
            {
                
            // 第一次輸入,沒有標準球。
                
            if (m_iTimes == 0)
                {
                    
            int num = (m_iNumOfBalls+1/ 3;
                    
            left.assign(num, -1);
                    
            right.assign(num, -1);
                    
            for (int i=0; i<num; i++)
                    {
                        
            left[i] = i;
                        
            right[i] = i + num;
                    }
                }
                
            else
                { 
            // 有標準球
                    VectorInt t0, t1, t2, t3;
                    SortBalls(t0, t1, t2, t3);
                    
            int tmp1 = t1.size() / 3;
                    
            for (int i=0; i<tmp1; i++)
                    {
                        
            left.push_back(t1[i]);
                        
            right.push_back(t1[i+tmp1]);
                    }
                    
            int tmp2 = t2.size() / 3;
                    
            for (int i=0; i<tmp2; i++)
                    {
                        
            left.push_back(t2[i]);
                        
            right.push_back(t2[i+tmp2]);
                    }
                    
            int tmp3 = t3.size() / 3;
                    
            for (int i=0; i<tmp3; i++)
                    {
                        
            left.push_back(t3[i]);
                        
            right.push_back(t3[i+tmp3]);
                    }
                    
                    switch ((t3.size()%
            3)*9+(t2.size()%3)*3+(t1.size()%3))
                    {
                    
            case 0*3*3 + 0*3 + 0:
                    
            case 0*3*3 + 0*3 + 1:
                    
            case 0*3*3 + 1*3 + 0:
                        break;
                    
            case 0*3*3 + 0*3 + 2:
                    
            case 0*3*3 + 1*3 + 2:
                    
            case 0*3*3 + 2*3 + 2:
            //        case 1*3*3 + 0*3 + 2:
                        
            left.push_back(t1[tmp1*2]);
                        
            right.push_back(t1[tmp1*2+1]);
                        break;
                    
            case 0*3*3 + 1*3 + 1:
                        
            left.push_back(t1[tmp1*2]);
                        
            right.push_back(t0[0]);
                        break;
                    
            case 0*3*3 + 2*3 + 0:
                    
            case 0*3*3 + 2*3 + 1:
            //        case 1*3*3 + 2*3 + 0:
                        
            left.push_back(t2[tmp2*2]);
                        
            right.push_back(t2[tmp2*2+1]);
                        break;
                    
            case 1*3*3 + 0*3 + 0:
            //        case 1*3*3 + 0*3 + 1:
            //        case 1*3*3 + 1*3 + 0:
                    
            case 2*3*3 + 0*3 + 0:
                        
            left.push_back(t3[tmp3*2]);
                        
            right.push_back(t0[0]);
                        break;
            //        case 1*3*3 + 1*3 + 1:
            //        case 1*3*3 + 1*3 + 2:
            //        case 1*3*3 + 2*3 + 1:
            //            left.push_back(t1[tmp1*2]);
            //            right.push_back(t3[tmp3*2]);
            //            break;
            //        case 1*3*3 + 2*3 + 2:
            //            left.push_back(t1[tmp1*2]);
            //            right.push_back(t1[tmp1*2+1]);
            //            left.push_back(t2[tmp2*2]);
            //            right.push_back(t2[tmp2*2+1]);
            //            break;
            //        case 2*3*3 + 0*3 + 1:
            //        case 2*3*3 + 0*3 + 2:
            //        case 2*3*3 + 1*3 + 0:
            //        case 2*3*3 + 1*3 + 1:
            //        case 2*3*3 + 2*3 + 0:
            //        case 2*3*3 + 1*3 + 2:
            //        case 2*3*3 + 2*3 + 1:
            //            left.push_back(t3[tmp3*2]);
            //            right.push_back(t3[tmp3*2+1]);
            //            break;
            //        case 2*3*3 + 2*3 + 2:
            //            left.push_back(t1[tmp1*2]);
            //            right.push_back(t1[tmp1*2+1]);
            //            left.push_back(t3[tmp3*2]);
            //            right.push_back(t3[tmp3*2+1]);
            //            break;
                    default:;
                    }
                }
                m_vLeft 
            = left;
                m_vRight 
            = right;
            }

            這里要注意,如果有標準球則能達到最佳的輸入。所以第一次沒有標準球要區別對待。此外其實沒有27種情況,因為第二,三類不可能和第四類同時出現。

            這是一個測試程序 ,解答12球的問題,它輸出左右天平放的球的編號,等待用戶判斷左傾,右傾還是平衡,直到輸出結果。

            #ifdef _TEST_
            int main()
            {
                
            int size = 12;
                CGuessBall guess(size);
                
            while (1)
                {
                    VectorInt 
            leftright;
                    
            int fd;
                    guess.GetBestInput(
            leftright);
                    
                    
            // 輸出左右天平的編號
                    
            for (int i=0; i<left.size(); i++)
                        printf(
            "%d, "left[i]);
                    printf(
            "\n");
                    
            for (int i=0; i<right.size(); i++)
                        printf(
            "%d, "right[i]);
                    printf(
            "\n");
                    
                    
            // 等待反饋
                    printf(
            "左傾,平衡,右傾(-1,0,1):");
                    scanf(
            "%d"&fd);
                    
            if (fd <= -1) fd = -1;
                    
            else if (fd >= 1) fd = 1;
                    
            else fd = 0;
                    
                    
            int rz = guess.RemoveImpsAns(fd);
                    
            if (rz == 1)
                    {
                        
            int ans = guess.GetTheFirstImpsAns();
                        printf(
            "猜出結果:%d球為%s球", ans/2, ans%2?"":"");
                        break;
                    }
                    
            else if (rz < 1)
                    {
                        printf(
            "輸入錯誤");
                        break;
                    }
                    
            else
                        continue;
                }
            }
            #endif

                從程序運行情況看,很多球也很少幾次就可以找出不規則球。下面討論N個球要多少次才能確定不規則球。最佳輸入能夠使天平的三種情況出現的次數均勻。如果是最佳輸入,系統的信息量只剩下原來的三分之一。由于對N個球,如果N不能被3整除,第一次就找不到最佳輸入。對N個球可以在[log3(2*N)]+1次內找出不規則球。

            第二個程序是測試上面兩種算法的性能
            #ifdef _TEST_TIME_
            #include 
            <windows.h>
            int main()
            {
                
            const int inc = 20;
                DWORD 
            time;
                CGuessBall guess;
                
            for (int i=0; i<2; i++)
                {
                    
            for (int j=12; j< 12+inc; j++)
                    {
                        
            time = 0;
                        
            for (int t=0; t<j*2; t++)
                        {
                            guess.Init(j, i);
                            DWORD start 
            = GetTickCount();
                            
            while (1)
                            {
                                VectorInt 
            leftright;
                                guess.GetBestInput(
            leftright);
                                
            int fd = guess.CheckFeedback(leftright, t);
                                
            int rz = guess.RemoveImpsAns(fd);
                                
            if (rz == 1)
                                    break;
                            }
                            
            time += GetTickCount()-start;
                        }
                        
            time /= j*2;
                        printf(
            "方法:%d\t球數:%d\t平均時間:%d\n", i, j, time);
                    }
                }
                
                return 
            0;
            }
            #endif
            這是測試結果:
            方法:0 球數:12        平均時間:0
            方法:0 球數:13        平均時間:0
            方法:0 球數:14        平均時間:0
            方法:0 球數:15        平均時間:0
            方法:0 球數:16        平均時間:0
            方法:0 球數:17        平均時間:1
            方法:0 球數:18        平均時間:1
            方法:0 球數:19        平均時間:1
            方法:0 球數:20        平均時間:5
            方法:0 球數:21        平均時間:5
            方法:0 球數:22        平均時間:5
            方法:0 球數:23        平均時間:6
            方法:0 球數:24        平均時間:6
            方法:0 球數:25        平均時間:6
            方法:0 球數:26        平均時間:27
            方法:0 球數:27        平均時間:26
            方法:0 球數:28        平均時間:25
            方法:0 球數:29        平均時間:167
            方法:0 球數:30        平均時間:162
            方法:0 球數:31        平均時間:157
            方法:0 球數:32        平均時間:171
            方法:0 球數:33        平均時間:165
            方法:0 球數:34        平均時間:163
            方法:1 球數:12        平均時間:0
            方法:1 球數:13        平均時間:0
            方法:1 球數:14        平均時間:0
            方法:1 球數:15        平均時間:0
            方法:1 球數:16        平均時間:0
            方法:1 球數:17        平均時間:0
            方法:1 球數:18        平均時間:0
            方法:1 球數:19        平均時間:0
            方法:1 球數:20        平均時間:0
            方法:1 球數:21        平均時間:0
            方法:1 球數:22        平均時間:0
            方法:1 球數:23        平均時間:0
            方法:1 球數:24        平均時間:0
            方法:1 球數:25        平均時間:0
            方法:1 球數:26        平均時間:0
            方法:1 球數:27        平均時間:0
            方法:1 球數:28        平均時間:0
            方法:1 球數:29        平均時間:0
            方法:1 球數:30        平均時間:0
            方法:1 球數:31        平均時間:0
            方法:1 球數:32        平均時間:0
            方法:1 球數:33        平均時間:0
            方法:1 球數:34        平均時間:0


            這里是全部代碼

            posted on 2008-03-22 18:04 lemene 閱讀(436) 評論(0)  編輯 收藏 引用

            久久精品无码一区二区三区| 久久人妻少妇嫩草AV无码蜜桃 | 一本色道久久99一综合| 精品久久久久香蕉网| 久久精品国产清自在天天线| 亚洲精品国产美女久久久| 国产亚洲色婷婷久久99精品91 | 久久久精品午夜免费不卡| 久久毛片一区二区| 久久国产精品二国产精品| 久久九九精品99国产精品| 久久这里的只有是精品23| 18岁日韩内射颜射午夜久久成人| 久久亚洲精品无码VA大香大香| 国产精品狼人久久久久影院| 久久亚洲精品无码AV红樱桃| 伊人久久大香线蕉AV一区二区 | 亚洲精品蜜桃久久久久久| 国产精品嫩草影院久久| 91久久精品91久久性色| 2019久久久高清456| 色婷婷久久久SWAG精品| 国产精品九九久久免费视频 | 久久精品卫校国产小美女| 久久精品国产一区二区三区不卡 | 久久精品国产欧美日韩| 国产精品九九久久精品女同亚洲欧美日韩综合区 | 国产精品乱码久久久久久软件 | 免费观看成人久久网免费观看| 日本人妻丰满熟妇久久久久久| 东方aⅴ免费观看久久av| 波多野结衣久久一区二区 | 久久国产乱子伦精品免费强| 久久狠狠高潮亚洲精品| 国产亚洲综合久久系列| 国产午夜福利精品久久2021| 久久99精品国产麻豆| 国产一区二区精品久久| 国产AⅤ精品一区二区三区久久| 久久综合狠狠综合久久激情 | 伊人久久大香线蕉精品|