• <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>

            huaxiazhihuo

             

            玩具代碼 24點(diǎn)游戲

                    所謂24點(diǎn),就是甩出幾個(gè)整數(shù),整數(shù)之間沒(méi)有固定的前后順序,給它們添加上加減乘除括號(hào)等,形成一條式子,最后運(yùn)算結(jié)果等于24。很自然的想法,就是祭出表達(dá)式樹(shù)。但24點(diǎn)只是一個(gè)小程序,用表達(dá)式樹(shù)來(lái)解決,實(shí)在有點(diǎn)大材小用了,太委屈表達(dá)式樹(shù)了,所以堅(jiān)決抵制。
                    瀏覽各網(wǎng)友們的24點(diǎn)文章,發(fā)現(xiàn)大多數(shù)都在用最笨拙的窮舉法寫(xiě)程序,或者是先搞成字符串,然后再對(duì)字符串求值,毫無(wú)新意,代碼風(fēng)格也差,都是四五重循環(huán),難以擴(kuò)充。用窮舉法也沒(méi)什么不好,但是,就算是窮舉,也要窮舉得漂漂亮亮,雖不能遺漏,但也不要重復(fù),然后代碼中也不能寫(xiě)死了,要保持一定的擴(kuò)充性。不過(guò)得承認(rèn),不管怎么樣,他們終究還是寫(xiě)出了24點(diǎn)的代碼。
                    24點(diǎn)粗看起來(lái)似乎有點(diǎn)棘手。但很容易就可以發(fā)現(xiàn)一種很自然的方法,假如4個(gè)整數(shù)參與24點(diǎn)了,那么就從中取出兩個(gè)數(shù),進(jìn)行加減乘除之后合成一個(gè)數(shù),放回去,于是4個(gè)數(shù)變成3個(gè)數(shù),再用同樣的辦法使這每一排3個(gè)數(shù)的組合變成兩個(gè)數(shù),最后就只剩下兩個(gè)數(shù),稍一運(yùn)算,很容易就可以判斷兩個(gè)數(shù)能否湊成24。很容易就看得出來(lái),這屬回溯法,最適合寫(xiě)成遞歸的形式。但是,這一次的遞歸,要用代碼表達(dá)出來(lái),卻著實(shí)有點(diǎn)不容易。不過(guò),只要有了算法,總是有辦法寫(xiě)成代碼。為了加深難度,也為效率,我也不打算在代碼中用到遞歸。
                    一般來(lái)說(shuō),回溯法屬深度優(yōu)先搜索法,它從問(wèn)題領(lǐng)域中選取一個(gè)狀態(tài)作為當(dāng)前節(jié)點(diǎn),進(jìn)行某一種運(yùn)算之后,形成下一級(jí)的狀態(tài),作為節(jié)點(diǎn),再進(jìn)行某種運(yùn)算,再形成下下級(jí)的狀態(tài),作為根據(jù)地,再嘗試新的節(jié)點(diǎn),直到?jīng)]有可用的節(jié)點(diǎn)了,稱為葉子,就判斷此時(shí)的狀態(tài)是否滿足問(wèn)題的解。不滿足,退回父節(jié)點(diǎn),進(jìn)行運(yùn)算,進(jìn)入下一級(jí)的狀態(tài),繼續(xù)深度搜索。如果父節(jié)點(diǎn)無(wú)法進(jìn)入新的狀態(tài),那么只好退回祖父節(jié)點(diǎn),進(jìn)行同樣的操作。所以回溯算法的關(guān)鍵,在于新?tīng)顟B(tài)的運(yùn)算代碼,和各級(jí)節(jié)點(diǎn)的保存恢復(fù)代碼。
                    再看看24點(diǎn)的問(wèn)題,為便于行文,假設(shè)只有4個(gè)數(shù)參與運(yùn)算。先來(lái)考察第一級(jí)狀態(tài)的節(jié)點(diǎn)數(shù)量。首先,從4個(gè)數(shù)中任意取出兩個(gè)數(shù),共有C(4,2) = 6 種組合,兩個(gè)數(shù)a和b,利用加減乘除和交換位置,可以湊出6個(gè)結(jié)果,分別為a+b,a*b,b-a,a/b,a-b,b/a。于是,第一級(jí)狀態(tài)就有36節(jié)點(diǎn)。同理類推,第二級(jí)狀態(tài)有C(3,2)*6,第三級(jí)狀態(tài)有C(2,2)*6,沒(méi)有第四級(jí)了,第三級(jí)的節(jié)點(diǎn),全部都是葉子,都要判斷。這意味著,24點(diǎn)算法中,存在36*18*6片葉子需要判斷。此外,24點(diǎn)中,4個(gè)數(shù)的狀態(tài)級(jí)數(shù)為3,可以預(yù)料到24點(diǎn)中狀態(tài)的級(jí)數(shù)比輸入?yún)?shù)的數(shù)目少1,你應(yīng)該知道WHY的。
                    由以上分析可知,每一級(jí)的狀態(tài),由3個(gè)參數(shù)決定,分別是第1個(gè)數(shù)a、第2個(gè)數(shù)b和運(yùn)算符號(hào)。運(yùn)算符號(hào)取值范圍為0-5,分別表示a+b,a-b,a*b,a/b,b-a,b/a這6種運(yùn)算。這3個(gè)參數(shù)是一個(gè)整體,代碼中用Step來(lái)表示,分別為nFst,nSnd,nOper。
            ……
                    忽略了思考過(guò)程,下面簡(jiǎn)單說(shuō)明代碼結(jié)構(gòu)。CGame24是主類,用以玩出24點(diǎn)游戲的解。其成員函數(shù)CalcNextResult()在內(nèi)部狀態(tài)中用輸入的數(shù)構(gòu)造出一個(gè)最終的表達(dá)式,這個(gè)表達(dá)式可能是正確的解,也可能不是。而Play()則通過(guò)調(diào)用不停地調(diào)用CalcNextResult()以嘗試得到一個(gè)正確的解。Express()則將表達(dá)式表示成字符串的形式。m_Nums用以儲(chǔ)存原始的輸入數(shù)據(jù)和中間運(yùn)算結(jié)果,其中最后的一個(gè)數(shù)為表達(dá)式的最終運(yùn)算結(jié)果。m_Flags用以指示m_Nums中的數(shù)是否已參與表達(dá)式中,以阻止同一個(gè)數(shù)多次進(jìn)入表達(dá)式中。
                    于是Step中的nFst,nSnd為m_Nums中的數(shù)的索引,很明顯,由于是組合關(guān)系,所以nSnd必將大于nFst。Step中還有一個(gè)nNext的變量,指向的是nSnd的下一個(gè)可用的索引,當(dāng)nOper為最后一種運(yùn)算時(shí),nSnd就要進(jìn)入到下一個(gè)位置了,也就是被賦予nNext的值。如果nNext沒(méi)有可用的值時(shí),就表示要改變nFst的下標(biāo)了。本來(lái)nNext的出現(xiàn)是為了將代碼寫(xiě)得好看一點(diǎn)而硬造出來(lái)的一個(gè)變量,但不料在后面,卻發(fā)揮了很重要的作用,簡(jiǎn)直是假如沒(méi)有它,代碼就沒(méi)法編了。
                    整片程序的壓力全在Step::ToNext()上,它所做的事情,不過(guò)是為了使?fàn)顟B(tài)進(jìn)入下一個(gè)狀態(tài)中。但是其實(shí)現(xiàn),卻異常復(fù)雜,要考慮組合的各種可能的情況,甚至還要考慮除數(shù)是否為0。承擔(dān)了太多的職責(zé),但是我也想不出更好的方式,也不打算再思考了。
                    好吧,我也承認(rèn)代碼寫(xiě)得異常糟糕,不過(guò),這只是玩具代碼,原本就不愿精雕細(xì)刻,它還存在好多不足之處,比如輸出結(jié)果中,有時(shí)會(huì)加入多余的括號(hào),這個(gè)問(wèn)題還能解決。然后,它還不夠智能,遍歷出來(lái)的一些解,其本質(zhì)上看還是相同,這個(gè)的解決就很有點(diǎn)難度了。此外,按抽象的觀點(diǎn)來(lái)看,回溯算法其實(shí)相當(dāng)于一個(gè)容器,它的循環(huán)遍歷葉子節(jié)點(diǎn)或者解,可看成迭代器,這種思路,完全可以表達(dá)成C++的代碼等等。如果讀者愿意優(yōu)化,請(qǐng)將優(yōu)化后的結(jié)果發(fā)給在下,在下也很想瞅瞅。其實(shí),我想說(shuō)的是,就算老夫親自操刀,也不見(jiàn)得就勝過(guò)一眾宵小了,慚愧。
                    算法這東西,現(xiàn)實(shí)中用得很少,高效復(fù)雜的算法自然有人在研究,我們只要注意將代碼寫(xiě)得清晰一點(diǎn)就好了。不過(guò),話又說(shuō)回來(lái),經(jīng)過(guò)各種算法狂轟濫炸后的大腦,編出來(lái)的代碼,貌似比沒(méi)有經(jīng)過(guò)算法折磨過(guò)的人,似乎總是要強(qiáng)一點(diǎn)。
            #include <stdio.h>
            #include 
            <math.h>
            #include 
            <assert.h>
            #include 
            <utility>
            #include 
            <iostream>

            using namespace std;

            unsigned 
            int gcd(unsigned int x, unsigned int y)   
            {   
                unsigned  
            int  nTimes=0;   
                
            for (; 0 == (x&1&& 0 == (y&1); x>>=1, y>>=1)
                    
            ++nTimes;

                
            if (x < y)
                    swap(x, y);

                
            while (y > 0)
                
            {
                    
            for (; 0 == (x & 1 );x >>= 1 )
                        ;   

                    
            if (x < y)
                        swap(x, y);
                    x 
            -= y;
                    
            if (x < y)
                        swap(x, y);
                }

                
            return x << nTimes;
            }
             


            class CRational
            {
            public:
                CRational(
            int nNumberator=0int nDenominator=1)
                    : m_nNum(nNumberator), m_nDe(nDenominator)
                
            {
                    assert(nDenominator 
            != 0);
                    standarlize();
                }

                
            int Numberator()const return m_nNum;}
                
            int Denominator()const return m_nDe;}

                CRational
            & operator+=(const CRational& _Right)
                
            {
                    m_nNum 
            = m_nNum*_Right.m_nDe + _Right.m_nNum*m_nDe;
                    m_nDe 
            *= _Right.m_nDe;
                    standarlize();
                    
            return *this;
                }


                CRational
            & operator-=(const CRational& _Right)
                
            {
                    m_nNum 
            = m_nNum*_Right.m_nDe - _Right.m_nNum*m_nDe;
                    m_nDe 
            *= _Right.m_nDe;
                    standarlize();
                    
            return *this;
                }


                CRational
            & operator*=(const CRational& _Right)
                
            {
                    m_nNum 
            *= _Right.m_nNum;
                    m_nDe 
            *= _Right.m_nDe;
                    standarlize();
                    
            return *this;
                }


                CRational
            & operator/=(const CRational& _Right)
                
            {
                    assert(_Right.Denominator() 
            != 0);
                    m_nNum 
            *= _Right.m_nDe;
                    m_nDe 
            *= _Right.m_nNum;
                    standarlize();
                    
            return *this;
                }


            private:
                
            void standarlize()
                
            {
                    
            if (m_nDe < 0)
                    
            {
                        m_nDe 
            = -m_nDe;
                        m_nNum 
            = -m_nNum;
                    }

                    
            int nGcd = gcd(abs(m_nNum), m_nDe);
                    m_nNum 
            /= nGcd;
                    m_nDe 
            /= nGcd;
                }

                
            int m_nNum;
                
            int m_nDe;
            }
            ;

            ostream
            & operator << (ostream& outconst CRational& rat)
            {
                cout 
            << rat.Numberator();
                
            if (rat.Denominator() != 1)
                    cout 
            << "/" << rat.Denominator();
                
            return out;
            }


            CRational 
            operator-(const CRational& _Left, const CRational& _Right)
            {
                CRational _Tmp(_Left);
                
            return _Tmp -= _Right;
            }


            CRational 
            operator+(const CRational& _Left, const CRational& _Right)
            {
                CRational _Tmp(_Left);
                
            return _Tmp += _Right;
            }


            CRational 
            operator*(const CRational& _Left, const CRational& _Right)
            {
                CRational _Tmp(_Left);
                
            return _Tmp *= _Right;
            }


            CRational 
            operator/(const CRational& _Left, const CRational& _Right)
            {
                CRational _Tmp(_Left);
                
            return _Tmp /= _Right;
            }


            bool operator==(const CRational& _Left, const CRational& _Right)
            {
                
            return _Left.Numberator()==_Right.Numberator() && _Left.Denominator()==_Right.Denominator();
            }


            enum OperType{ OPER_ADD, OPER_SUB1, OPER_MUL, OPER_DIV1, OPER_SUB2, OPER_DIV2};

            const char* g_sOPER_SYMBOL = "+-*/-/";
            class CGame24
            {
            public:
                CGame24(
            int nRes, int* pNums, int nLen);

                
            bool Play();
                
            bool CalcNextResult();
                size_t Express(
            char* pExp);

            private:
                
            struct Step
                
            {
                    
            char nOper;
                    
            char nFst;
                    
            char nSnd;
                    
            char nNext;

                    
            void ToNext(bool* pFlags, const CRational* pNums, int nMax);

                    
            bool HasNext(const bool* pFlags, int nMax)
                    
            {
                        
            if (nNext >= nMax)
                        
            {
                            
            int nCount = 0;
                            
            for (char i = nFst+1; i < nSnd && nCount<2; i++)
                            
            {
                                
            if (!pFlags[i])
                                    nCount
            ++;
                            }

                            
            return nCount == 2;
                        }

                        
            return true;
                    }


                    
            void Discard(bool* pFlags)
                    
            {
                        pFlags[nFst] 
            = false;
                        pFlags[nSnd] 
            = false;
                        nFst 
            = 0;
                        nSnd 
            = 0;
                        nNext 
            = 0;
                    }

                }
            ;

                size_t buildExpress(
            char* pExp, char nStep, char nSuperOper);

                
            enum {_nSIZE = 100};
                CRational m_Nums[_nSIZE
            *2];
                
            bool m_Flags[_nSIZE*2];
                Step m_Steps[_nSIZE];
                
            int m_nRes;
                
            char m_nLen;
                
            char m_nCur;
            }
            ;

            void CGame24::Step::ToNext(bool* pFlags, const CRational* pNums, int nMax)
            {
                assert(HasNext(pFlags, nMax));
                
            if (nNext == nMax)
                
            {
                    pFlags[nFst] 
            = false;
                    pFlags[nSnd] 
            = false;
                    nOper 
            = 0;
                    nNext 
            = 0;
                    nFst
            ++;
                    nSnd 
            = nFst;
                }

                
            if (nFst >= nSnd)
                
            {
                    
            for (; nFst<nMax-1 && pFlags[nFst]; nFst++)
                        ;
                    nOper 
            = 0;
                    pFlags[nFst] 
            = true;
                    nSnd 
            = nFst;
                    
            for (nNext = nFst+1; nNext<nMax && pFlags[nNext]; nNext++)
                        ;
                    assert (nNext 
            != nMax);
                }


                
            if (nNext > nSnd)
                
            {
                    assert(
            !pFlags[nNext]);
                    
            if (nSnd != nFst)
                        pFlags[nSnd] 
            = false;
                    nSnd 
            = nNext;
                    pFlags[nSnd] 
            = true;
                    nOper 
            = 0;
                    
            return;
                }

                nOper
            ++;
                
            if (nOper==OPER_DIV1 && pNums[nSnd].Numberator()==0)
                    nOper
            ++;
                
            char nNextOper = nOper+1;
                
            if (nNextOper>OPER_MUL)
                
            {
                    
            if (nNextOper == OPER_DIV1 && pNums[nSnd].Numberator()==0)
                        nNextOper
            ++;
                    
            if (nNextOper == OPER_DIV2 && pNums[nFst].Numberator()==0)
                        nNextOper
            ++;
                }


                
            if (nNextOper > OPER_DIV2)
                
            {
                    
            for (nNext=nSnd+1; nNext<nMax && pFlags[nNext]; nNext++)
                        ;
                }

            }


            CRational OperateRationals(
            const CRational& fst, const CRational& snd, char nOper)
            {
                
            switch (nOper)
                
            {
                
            case OPER_ADD: return fst + snd; 
                
            case OPER_SUB1: return fst - snd; 
                
            case OPER_SUB2: return snd - fst; 
                
            case OPER_MUL: return fst * snd; 

                
            case OPER_DIV1: 
                    assert (snd.Numberator() 
            != 0);
                    
            return fst/snd;

                
            case OPER_DIV2: 
                    assert (fst.Numberator() 
            != 0);
                    
            return snd/fst;
                }

                assert (
            false);
                
            return 0;
            }


            CGame24::CGame24(
            int nRes, int* pNums, int nLen)
            {
                assert(nLen 
            > 0 && nLen < _nSIZE);
                m_nRes 
            = nRes;
                
            for (int i=0; i<nLen; i++)
                    m_Nums[i] 
            = pNums[i];
                memset(m_Flags, 
            0sizeof(m_Flags));
                memset(m_Steps, 
            0sizeof(m_Steps));
                m_nLen 
            = static_cast<char>(nLen);
                m_nCur 
            = 0;
            }


            bool CGame24::CalcNextResult()
            {
                
            while (m_nCur >= 0 && !m_Steps[m_nCur].HasNext(m_Flags, m_nLen+m_nCur))
                    m_Steps[m_nCur
            --].Discard(m_Flags);
                
            if (m_nCur < 0)
                    
            return false;

                
            while (m_nCur < m_nLen-1)
                
            {
                    m_Steps[m_nCur].ToNext(m_Flags, m_Nums, m_nLen
            +m_nCur);
                    
            const Step& step = m_Steps[m_nCur];
                    m_Nums[m_nLen
            +m_nCur] = OperateRationals(m_Nums[step.nFst], m_Nums[step.nSnd], step.nOper);
                    m_nCur
            ++;
                }

                m_nCur
            --;

                
            return true;
            }


            bool CGame24::Play()
            {
                
            while (CalcNextResult())
                
            {
                    
            if (m_Nums[m_nLen+m_nCur] == m_nRes)
                        
            return true;
                }

                
            return false;
            }


            size_t CGame24::Express(
            char* pExp)
            {
                size_t len 
            = buildExpress(pExp, m_nCur, OPER_ADD); // 加法的運(yùn)算級(jí)別最低
                pExp[len] = 0;
                
            return len;
            }


            bool NeedParentheses(char nSuperOper, char nSubOper)
            {
                assert(nSuperOper 
            <= OPER_DIV2);
                assert(nSubOper 
            <= OPER_DIV2);
                
            static const char g_ACTUAL_OPER[] = {OPER_ADD, OPER_SUB1, OPER_MUL, OPER_DIV1, OPER_SUB1, OPER_DIV1};
                nSuperOper 
            = g_ACTUAL_OPER[nSuperOper];
                nSubOper 
            = g_ACTUAL_OPER[nSubOper];
                
            return (nSuperOper>nSubOper) || (nSuperOper==nSubOper && (nSubOper==OPER_SUB1 || nSuperOper==OPER_DIV1));
            }


            size_t CGame24::buildExpress(
            char* pExp, char nStep, char nSuperOper)
            {
                assert(nStep 
            <= m_nCur);
                
            char* sPos = pExp;
                
            const Step& step = m_Steps[nStep];
                
            char nFst = step.nFst;
                
            char nSnd = step.nSnd;
                
            char nOper = step.nOper;
                
            if(step.nOper==OPER_SUB2 || step.nOper==OPER_DIV2)
                    swap(nFst, nSnd);

                
            bool bParentheses = NeedParentheses(nSuperOper, nOper);
                
            if (bParentheses)
                    
            *sPos++ = '(';
                
            if (nFst >= m_nLen)
                    sPos 
            += buildExpress(sPos, nFst-m_nLen, nOper);
                
            else
                    sPos 
            += sprintf(sPos, ("%d"), m_Nums[nFst].Numberator());

                
            *sPos++ = g_sOPER_SYMBOL[nOper];

                
            if (nSnd >= m_nLen)
                    sPos 
            += buildExpress(sPos, nSnd-m_nLen, nOper);
                
            else
                    sPos 
            += sprintf(sPos, ("%d"), m_Nums[nSnd].Numberator());

                
            if (bParentheses)
                    
            *sPos++ = ')';
                
            return sPos-pExp;
            }


            int main()
            {
                
            char sExpress[256= 0 };
                
            int Nums[] = {12345};
                CGame24 game(
            24, Nums, 5);
                
            while (game.Play())
                
            {
                    game.Express(sExpress);
                    cout 
            << sExpress << endl;
                }

                
            return 0;
            }

            posted on 2012-06-07 16:20 華夏之火 閱讀(1879) 評(píng)論(1)  編輯 收藏 引用 所屬分類: 玩具代碼

            評(píng)論

            # re: 玩具代碼 24點(diǎn)游戲 2012-06-15 14:29 ss

            可以運(yùn)行嗎  回復(fù)  更多評(píng)論   

            導(dǎo)航

            統(tǒng)計(jì)

            常用鏈接

            留言簿(6)

            隨筆分類

            隨筆檔案

            搜索

            積分與排名

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久久久亚洲av成人网人人软件| 久久亚洲国产最新网站| 99国产精品久久| 激情久久久久久久久久| 久久亚洲国产成人影院网站| 99久久国产精品免费一区二区| 久久婷婷五月综合国产尤物app| 国产福利电影一区二区三区久久久久成人精品综合 | 夜夜亚洲天天久久| 免费无码国产欧美久久18| 久久精品国产亚洲AV电影| 91亚洲国产成人久久精品网址| 久久中文字幕人妻丝袜| 中文字幕成人精品久久不卡| 无码国内精品久久综合88| 国内精品久久久久久久涩爱| 久久综合给合久久狠狠狠97色| 久久无码精品一区二区三区| 久久99热国产这有精品| 少妇精品久久久一区二区三区| 伊人久久亚洲综合影院| 久久www免费人成精品香蕉| 国产精品久久成人影院| 欧美亚洲国产精品久久高清| 久久久噜噜噜久久中文字幕色伊伊| 国产Av激情久久无码天堂| 伊人久久大香线蕉av不卡| 亚洲欧美成人久久综合中文网| .精品久久久麻豆国产精品| 亚洲午夜精品久久久久久浪潮| 一本一道久久精品综合| 久久99精品国产麻豆| 久久99国产综合精品免费| 久久精品一区二区三区AV| 一本久久a久久精品亚洲| 久久精品国产亚洲AV久| 中文精品久久久久人妻不卡| 久久精品国产亚洲AV久| 久久精品国产亚洲AV无码麻豆 | 污污内射久久一区二区欧美日韩| 国产女人aaa级久久久级|