青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Creative Commons License
本Blog采用 知識共享署名-非商業(yè)性使用-禁止演繹 3.0 Unported許可協(xié)議 進行許可。 —— Fox <游戲人生>

游戲人生

游戲人生 != ( 人生 == 游戲 )
站點遷移至:http://www.yulefox.com。請訂閱本博的朋友將RSS修改為http://feeds.feedburner.com/yulefox
posts - 62, comments - 508, trackbacks - 0, articles - 7

以前在學(xué)習(xí)非數(shù)值算法的時候,曾經(jīng)了解過動態(tài)規(guī)劃算法(Dynamic programming),以下是對Wikipedia動態(tài)規(guī)劃的翻譯,圖也是Wikipedia上的,倉促行文,不到之處,請方家指正。

這篇文章的術(shù)語實在是太多了,所以我在文中加入了少量注釋,一律以粗斜體注明。

本文的不足之處將隨時修正,MIT的《Introduction to Algorithms》第15章是專門講動態(tài)規(guī)劃的。

_____________________________________________________________

動態(tài)規(guī)劃

在數(shù)學(xué)與計算機科學(xué)領(lǐng)域,動態(tài)規(guī)劃用于解決那些可分解為重復(fù)子問題(overlapping subproblems,想想遞歸求階乘吧)并具有最優(yōu)子結(jié)構(gòu)(optimal substructure,想想最短路徑算法)(如下所述)的問題,動態(tài)規(guī)劃比通常算法花費更少時間。

上世紀(jì)40年代,Richard Bellman最早使用動態(tài)規(guī)劃這一概念表述通過遍歷尋找最優(yōu)決策解問題的求解過程。1953年,Richard Bellman將動態(tài)規(guī)劃賦予現(xiàn)代意義,該領(lǐng)域被IEEE納入系統(tǒng)分析和工程中。為紀(jì)念Bellman的貢獻,動態(tài)規(guī)劃的核心方程被命名為貝爾曼方程,該方程以遞歸形式重申了一個優(yōu)化問題。

在“動態(tài)規(guī)劃”(dynamic programming)一詞中,programming與“計算機編程”(computer programming)中的programming并無關(guān)聯(lián),而是來自“數(shù)學(xué)規(guī)劃”(mathematical programming),也稱優(yōu)化。因此,規(guī)劃是指對生成活動的優(yōu)化策略。舉個例子,編制一場展覽的日程可稱為規(guī)劃。 在此意義上,規(guī)劃意味著找到一個可行的活動計劃。

  • 概述

 

圖1 使用最優(yōu)子結(jié)構(gòu)尋找最短路徑:直線表示邊,波狀線表示兩頂點間的最短路徑(路徑中其他節(jié)點未顯示);粗線表示從起點到終點的最短路徑。

不難看出,start到goal的最短路徑由start的相鄰節(jié)點到goal的最短路徑及start到其相鄰節(jié)點的成本決定。

 

 

最優(yōu)子結(jié)構(gòu)即可用來尋找整個問題最優(yōu)解的子問題的最優(yōu)解。舉例來說,尋找上某頂點到終點的最短路徑,可先計算該頂點所有相鄰頂點至終點的最短路徑,然后以此來選擇最佳整體路徑,如圖1所示。

一般而言,最優(yōu)子結(jié)構(gòu)通過如下三個步驟解決問題:

a) 將問題分解成較小的子問題;

b) 通過遞歸使用這三個步驟求出子問題的最優(yōu)解;

c) 使用這些最優(yōu)解構(gòu)造初始問題的最優(yōu)解。

子問題的求解是通過不斷劃分為更小的子問題實現(xiàn)的,直至我們可以在常數(shù)時間內(nèi)求解。

 

 

圖2 Fibonacci序列的子問題示意圖:使用有向無環(huán)圖(DAG, directed acyclic graph)而非表示重復(fù)子問題的分解。

為什么是DAG而不是樹呢?答案就是,如果是樹的話,會有很多重復(fù)計算,下面有相關(guān)的解釋。

 

 

一個問題可劃分為重復(fù)子問題是指通過相同的子問題可以解決不同的較大問題。例如,在Fibonacci序列中,F(xiàn)3 = F1 + F2和F4 = F2 + F3都包含計算F2。由于計算F5需要計算F3和F4,一個比較笨的計算F5的方法可能會重復(fù)計算F2兩次甚至兩次以上。這一點對所有重復(fù)子問題都適用:愚蠢的做法可能會為重復(fù)計算已經(jīng)解決的最優(yōu)子問題的解而浪費時間。

為避免重復(fù)計算,可將已經(jīng)得到的子問題的解保存起來,當(dāng)我們要解決相同的子問題時,重用即可。該方法即所謂的緩存(memoization,而不是存儲memorization,雖然這個詞亦適合,姑且這么叫吧,這個單詞太難翻譯了,簡直就是可意會不可言傳,其意義是沒計算過則計算,計算過則保存)。當(dāng)我們確信將不會再需要某一解時,可以將其拋棄,以節(jié)省空間。在某些情況下,我們甚至可以提前計算出那些將來會用到的子問題的解。

總括而言,動態(tài)規(guī)劃利用:

1) 重復(fù)子問題

2) 最優(yōu)子結(jié)構(gòu)

3) 緩存

動態(tài)規(guī)劃通常采用以下兩種方式中的一種兩個辦法:

自頂向下:將問題劃分為若干子問題,求解這些子問題并保存結(jié)果以免重復(fù)計算。該方法將遞歸和緩存結(jié)合在一起。

自下而上:先行求解所有可能用到的子問題,然后用其構(gòu)造更大問題的解。該方法在節(jié)省堆棧空間和減少函數(shù)調(diào)用數(shù)量上略有優(yōu)勢,但有時想找出給定問題的所有子問題并不那么直觀。

為了提高按名傳遞(call-by-name,這一機制與按需傳遞call-by-need相關(guān),復(fù)習(xí)一下參數(shù)傳遞的各種規(guī)則吧,簡單說一下,按名傳遞允許改變實參值)的效率,一些編程語言將函數(shù)的返回值“自動”緩存在函數(shù)的特定參數(shù)集合中。一些語言將這一特性盡可能簡化(如SchemeCommon LispPerl),也有一些語言需要進行特殊擴展(如C++,C++中使用的是按值傳遞和按引用傳遞,因此C++中本無自動緩存機制,需自行實現(xiàn),具體實現(xiàn)的一個例子是Automated Memoization in C++)。無論如何,只有指稱透明(referentially transparent,指稱透明是指在程序中使用表達式、函數(shù)本身或以其值替換對程序結(jié)果沒有任何影響)函數(shù)才具有這一特性。

  • 例子

1. Fibonacci序列

尋找Fibonacci序列中第n個數(shù),基于其數(shù)學(xué)定義的直接實現(xiàn):

   function fib(n)
       if n = 0
           return 0
       else if n = 1
           return 1
       return fib(n-1) + fib(n-2)

如果我們調(diào)用fib(5),將產(chǎn)生一棵對于同一值重復(fù)計算多次的調(diào)用樹:

  1. fib(5)
  2. fib(4) + fib(3)
  3. (fib(3) + fib(2)) + (fib(2) + fib(1))
  4. ((fib(2) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
  5. (((fib(1) + fib(0)) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))

特別是,fib(2)計算了3次。在更大規(guī)模的例子中,還有更多fib的值被重復(fù)計算,將消耗指數(shù)級時間。

現(xiàn)在,假設(shè)我們有一個簡單的映射(map)對象m,為每一個計算過的fib及其返回值建立映射,修改上面的函數(shù)fib,使用并不斷更新m。新的函數(shù)將只需O(n)的時間,而非指數(shù)時間:

   var m := map(0 → 1, 1 → 1)
   function fib(n)
       if map m does not contain key n
           m[n] := fib(n-1) + fib(n-2)
       return m[n]

這一保存已計算出的數(shù)值的技術(shù)即被稱為緩存,這兒使用的是自頂向下的方法:先將問題劃分為若干子問題,然后計算和存儲值。

自下而上的方法中,我們先計算較小的fib,然后基于其計算更大的fib。這種方法也只花費線性(O(n))時間,因為它包含一個n-1次的循環(huán)。然而,這一方法只需要常數(shù)(O(1))的空間,相反,自頂向下的方法則需要O(n)的空間來儲存映射關(guān)系。

   function fib(n)
       var previousFib := 0, currentFib := 1
       if n = 0
           return 0
       else if n = 1
           return 1
       repeat n-1 times
           var newFib := previousFib + currentFib
           previousFib := currentFib
           currentFib  := newFib
       return currentFib

在這兩個例子,我們都只計算fib(2)一次,然后用它來計算fib(3)和fib(4),而不是每次都重新計算。

2. 一種平衡的0-1矩陣

考慮n*n矩陣的賦值問題:只能賦0和1,n為偶數(shù),使每一行和列均含n/2個0及n/2個1。例如,當(dāng)n=4時,兩種可能的方案是:

+ - - - - +             + - - - - +
| 0 1 0 1 |             | 0 0 1 1 |
| 1 0 1 0 |             | 0 0 1 1 |
| 0 1 0 1 |             | 1 1 0 0 |
| 1 0 1 0 |             | 1 1 0 0 |
+ - - - - +             + - - - - +

問:對于給定n,共有多少種不同的賦值方案。

至少有三種可能的算法來解決這一問題:窮舉法(brute force)、回溯法(backtracking)及動態(tài)規(guī)劃(dynamic programming)。窮舉法列舉所有賦值方案,并逐一找出滿足平衡條件的方案。由于共有C(n, n/2)^n種方案(在一行中,含n/2個0及n/2個1的組合數(shù)為C(n,n/2),相當(dāng)于從n個位置中選取n/2個位置置0,剩下的自然是1),當(dāng)n=6時,窮舉法就已經(jīng)幾乎不可行了。回溯法先將矩陣中部分元素置為0或1,然后檢查每一行和列中未被賦值的元素并賦值,使其滿足每一行和列中0和1的數(shù)量均為n/2。回溯法比窮舉法更加巧妙一些,但仍需遍歷所有解才能確定解的數(shù)目,可以看到,當(dāng)n=8時,該題解的數(shù)目已經(jīng)高達116963796250。動態(tài)規(guī)劃則無需遍歷所有解便可確定解的數(shù)目(意思是劃分子問題后,可有效避免若干子問題的重復(fù)計算)。

通過動態(tài)規(guī)劃求解該問題出乎意料的簡單。考慮每一行恰含n/2個0和n/2個1的k*n(1<=k<=n)的子矩陣,函數(shù)f根據(jù)每一行的可能的賦值映射為一個向量,每個向量由n個整數(shù)對構(gòu)成。向量每一列對應(yīng)的一個整數(shù)對中的兩個整數(shù)分別表示該列上該行以下已經(jīng)放置的0和1的數(shù)量。該問題即轉(zhuǎn)化為尋找f((n/2,n/2),(n/2,n/2),...,(n/2,n/2))(具有n個參數(shù)或者說是一個含n個元素的向量)的值。其子問題的構(gòu)造過程如下:

1) 最上面一行(第k行)具有C(n, n/2)種賦值;

2) 根據(jù)最上面一行中每一列的賦值情況(為0或1),將其對應(yīng)整數(shù)對中相應(yīng)的元素值減1;

3) 如果任一整數(shù)對中的任一元素為負(fù),則該賦值非法,不能成為正確解;

4) 否則,完成對k*n的子矩陣中最上面一行的賦值,取k=k-1,計算剩余的(k-1)*n的子矩陣的賦值;

5) 基本情況是一個1*n的細(xì)小的子問題,此時,該子問題的解的數(shù)量為0或1,取決于其向量是否是n/2個(0, 1)和n/2個(1, 0)的排列。

例如,在上面給出的兩種方案中,向量序列為:

((2, 2) (2, 2) (2, 2) (2, 2))       ((2, 2) (2, 2) (2, 2) (2, 2))     k = 4
  0      1      0      1              0       0       1      1

((1, 2) (2, 1) (1, 2) (2, 1))       ((1, 2) (1, 2) (2, 1) (2, 1))     k = 3
  1      0      1      0              0      0      1      1

((1, 1) (1, 1) (1, 1) (1, 1))       ((0, 2) (0, 2) (2, 0) (2, 0))     k = 2
  0      1      0      1              1      1      0      0

((0, 1) (1, 0) (0, 1) (1, 0))       ((0, 1) (0, 1) (1, 0) (1, 0))     k = 1
  1      0      1      0              1      1      0      0

((0, 0) (0, 0) (0, 0) (0, 0))       ((0, 0) (0, 0), (0, 0) (0, 0))

動態(tài)規(guī)劃在此的意義在于避免了相同f的重復(fù)計算,更進一步的,上面著色的兩個f,雖然對應(yīng)向量不同,但f的值是相同的,想想為什么吧:D

該問題解的數(shù)量(序列a058527在OEIS)是1, 2, 90, 297200, 116963796250, 6736218287430460752, ...

下面的外部鏈接中包含回溯法的Perl源代碼實現(xiàn),以及動態(tài)規(guī)劃法的MAPLE和C語言的實現(xiàn)。

3. 棋盤

考慮n*n的棋盤及成本函數(shù)C(i,j),該函數(shù)返回方格(i,j)相關(guān)的成本。以5*5的棋盤為例:

5 | 6 7 4 7 8
4 | 7 6 1 1 4
3 | 3 5 7 8 2
2 | 2 6 7 0 2
1 | 7 3 5 6 1
- + - - - - -
  | 1 2 3 4 5

可以看到:C(1,3)=5

從棋盤的任一方格的第一階(即行)開始,尋找到達最后一階的最短路徑(使所有經(jīng)過的方格的成本之和最小),假定只允許向左對角、右對角或垂直移動一格。

5 |
4 |
3 |
2 |   x x x
1 |     o
- + - - - - -
  | 1 2 3 4 5

該問題展示了最優(yōu)子結(jié)構(gòu)。即整個問題的全局解依賴于子問題的解。定義函數(shù)q(i,j),令:q(i,j)表示到達方格(i,j)的最低成本。

如果我們可以求出第n階所有方格的q(i,j)值,取其最小值并逆向該路徑即可得到最短路徑。

記q(i,j)為方格(i,j)至其下三個方格((i-1,j-1)、(i-1,j)、(i-1,j+1))最低成本與c(i,j)之和,例如:

5 |
4 |     A
3 |   B C D
2 |
1 |
- + - - - - -
  | 1 2 3 4 5

q(A) = min(q(B),q(C),q(D)) + c(A)

定義q(i,j)的一般形式:

            |-  inf.                                                  j<1 or j>n
q(i,j) = -+-  c(i,j)                                                i=1
            |-  min(q(i-1,j-1),q(i-1,j),q(i-1,j+1))+c(i,j)   otherwise.

方程的第一行是為了保證遞歸可以退出(處理邊界時只需調(diào)用一次遞歸函數(shù))。第二行是第一階的取值,作為計算的起點。第三行的遞歸是算法的重要組成部分,與例子ABCD類似。從該定義我們可以直接給出計算q(i,j)的簡單的遞歸代碼。在下面的偽代碼中,n表示棋盤的維數(shù),C(i,j)是成本函數(shù),min()返回一組數(shù)的最小值:

function minCost(i, j)
    if j < 1 or j > n
        return infinity
    else if i = 1
        return c(i,j)
    else
        return min(minCost(i-1,j-1),minCost(i-1,j),minCost(i-1,j+1))+c(i,j)

需要指出的是,minCost只計算路徑成本,并不是最終的實際路徑,二者相去不遠(yuǎn)。與Fibonacci數(shù)相似,由于花費大量時間重復(fù)計算相同的最短路徑,這一方式慢的恐怖。不過,如果采用自下而上法,使用二維數(shù)組q[i,j]代替函數(shù)minCost,將使計算過程快得多。我們?yōu)槭裁匆@樣做呢?選擇保存值顯然比使用函數(shù)重復(fù)計算相同路徑要簡單的多。

我們還需要知道實際路徑。路徑問題,我們可以通過另一個前任數(shù)組p[i,j]解決。這個數(shù)組用于描述路徑,代碼如下:

function computeShortestPathArrays()
     for x from 1 to n
         q[1, x] := c(1, x)
     for y from 1 to n
         q[y, 0]     := infinity
         q[y, n + 1] := infinity
     for y from 2 to n
         for x from 1 to n
             m := min(q[y-1, x-1], q[y-1, x], q[y-1, x+1])
             q[y, x] := m + c(y, x)
             if m = q[y-1, x-1]
                 p[y, x] := -1
             else if m = q[y-1, x]
                 p[y, x] :=  0
             else
                 p[y, x] :=  1

剩下的求最小值和輸出就比較簡單了:

function computeShortestPath()
     computeShortestPathArrays()
     minIndex := 1
     min := q[n, 1]
     for i from 2 to n
         if q[n, i] < min
             minIndex := i
             min := q[n, i]
     printPath(n, minIndex)

function printPath(y, x)
     print(x)
     print("<-")
     if y = 2
         print(x + p[y, x])
     else
         printPath(y-1, x + p[y, x])

4. 序列比對

序列比對是動態(tài)規(guī)劃的一個重要應(yīng)用。序列比對問題通常是使用編輯操作(替換、插入、刪除一個要素等)進行序列轉(zhuǎn)換。每次操作對應(yīng)不同成本,目標(biāo)是找到編輯序列的最低成本。

可以很自然地想到使用遞歸解決這個問題,序列AB的最優(yōu)編輯通過以下措施之一實現(xiàn):

插入B的第一個字符,對AB的剩余序列進行最優(yōu)比對;

刪去A的第一個字符,對AB進行最優(yōu)比對;

B的第一個字符替換A的第一個字符,對A的剩余序列和B進行最優(yōu)比對。

局部比對可在矩陣中列表表示,單元(i,j)表示A[1..i]到b[1..j]最優(yōu)比對的成本。單元(i,j)的成本計算可通過累加相鄰單元的操作成本并選擇最優(yōu)解實現(xiàn)。至于序列比對的不同實現(xiàn)算法,參見Smith-WatermanNeedleman-Wunsch

對序列比對的話題并不熟悉,更多的話也無從談起,有熟悉的朋友倒是可以介紹一下。

  • 應(yīng)用動態(tài)規(guī)劃的算法

1) 許多字符串操作算法如最長公共子列最長遞增子列最長公共字串

2) 將動態(tài)規(guī)劃用于的樹分解,可以有效解決有界樹寬圖生成樹等許多與圖相關(guān)的算法問題;

3) 決定是否及如何可以通過某一特定上下文無關(guān)文法產(chǎn)生給定字符串的Cocke-Younger-Kasami (CYK)算法;

4) 計算機國際象棋轉(zhuǎn)換表駁斥表的使用;

5) Viterbi算法(用于隱式馬爾可夫模型);

6) Earley算法(一類圖表分析器);

7) Needleman-Wunsch及其他生物信息學(xué)中使用的算法,包括序列比對結(jié)構(gòu)比對RNA結(jié)構(gòu)預(yù)測;

8) Levenshtein距離(編輯距離);

9) 弗洛伊德最短路徑算法;

10) 連鎖矩陣乘法次序優(yōu)化;

11) 子集求和背包問題分治問題的偽多項式時間算法;

12) 計算兩個時間序列全局距離的動態(tài)時間規(guī)整算法;

13) 關(guān)系型數(shù)據(jù)庫的查詢優(yōu)化的Selinger(又名System R)算法;

14) 評價B樣條曲線的De Boor算法

15) 用于解決板球運動中斷問題的Duckworth-Lewis方法;

16) 價值迭代法求解馬爾可夫決策過程

17) 一些圖形圖像邊緣以下的選擇方法,如“磁鐵”選擇工具在Photoshop

18) 間隔調(diào)度

19) 自動換行

20) 巡回旅行商問題又稱郵差問題或貨擔(dān)郎問題);

21) 分段最小二乘法

22) 音樂信息檢索跟蹤。

對于這些算法應(yīng)用,大多未曾接觸,甚至術(shù)語翻譯的都有問題,鑒于本文主要在于介紹動態(tài)規(guī)劃,所以倉促之中,未及查證。

  • 相關(guān)

1) 貝爾曼方程

2) 馬爾可夫決策過程

3) 貪心算法

  • 參考
  • Adda, Jerome, and Cooper, Russell, 2003. Dynamic Economics. MIT Press. An accessible introduction to dynamic programming in economics. The link contains sample programs.
  • Richard Bellman, 1957, Dynamic Programming, Princeton University Press. Dover paperback edition (2003), ISBN 0486428095.
  • Bertsekas, D. P., 2000. Dynamic Programming and Optimal Control, Vols. 1 & 2, 2nd ed. Athena Scientific. ISBN 1-886529-09-4.
  • Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein, 2001. Introduction to Algorithms, 2nd ed. MIT Press & McGraw-Hill. ISBN 0-262-03293-7. Especially pp. 323–69.
  • Giegerich, R., Meyer, C., and Steffen, P., 2004, "A Discipline of Dynamic Programming over Sequence Data," Science of Computer Programming 51: 215-263.
  • Nancy Stokey, and Robert E. Lucas, with Edward Prescott, 1989. Recursive Methods in Economic Dynamics. Harvard Univ. Press.
  • S. P. Meyn, 2007. Control Techniques for Complex Networks, Cambridge University Press, 2007.
    • 外部鏈接

    _____________________________________________________________

    關(guān)于動態(tài)規(guī)劃,這只是一篇譯文,后面將根據(jù)實際問題具體寫點動態(tài)規(guī)劃的應(yīng)用。

  • posted @ 2008-05-07 20:43 Fox 閱讀(31171) | 評論 (9)編輯 收藏

    Author: Fox

    首先聲明:本人沒有解決掉這個問題。

    相比第一道讓CPU占用率曲線聽你指揮對Windows系統(tǒng)中CPU占有率概念的考察和對API的使用,以及第二道中國象棋將帥問題對抽象建模的考察。這道題目才算是一道算法題吧?之前那兩道尤其是中國象棋將帥問題總有點腦筋急轉(zhuǎn)彎的味道。

    題目:星期五的晚上,一幫同事在希格瑪大廈附近的“硬盤酒吧”多喝了幾杯。程序員多喝了幾杯之后談什么呢?自然是算法問題。有個同事說:

    “我以前在餐館打工,顧客經(jīng)常點非常多的烙餅。店里的餅大小不一,我習(xí)慣在到達顧客飯桌前,把一摞餅按照大小次序擺好——小的在上面,大的在下面。由于我一只手托著盤子,只好用另一只手,一次抓住最上面的幾塊餅,把它們上下顛倒個個兒,反復(fù)幾次之后,這摞烙餅就排好序了。

    我后來想,這實際上是個有趣的排序問題:假設(shè)有n塊大小不一的烙餅,那最少要翻幾次,才能達到最后大小有序的結(jié)果呢?

    你能否寫出一個程序,對于n塊大小不一的烙餅,輸出最優(yōu)化的翻餅過程呢?

    排序問題是數(shù)據(jù)結(jié)構(gòu)和算法中比較重要的一個了,之前在一篇被同事成為標(biāo)題黨的文章中因為提到排序中關(guān)于(非)穩(wěn)定排序的概念還被很多TX鄙視了一番,甚至引來人身攻擊,現(xiàn)在想來都有些后怕。

    這道題目一眼看上去最先讓我想到的居然是那道經(jīng)典的漢諾塔(Tower of Hanoi)問題(當(dāng)然,漢諾塔不算排序問題)。

    1) 相似點★:

    a. 都要不斷調(diào)整位置,最終實現(xiàn)從小到大排好;

    b. 都要借助中間量進行調(diào)整。

    2) 不同處★:

    a. 漢諾塔有多出來的兩根針,翻烙餅只有一只手,明確說明沒有第三只手;

    b. 漢諾塔一次只能移動一個,翻烙餅沒限制;

    c. 漢諾塔要保證小的始終在上面,翻烙餅沒限制;

    d. 漢諾塔移動之前就有序,所以其移動次數(shù)是固定的,算法的邏輯也固定(不管是遞歸還是棧操作),翻烙餅沒有這個前提。

    3) 把題目用程序語言描述一下吧★:

    a. Input : n.

    b. Process : generate n random number 0-(n-1), sortting.

    c. Output : 0, 1, ..., n-1, and move times num.

    4) 存儲結(jié)構(gòu)★★★:

    我最開始想到的是:這一摞烙餅其實就是一個雙鏈表,每翻一次相當(dāng)于將頭節(jié)點H與某非頭節(jié)點N進行如下變換:

    H->next = N->next

    N->prior = H->prior = NULL

    N->next->prior = H

    如果使用普通的雙鏈表,這兒就有一個很明顯的問題,H和N之間的所有節(jié)點(如果有的話)的前趨prior和后繼next互換了,對于n比較大的情況,這個操作明顯浪費時間啊(交換前趨prior和后繼next和題目要求的翻幾次似乎也沒有關(guān)系吧?只是我作為一個一線的Coder考慮的太具體了)。如果摒棄前趨和后繼的概念,又該怎樣描述呢?

    唐朝禪宗大師青原行思曾說:參禪之初,看山是山,看水是水;禪有悟時,看山不是山,看水不是水;禪中徹悟,看山仍然山,看水仍然是水

    俗:日,扯那么多,什么意思?

    師:前趨不是前趨,后繼不是后繼。

    俗:日,前趨不是前趨,難道是后繼嗎?

    師:然也。

    Fox:O, my God!整點實際的吧!翻轉(zhuǎn)一次之后,前趨視為后繼,后繼視為前趨,第奇數(shù)次翻轉(zhuǎn)的前趨是后繼,第偶數(shù)次翻轉(zhuǎn)的后繼是前趨。

    整個鏈表的形態(tài)如下:

    H:Head, F:First, G:F's next, B:C's prior, C:Change, D, C's next, L:Last.

    H<==>F<==>G<=...=>B<==>C<==>D<=...=>L

    F與C交換的操作如下(Word、PS畫圖),n表示next,p表示prior:

    這里只需要修改F、D節(jié)點的prior,H、C節(jié)點的next,其他節(jié)點不變。

    后面想了一下,這種方式很難在不添加flag或者對換n、p的情況下正常操作,沒有找到好的方法L如果你有好的方法不妨告訴我

    最后只好作罷,何苦呢?直接使用數(shù)組就完了嘛J!既然是數(shù)組,除了翻轉(zhuǎn)移動麻煩一點,理解和操作還是很容易的。

    果然不是搞數(shù)學(xué)、算法出身的,一上來考慮的就是如何存儲^.^'''',而不是算法本身。

    更可笑的是,扯了半天,最后居然還是用數(shù)組

    5) 算法分析★★★★★:

    冒泡排序思想:

    最關(guān)鍵的是要抽象出隨機數(shù)列特征(含當(dāng)前和移動后數(shù)列特征的抽象),并盡量使每一次翻轉(zhuǎn)有效(所謂有效,即盡量保證每一次操作都向最后的有序靠近而非背離 )。

    師:要使大頭在后,應(yīng)使大頭在后。

    俗:日,這是什么狗屁邏輯?

    師:因果。在前既是在后。

    俗:USA, CNN(操你娘)。

    師:翻轉(zhuǎn)。既不在前,也不在后,使之在前,使之在后。

    俗:日,什么東西?既不在前,也不在后,不前不后,難道在中間啊?

    師:然也。

    Fox:O, my God!整點實際的吧!整個過程分為n輪,在第i(i=0, 1, ..., n-1)輪:

    a. 找到大頭n-i,是否有序?是,轉(zhuǎn)g;

    b. 是否大頭在后?是,轉(zhuǎn)f;

    c. 是否大頭在前?是,轉(zhuǎn)e;

    d. 將隊頭(第一個元素)與大頭n-i對調(diào)(別忘了是翻轉(zhuǎn),中間元素也變序了),++times

    e. 將隊頭n-i與第n-i個元素對調(diào),++times

    f. ++i,轉(zhuǎn)a;

    g. 輸出序列(0, 1, ..., n)和翻轉(zhuǎn)次數(shù)times;OVER:D。

    快速排序思想:

    在最開始的時候,我就想到使用快速排序的思想,先使整個數(shù)列局部有序,最后調(diào)整使全部有序。悲哀的是,在我考慮 4 3 1 2這個數(shù)列的時候,我斷定還要通過上面的方式重新像冒泡排序一樣重新來過,立即放棄想法,于是給了上面的思路,而且堅定的認(rèn)為這個方法已經(jīng)很好。結(jié)果,下午GR告訴我他的反例:4 3 1 2 --> 2 1 3 4|--> 1 2| 3 4,“|”表示從該處翻轉(zhuǎn)。

    我必須承認(rèn),這才是好的方法,我過分拘泥于不去改動已經(jīng)有序的部分。然而,這家伙只知道反駁我,卻無法給出算法。

    我只好自己重新考慮局部有序之后的問題。

    十分鐘后,我有了如下的答案(目前我能想到的最佳答案),但不得不承認(rèn),表述算法比給出一種情況對應(yīng)的解要麻煩的多的多的多,假定A、B滿足A==B-1,即A、B為相鄰數(shù)列(為簡單記,元素和數(shù)列統(tǒng)稱數(shù)列)。則A、B的組合類型有8種:B(O)A(O)、B(C)A(O)、B(O)A(C)、B(C)A(C)、A(C)B(O)、A(O)B(O)、A(C)B(C)、A(O)B(C),O表示正向(obverse)C表示逆向(reverse),以1 2 3 4為例:

    B(O)A(O):3 4 1 2<2>B(C)A(O):4 3 1 2<4>B(O)A(C):3 4 2 1<5>、B(C)A(C):4 3 2 1<7>;

    A(C)B(C):2 1 4 3<1>A(O)B(C):1 2 4 3<3>A(C)B(O):2 1 3 4<6>、A(O)B(O):1 2 3 4<8>。

    對應(yīng)操作規(guī)則如下:

    a. 0x0101: B(O)A(O) --> B(C)A(O); 3

    b. 0x0001: B(C)A(O) --> A(C)B(O); 2

    c. 0x0101: B(O)A(C) --> B(C)A(C);1

    d. 0x0000: B(C)A(C):如果當(dāng)前只剩A、B兩個子列,則翻轉(zhuǎn)一次成A(O)B(O)1 2 3 4為最終結(jié)果,否則,認(rèn)為B(C)A(C)可以作為一個逆序有序數(shù)列考慮,暫時無需翻轉(zhuǎn);

    e. 0x1010: A(C)B(C) --> A(O)B(C); 3

    f. 0x1110: A(O)B(C) --> B(O)A(C);  2

    g. 0x1011: A(C)B(O) --> A(O)B(O);1

    h. 0x1111: A(O)B(O):A、B可以作為一個有序數(shù)列考慮如果當(dāng)前只有A、B兩個子列,則正序序列A(O)B(O)1 2 3 4為最終結(jié)果

    上面規(guī)則的制定其實是反向?qū)С?/strong>的,遵循的原則是,正序有序的A(O)B(O)和逆序有序的B(C)A(C)可以看作一個序列,A(C)B(O)、B(O)A(C)可一步達到,B(C)A(O)、A(O)B(C)可兩步達到,B(O)A(O)、A(C)B(C)可三步達到。即對于4個元素,最壞的的A(C)B(C)需要4步(對應(yīng)于上面的冒泡法卻只需要3步L)。而且當(dāng)元素比較多的時候,記住1、2個有序子列是可行的,但對于完全無序的數(shù)列,分析出所有有序子列,既不現(xiàn)實,也無必要。

    修改規(guī)則如下:當(dāng)隊頭無序&&相鄰數(shù)列有序||隊頭有序,翻轉(zhuǎn)隊頭;否則,將隊頭連同該元素一同翻轉(zhuǎn)

    由此可見,這算法還要改進:

    a. 判斷Array[0]是否正序連續(xù)(連續(xù)個數(shù)nNum1),如果nNum1==n,轉(zhuǎn)i,如果nNum1!=1,轉(zhuǎn)c;

    b. 判斷Array[0]是否逆序連續(xù)(連續(xù)個數(shù)nNum1),如果nNum1==n,翻轉(zhuǎn)Array,轉(zhuǎn)f;

    c. 從下標(biāo)nNum1開始查找Array[0]+1(bObserve = true)和Array[0]-1(bObserve = false)的下標(biāo)nStart2,如果nNum1==nStart2bOrder1==true,轉(zhuǎn)e,如果nNum1!=1,置nEnd2=nStart2

    d. 判斷( bObserve == true&&Array[nStart2]+1==Array[nStart2+1] ) || ( bObserve == false&&Array[nStart2]==Array[nStart2+1]+1 ),true則置nEnd2=nStart2,false則置nEnd2=nStart2+1;

    e. 翻轉(zhuǎn)Array[0] to Array[nEnd2],轉(zhuǎn)a;

    f. 輸出Arraytimes

    這樣來看,改進后的算法竟簡單了許多!

    不幸的是:按上面給出的算法翻轉(zhuǎn)合并1 3 5 6 4 8 7 9 2 0:

    1 3 5 6 4 8 7 9| 2| 0

    2 9 7 8 4 6 5| 3| 1 0

    3 5 6| 4| 8 7 9 2 1 0

    4 6| 5| 3 8 7 9 2 1 0

    5 6| 4 3 8 7 9 2 1 0

    6 5 4 3 8| 7| 9 2 1 0

    7 8 3 4 5| 6| 9 2 1 0

    進入死循環(huán)了……

    很明顯應(yīng)該是下面這個樣子:

    1 3 5 6 4 8 7 9 2| 0

    9 8 7 4 6 5| 3 1 2 0

    5 6 4| 7 8 9 3 1 2 0

    6 5 4 7| 8 9 3 1 2 0

    4 5 6 7 8 9 3| 1 2 0

    3 4 5 6 7 8 9 1 2| 0

    1 9 8 7 6 5 4 3 2| 0

    2 3 4 5 6 7 8 9 1| 0

    9 8 7 6 5 4 3 2 1 0|

    0 1 2 3 4 5 6 7 8 9

    執(zhí)行9次翻轉(zhuǎn)。算法如何得到呢?

    a. 確定最小無序完整子集SnSn中含n>1個連續(xù)數(shù));

    b. 將Sn最前面的有序子集Soo>1)加以考慮,一個子集?兩個子集?

    ______________________________________________________________________________

    O, my God!

    這個問題,從前天晚上到現(xiàn)在,思考、分析、抽象了至少有15個小時以上了:

    a. Apr.18th-19th: 23:00 - 01:30

    b. Apr.19th: 11:00 - 13:00

    c. Apr.19th-20th: 22:00 - 05:30

    d. Apr.20th: 11:00 - 15:00

    結(jié)果是,到現(xiàn)在無法給出一個最優(yōu)的翻轉(zhuǎn)算法。一個周末都花在這上面了,準(zhǔn)備放棄L

    LP催著我讓我回學(xué)校,是該回去了!

    posted @ 2008-04-20 14:59 Fox 閱讀(4066) | 評論 (13)編輯 收藏

    Author: Fox

    晚上沒有加班,打游戲打到9點過,后面就又看了一道《編程之美》的題目《中國象棋將帥問題》。

    題目:下過中國象棋的朋友都知道,雙方的“將”和“帥”相隔遙遠(yuǎn),并且它們不能照面。在象棋殘局中,許多高手能利用這一規(guī)則走出精妙的殺招。假設(shè)棋盤上只有“將”和“帥”二子(如圖1-3所示)(為了下面敘述方便,我們約定用A表示“將”,B表示“帥”):

    1-3副本

    AB二子被限制在己方3×3的格子里運動。例如,在如上的表格里,A被正方形{d10, f10, d8, f8}包圍,而B被正方形{d3, f3, d1, f1}包圍。每一步,AB分別可以橫向或縱向移動一格,但不能沿對角線移動。另外,A不能面對B,也就是說,AB不能處于同一縱向直線上(比如Ad10的位置,那么B就不能在d1d2以及d3)。

    請寫出一個程序,輸出AB所有合法位置。要求在代碼中只能使用一個變量。

    在紙上畫了半天,Soft從臺灣給帶的長壽都讓我抽完了,總算對得起這會兒工夫……

    我的思路大致如下:

    1) 只能使用一個變量nNum ==> 只能使用一個循環(huán),nNum只能用來表示A、B位置的組合,nNum最大為9×9-1=80;

    2) 如何用nNum表示一個A、B位置的組合?

    下圖表示A(紅色)B(藍色)所在位置:

    6 7 8
    3 4 5
    0 1 2
    6 7 8
    3 4 5
    0 1 2

    nNum%9表示A的位置nNum/9表示B的位置,如nNum==15,A==6B==1

    3) 如何確定A、B位置的合法性?

    規(guī)則都指定了,合法性的確定也就很簡單了:A%3 != B%3。

    OK,剩下的輸出就很簡單了,為了好看一點,這里希望直接按題目給的圖表示出A、B的位置,如:“A:d10, B:e3”,還有顏色:D。

    A的行號:A/3+8;

    A的列號:A%3+d;

    B的行號:B/3+1;

    B的列號:B%3+d;

    代碼如下(注釋掉的部分只是為了輸出更“漂亮”一點):

     

     1 #include <stdio.h>
     2 //#include <windows.h>
     3 
     4 //HANDLE hStdout;
     5 //CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
     6 //WORD wOldColorAttrs;
     7 
     8 int _tmain(int argc, _TCHAR* argv[])
     9 {
    10     //hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    11     //GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
    12     //wOldColorAttrs = csbiInfo.wAttributes;
    13 
    14     int nNum = 81;    // nNum表示所有位置(含非法),故nNum = 3 * 3 * 3 * 3
    15     while( nNum-- )
    16     {
    17         if( nNum%9%3 != nNum/9%3 )
    18         {
    19             //SetConsoleTextAttribute(hStdout, FOREGROUND_RED | FOREGROUND_INTENSITY);
    20             printf("A:%x%02d ", nNum%9%3+0xd, nNum%9/3+8);
    21             //SetConsoleTextAttribute(hStdout, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
    22             printf("B:%x%02d  ", nNum/9%3+0xd, nNum/9/3+1);
    23         }
    24         if!(nNum%9) )
    25             printf("\n");
    26     };
    27     printf("\n");
    28     //SetConsoleTextAttribute(hStdout, wOldColorAttrs);
    29     return 0;
    30 }

    輸出:

    點擊查看更清晰原圖:D

    PS: 剛寫完,沒有來得及總結(jié)更多,急著向LP炫耀。但上面的思路應(yīng)該比較清晰了,也不管書上的答案了,反正我感覺我這點代碼效率應(yīng)該也不會低到哪兒吧:-)?

    posted @ 2008-04-18 00:26 Fox 閱讀(4042) | 評論 (8)編輯 收藏

    Author: Fox

    前兩天在買《計算機程序設(shè)計藝術(shù)》中文版的時候,偶然發(fā)現(xiàn)《編程之美》這本書,當(dāng)時翻了一下,看到“讓CPU占用率曲線聽你指揮”這樣的題目確實讓人為之一動。寫一段代碼,可以讓CPU占有率曲線畫出平滑的正弦曲線,有點意思:-)。

    當(dāng)然,最后沒有買這本書,雖然我可以肯定這是本好書。

    我從CSDN讀書上找到幾節(jié),閑來讀一讀。今天來討論一下《讓CPU占用率曲線聽你指揮》。

    題目:寫一個程序,讓用戶來決定Windows任務(wù)管理器(Task Manager)的CPU占用率。程序越精簡越好,計算機語言不限。例如,可以實現(xiàn)下面三種情況:

    1.    CPU的占用率固定在50%,為一條直線;

    2.    CPU的占用率為一條直線,但是具體占用率由命令行參數(shù)決定(參數(shù)范圍1~ 100);

    3.    CPU的占用率狀態(tài)是一個正弦曲線。

    在討論具體實現(xiàn)之前,一個非常重要的問題是:什么是CPU占用率

    編程之美》寫道:“在任務(wù)管理器的一個刷新周期內(nèi),CPU忙(執(zhí)行應(yīng)用程序)的時間和刷新周期總時間的比率,就是CPU的占用率,也就是說,任務(wù)管理器中顯示的是每個刷新周期內(nèi)CPU占用率的統(tǒng)計平均值。

    打開“Windows 任務(wù)管理器”,“性能”中有“CPU使用記錄”一項,給出的就是CPU占有率曲線。

    至于一個刷新周期到底是多長,書中似乎沒有明確給出,只是說“大約是1秒鐘更新一次”,我打開Windows自帶的時鐘,也感覺大約是1秒鐘。

    另外的常識是:

    單核環(huán)境下,空死循環(huán)會導(dǎo)致100%的CPU占有率。雙核環(huán)境下,CPU總占有率大約為50%,四核會不會是25%左右呢?(我沒有四核,只能猜測了,估計各核間切換也會耗掉點時間,因為我的雙核環(huán)境并沒有出現(xiàn)一核100%,另一核空閑的情況)。

    當(dāng)CPU整個刷新周期(絕大多數(shù)時間)空閑時,CPU占有率趨于0。

    書中給出的正弦實現(xiàn)如下:

    1 #include "Windows.h"
    2 #include "stdlib.h"
    3 #include "math.h" 
    4 
    5 const double SPLIT = 0.01;
    6 const int COUNT = 200;
    7 const double PI = 3.14159265;
    8 const int INTERVAL = 300;
    9 
    10 int _tmain(int argc, _TCHAR* argv[])
    11 {
    12     DWORD busySpan[COUNT];  //array of busy times
    13     DWORD idleSpan[COUNT];  //array of idle times
    14     int half = INTERVAL / 2;
    15     double radian = 0.0;
    16     for(int i = 0; i < COUNT; i++)
    17     {
    18         busySpan[i] = (DWORD)(half + (sin(PI * radian) * half));
    19         idleSpan[i] = INTERVAL - busySpan[i];
    20         radian += SPLIT;
    21     }
    22     DWORD startTime = 0;
    23     int j = 0;
    24     while (true)
    25     {
    26         j = j % COUNT;
    27         startTime = GetTickCount();
    28         while ((GetTickCount() - startTime) <= busySpan[j]) ;
    29         Sleep(idleSpan[j]);
    30         j++;
    31     }
    32     return 0;
    33 }


    在單核環(huán)境(P4 2.40)下,其表現(xiàn)還是不錯的:

    點擊查看大圖

    在雙核環(huán)境(Core2 E4500)下,就有點差強人意不盡人意了:

    點擊查看大圖

    不過,總還能看出是正弦曲線。

    上面兩圖的問題:

    1) 單核時曲線不夠平滑,是由于QQ等程序占用CPU所致;

    2) 雙核時曲線更加抖動,我的理解是除其他程序影響外,由于線程沒有固定運行在一個CPU上導(dǎo)致的,后面看到書上提到線程遷移,個人感覺這個叫法欠妥啊,總覺得線程遷移令人費解。

    可以立即想到的是:讓進程在指定處理器上運行(處理器親緣關(guān)系),由Windows提供了兩個API可以做到這一點:GetCurrentProcessSetProcessAffinityMask的。

    修改之后的代碼如下:

    1 #include "Windows.h"
    2 #include "stdlib.h"
    3 #include "math.h" 
    4 
    5 const double SPLIT = 0.01;
    6 const int COUNT = 200;
    7 const double PI = 3.14159265;
    8 const int INTERVAL = 300;
    9 
    10 int _tmain(int argc, _TCHAR* argv[])
    11 {
    12    SetProcessAffinityMask(
    13         GetCurrentProcess(),
    14         0x00000001          //cpu mask
    15         );
    16 
    17     DWORD busySpan[COUNT];  //array of busy times
    18     DWORD idleSpan[COUNT];  //array of idle times
    19     int half = INTERVAL / 2;
    20     double radian = 0.0;
    21     for(int i = 0; i < COUNT; i++)
    22     {
    23         busySpan[i] = (DWORD)(half + (sin(PI * radian) * half));
    24         idleSpan[i] = INTERVAL - busySpan[i];
    25         radian += SPLIT;
    26     }
    27     DWORD startTime = 0;
    28     int j = 0;
    29     while (true)
    30     {
    31         j = j % COUNT;
    32         startTime = GetTickCount();
    33         while ((GetTickCount() - startTime) <= busySpan[j]) ;
    34         Sleep(idleSpan[j]);
    35         j++;
    36     }
    37     return 0;
    38 }


    雙核環(huán)境(Core2 E4500)修改之后的輸出如下:

    點擊查看大圖

    我理想中的表現(xiàn)是:

    1) 曲線是平滑的,最好不因其他應(yīng)用程序或操作的執(zhí)行而改變;

    2) 不管是單核還是雙核,峰值皆為100%,谷值為0。

    對于第一點,其實就是保證任一刷新周期中的CPU占有率都可以被精確控制在0-100之間,如果你可以使CPU一直保持50%(而不是近似的上下波動),產(chǎn)生一條平滑的曲線就很easy了。

    問題的關(guān)鍵在于,除了當(dāng)前你寫的程序可以控制,其他程序或操作如何控制?或者說:如何控制CPU的運行情況才是關(guān)鍵之處

    PS: 一晚上老是斷網(wǎng),搞得思路頻頻被打斷,興致也損了大半。總之,《編程之美》還是值得玩味一把吧:D。

    posted @ 2008-04-17 00:20 Fox 閱讀(11212) | 評論 (10)編輯 收藏

    Author: Fox

    本文寫給滿足以下條件的你(前面4點對應(yīng)只讀的你,后面4點對應(yīng)只寫的你):

    1) 經(jīng)常閱讀別人的Blog,所謂經(jīng)常,平均每天閱讀量在100篇左右;

    2) 不希望花費大量時間在輸入網(wǎng)址、鼠標(biāo)點擊和滾動上;

    3) 有固定的閱讀習(xí)慣,指專注于某一領(lǐng)域、某些特定的Blog;

    4) 尚未使用或剛使用Google Reader并愿意使用Google Reader

    5) 經(jīng)常更新自己的Blog,所謂經(jīng)常,平均每月更新1篇或以上;

    2) 不希望花費大量時間在復(fù)制、粘貼上;

    3) 希望多與其他人交流;

    4) 尚未使用或剛使用Windows Live Writer并愿意使用Windows Live Writer

    1、For只讀的你

    1) 啟用Google Reader

    做互聯(lián)網(wǎng)的就是做互聯(lián)網(wǎng)的,GoogleGoogle Reader有個帳號就能開啟。

    之前寫過一個給我GF看的一點東西,這兒對于啟用Google Reader不再贅述了,有需要的TX可以看一下或者直接Google

    2) 添加RSS地址

    首先要找到RSS地址,大多數(shù)網(wǎng)站提供的Blog,RSS地址都擺在顯眼的地方,對于QQ空間這種算不上Blog的Blog來說,QQ空間的RSS也存在,比如,QQ號碼為10000的用戶,它的RSS就是:http://feeds.qzone.qq.com/cgi-bin/cgi_rss_out?uin=10000

    找到RSS地址之后,就可以將其添加到訂閱列表里面了。

    3) 關(guān)于OPML

    如果你想共享或備份你的訂閱,Google Reader具有“導(dǎo)入/導(dǎo)出”功能,不提供具體的使用方式了,Google吧。

    4) 星標(biāo)和標(biāo)簽

    看過的好文希望下次再讀,就加個星標(biāo)吧。

    RSS太多,就使用標(biāo)簽吧。

    覺得我這兒寫的太簡單,就Google吧,或者看看Google Reader的四個常用小技巧

    2、For只寫的你

    1) 使用Windows Live Writer

    做軟件的就是做軟件的,Microsoft的這個Windows Live Writer是要下載安裝的。

    具體如何去用,下載下來自然就會用了。

    友情提示:Live Writer的格式、超鏈接、查看、草稿、帳戶、日志、分類列表,都很好用。

    2) 關(guān)于CSS

    我不太喜歡Blog的頁面太亂,一會兒五號字,一會兒二號字,一會兒宋體,一會兒楷體。

    我喜歡把一些我認(rèn)為重要的地方鏈接的內(nèi)容突出顯示,以前沒添加CSS的時候,每次都要自己去一個一個的修改鏈接的字體和顏色,浪費很多時間,如果你的Blog支持CSS,就去看一下怎么使用吧,比如我的CSS就很簡單:

    <style type=text/css>
    #top a{ border-bottom:1px dashed; color:white;  }
    #top a:link{ border-bottom:1px dashed; color:white; }
    #top a:hover{ border-bottom:1px dashed; color:white; }
    #top a:visited{ border-bottom:1px dashed; color:white; }
    .post a:link{ border-bottom:1px dashed; color:maroon; }
    .post a:hover{ border-bottom:1px dashed; color:maroon; }
    .post a:visited{ border-bottom:1px dashed; color:maroon; }
    .postbody a{ color:white; background:maroon;  }
    .postbody a:link{ color:white; background:maroon;  }
    .postbody a:hover{ color:white; background:maroon; }
    .postbody a:visited{ color:white; background:maroon; }
    </style>

    我還使用了DevToolBar幫助我確定了CSS。

    3) 添加Google專用的訂閱

    我為此還專門PS了一張圖片,你可以將下面的代碼放到“公告”里面,當(dāng)然,你想放到哪兒就放到哪兒:

    <br />訂閱到:<br />
    <a href="><img src="http://m.shnenglu.com/images/cppblog_com/Fox/6064/o_GoogleRss.jpg" border="0" alt="添加 游戲人生 到 Google 閱讀器"></a><br />

    我這兒是有圖片的,只有文字的就是這樣:

    <br />訂閱到:<br />
    <a href=">添加 游戲人生 到 Google 閱讀器</a><br />

    上面的m.shnenglu.com/Fox/Rss.aspx是我的RSS地址,你要換成你的:-)。

    4) 關(guān)于郵箱地址

    不要把郵箱地址直接放在頁面上,我之前曾經(jīng)這樣做,后面每天收到不少的垃圾郵件,就取消了,因為這年頭,寫個郵箱地址搜索器,然后發(fā)垃圾郵件太easy了。

    PS: 好用的東西還很多,因為我自己是GFans,所有在工作、學(xué)習(xí)和生活中使用了Analytics、Gmail、iGoogle、Picasa、Reader、Talk、筆記本、快訊、日歷、網(wǎng)頁歷史記錄、網(wǎng)站管理員工具、文件,反正都是免費的,而且用來比較方便,一個帳號可以搞定很多東西:D。

    posted @ 2008-04-15 15:59 Fox 閱讀(1847) | 評論 (3)編輯 收藏

    Author: Fox

    John von Neumann說:Any one who considers arithmetical methods of producing random digits is , of course, in a state of sin.

    所以,在討論算法實現(xiàn)隨機數(shù)的時候,總是說“偽隨機數(shù)”。

    現(xiàn)在,應(yīng)用最廣的隨機數(shù)生成算法是由Derrick Henry Lehmer1951年給出的線性同余法

    ????????????? Xn+1 = ( aXn + c ) mod m,? n>=0.

    上一篇偽隨機數(shù)的論述中,并沒有給出X0, a, c, m的取值規(guī)則,只是給出了ANSI CMicrosoft Visual C++的實現(xiàn)。

    在這兒我們可以自己先思考一下,我們期望從上式中得到的隨機數(shù)應(yīng)該滿足:

    1) 上式的輸出足夠隨機,這是最基本的要求;

    2) 上式給出盡量多的輸出,越接近m個越好(不可能超過m),即周期盡量長,最好為m,這樣才能保證上式滿足均勻分布m個數(shù)在周期m中各出現(xiàn)一次m個數(shù)出現(xiàn)的概率相等);

    3) 上式的生成速度足夠快。

    最容易想到的,m的取值為計算機字大小(如2^32或2^64)

    但是這兒有個很嚴(yán)重的問題:Xn低位的隨機性很弱。原因如下:

    令d|m, 且

    ????????????? Yn = Xn mod d

    ????????????? Yn+1 = ( ( aXn + c ) mod m ) mod d

    ????????????????????? = ( aYn + c ) mod d

    上述表達式的意義即:Yn為Xn低k位(d=2^k),這樣的Yn序列形成周期為d甚至更短的同余序列。舉例說明:d為2^1時,Yn為Xn的最低位(可假定為1或0),若Yn+1 != Yn,則Yn+2 == Yn必定成立,僅當(dāng)a、c皆為奇數(shù)時Yn、Yn+1將0、1交替,否則,為常數(shù)(0或1)。

    暫時拋開隨機性不管,先找到周期為m的隨機序列中的取值規(guī)則。

    Donald KnuthThe Art of Computer Programming, Volume 2: Seminumerical Algorithms中的3.2.1.2節(jié)對m, a, c和X0取值規(guī)則的表述:

    1) gcd(c, m) = 1. 即c, m互素,再白一點,c, m除1之外沒有其他公因子;

    2) 任給質(zhì)數(shù)p, p|m ==> p|(a-1). 即m%p==0,則(a-1)%p==0。

    3) 4|m ==> 4|(a-1). 即m%4==0,則(a-1)%4==0。

    這個證明過程對于我這樣的數(shù)論基礎(chǔ)不是很扎實的搞應(yīng)用技術(shù)的人來說有點難以理解了。有興趣的話,還是去看3.2.1.2的證明吧:-)。

    上面的規(guī)則告訴我們,滿足了上述規(guī)則后,可以保證序列周期為m。對于前面提到的關(guān)于隨機性的問題,既然Xn低位的隨機性比較弱,可以只取Xn的高位作為輸出。高位的隨機性和統(tǒng)計意義由a, c確定,其取值涉及統(tǒng)計檢驗,具體的也還是看3.3吧。

    這篇文章解決了具有統(tǒng)計意義的隨機數(shù)的部分理論問題。

    PS: 之前曾經(jīng)BS過Windows Live Writer,當(dāng)時覺得Writer編輯功能太少,不能直接設(shè)定鏈接文字的字體顏色,知道CSS可以設(shè)定之后,又覺得Word 2007編輯的Blog轉(zhuǎn)成html之后太大,而且也知道Word 2007上面是可以設(shè)置鏈接的target為_blank的。現(xiàn)在發(fā)現(xiàn)Writer還是很不錯的了,原來是可以設(shè)定格式的,也可以直接編輯html,而且可以Web預(yù)覽,鏈接還可以加入到鏈接詞匯表,挺方便的。

    越來越發(fā)現(xiàn)Wikipedia是個和Google一樣的好東西!

    posted @ 2008-04-15 14:01 Fox 閱讀(4396) | 評論 (3)編輯 收藏

    轉(zhuǎn)自:http://m.shnenglu.com/Bugs/archive/2008/04/01/45903.html

    基本要求:
    1、有軟件工程思想,熟悉面向?qū)ο笏枷搿?br>2、有良好的編碼風(fēng)格和文檔編寫習(xí)慣
    3、熟悉C++語言,了解STL
    4、了解多線程程序設(shè)計技術(shù)
    5、熱愛游戲、有游戲開發(fā)經(jīng)驗者優(yōu)先
    6、有團隊開發(fā)精神優(yōu)先

    客戶端程序員:
    1、了解DirectX編程技術(shù),有良好的數(shù)學(xué)、各種算法基礎(chǔ)優(yōu)先
    2、有圖形開發(fā)經(jīng)驗者優(yōu)先
    3、熟悉UI編程技術(shù)優(yōu)先
    4、熟悉引擎開發(fā)者優(yōu)先

    服務(wù)器端程序員:
    1、熟悉通信以及網(wǎng)絡(luò)安全技術(shù). 熟悉TCP/IP協(xié)議及相關(guān)編程技術(shù)優(yōu)先
    2、有關(guān)系數(shù)據(jù)庫編程及概念經(jīng)驗(MySql、PostgreSQL、MS Sql)
    3、了解分布式服務(wù)器架構(gòu)技術(shù)優(yōu)先
    4、了解Lua、Python有游戲腳本系統(tǒng)設(shè)計經(jīng)驗優(yōu)先

    辦公地點:成都
    有意向的朋友跟貼或發(fā)簡歷到本博左側(cè)郵箱。

    posted @ 2008-04-11 12:41 Fox 閱讀(885) | 評論 (2)編輯 收藏

    Author: Fox

    一、隨機數(shù)

    軟件實現(xiàn)的隨機數(shù)生成器(random number generator, RNG)生成的隨機數(shù)列大多是惟定數(shù)列,因此一般被稱作偽隨機數(shù)(pseudorandom number, PRN),而基于熱噪聲(thermal noise)光電效應(yīng)(photoelectric effect)放射性衰變(radioactive disintegration)等不可預(yù)知的物理現(xiàn)象實現(xiàn)的硬件隨機數(shù)生成器(hardware random number generator)生成的隨機數(shù)才被認(rèn)為是真隨機數(shù)(truly random number)。PRN在計算機領(lǐng)域主要用于仿真(simulation)和密碼學(xué)(cryptography)。

    本文僅討論偽隨機數(shù)軟件實現(xiàn)算法,并且僅討論滿足均勻分布(uniform distribution, UD)的偽隨機數(shù)發(fā)生器(pseudorandom number generator, PRNG)。非均勻分布(non-uniform distribution, NUD)的PRNG可通過滿足均勻分布的PRNG與非線性函數(shù)生成。

    本文討論的PRNG應(yīng)滿足以下三點:

    a. 在取值空間內(nèi)滿足UD,即等概率取得取值空間內(nèi)任意值。

    b. 充分隨機,即該隨機數(shù)列周期(period)應(yīng)盡量長。此點由所謂的熵(entropy)度量。

    c. 唯一輸入或稱種子(seed)對應(yīng)唯一輸出PRN。這一點ms是計算機的強項,也是PRN惟定的根源,也是算法被破解的根源。

    由于PRN sequence(PRNS)惟定,所以算法的安全性主要取決于熵的選擇和算法的復(fù)雜性

    二、可預(yù)測PRNG與不可預(yù)測PRNG

    所謂可預(yù)測(determined)PRNG,也被稱為統(tǒng)計意義上的PRNG(statistic PRNG),一般只有一個seed,而對于同一個seed,生成的PRNS是惟定的。

    不可預(yù)測(indetermined)PRNG,從理論上講,不可預(yù)測的PRNG是不存在的,這兒指密碼學(xué)安全的PRNG(cryptographically secure PRNG, CSPRNG)。

    三、常用PRNGs

    1、線性同余發(fā)生器(linear congruential generators, LCG)

    單博偉標(biāo)準(zhǔn)庫rand()函數(shù)的缺陷以及Blitz++隨機數(shù)生成的簡介中給出了The C Programming Langurage 2nd的實現(xiàn),我手頭沒有這本書,所以無從查證,FallHunter應(yīng)該還有吧。

    ?1 unsigned? int ?next? = ? 1 ;
    ?2
    ?3 /* ?rand:?return?pseudo-random?integer?on?0..32767? */
    ?4 int ?rand( void )
    ?5 {
    ?6 ?next? = ?next? * ? 1103515245 ? + ? 12345 ;
    ?7 ? return ?(unsigned? int )(next / 65536 )? % ? 32768 ;
    ?8 }

    ?9
    10 /* ?srand:?set?seed?for?rand()? */
    11 void ?srand(unsigned? int ?seed)
    12 {
    13 ?next? = ?seed;
    14 }

    VS2003中給的實現(xiàn)是:

    ?1 /* **
    ?2 *rand.c?-?random?number?generator
    ?3 *
    ?4 *???????Copyright?(c)?Microsoft?Corporation.?All?rights?reserved.
    ?5 *
    ?6 *Purpose:
    ?7 *???????defines?rand(),?srand()?-?random?number?generator
    ?8 *
    ?9 ****************************************************************************** */

    10
    11 #include? < cruntime.h >
    12 #include? < mtdll.h >
    13 #include? < stddef.h >
    14 #include? < stdlib.h >
    15
    16 #ifndef?_MT
    17 static ? long ?holdrand? = ? 1L ;
    18 #endif ??/*?_MT?*/
    19
    20 /* **
    21 *void?srand(seed)?-?seed?the?random?number?generator
    22 *
    23 *Purpose:
    24 *???????Seeds?the?random?number?generator?with?the?int?given.??Adapted?from?the
    25 *???????BASIC?random?number?generator.
    26 *
    27 *Entry:
    28 *???????unsigned?seed?-?seed?to?seed?rand?#?generator?with
    29 *
    30 *Exit:
    31 *???????None.
    32 *
    33 *Exceptions:
    34 *
    35 ****************************************************************************** */

    36
    37 void ?__cdecl?srand?(
    38 ????????unsigned? int ?seed
    39 ????????)
    40 {
    41 #ifdef?_MT
    42
    43 ????????_getptd() -> _holdrand? = ?(unsigned? long )seed;
    44
    45 #else ??/*?_MT?*/
    46 ????????holdrand? = ?( long )seed;
    47 #endif ??/*?_MT?*/
    48 }

    49
    50
    51 /* **
    52 *int?rand()?-?returns?a?random?number
    53 *
    54 *Purpose:
    55 *???????returns?a?pseudo-random?number?0?through?32767.
    56 *
    57 *Entry:
    58 *???????None.
    59 *
    60 *Exit:
    61 *???????Returns?a?pseudo-random?number?0?through?32767.
    62 *
    63 *Exceptions:
    64 *
    65 ****************************************************************************** */

    66
    67 int ?__cdecl?rand?(
    68 ???????? void
    69 ????????)
    70 {
    71 #ifdef?_MT
    72
    73 ????????_ptiddata?ptd? = ?_getptd();
    74
    75 ???????? return (?((ptd -> _holdrand? = ?ptd -> _holdrand? * ? 214013L
    76 ???????????? + ? 2531011L )? >> ? 16 )? & ? 0x7fff ?);
    77
    78 #else ??/*?_MT?*/
    79 ???????? return (((holdrand? = ?holdrand? * ? 214013L ? + ? 2531011L )? >> ? 16 )? & ? 0x7fff );
    80 #endif ??/*?_MT?*/
    81 }


    2、LFG, LFSR等

    限于篇幅,有興趣的TX可以參考后面的資料,主要是Wikipedia,上面給的算法還是非常詳細(xì)的,CSPRNGs包括Blum Blum Shub、Fortuna等。

    如果出于安全性的考慮,PRNG的輸出不應(yīng)直接作為種子。在《構(gòu)建安全的軟件》一書中,作者認(rèn)為“一個好的PRNG具有這樣的性質(zhì):給足夠的熵,即使攻擊者知道全部的算法細(xì)節(jié),還是不能猜出生成的數(shù)據(jù)流”。

    三、PRNG的安全測試

    FIPS-140標(biāo)準(zhǔn)包含了對RNs的測試規(guī)范。這個標(biāo)準(zhǔn)我也沒有去仔細(xì)看,所以沒法給出更多的信息。被提到的測試包有DIEHARD、pLab

    四、隨機數(shù)在游戲中的應(yīng)用

    1、隨機數(shù)為游戲帶來更多的不確定性,不確定性產(chǎn)生可玩性

    這個應(yīng)該是所有游戲的根本了吧。游戲中玩家、怪物、NPC的很多屬性都是一個范圍,比如攻擊就有最小攻擊和最大攻擊,每次的實際攻擊傷害總在二者之間。

    同樣的,玩家樂此不疲的一次次“洗裝備”、“碰運氣”。其他類推吧?。

    2、游戲的AI更多的依賴于隨機數(shù)

    如果怪物、NPC的AI一切盡在玩家的掌握中,游戲的樂趣就大大降低了,這一點在單機游戲中有著很好的體現(xiàn)。War3中,人機對戰(zhàn),電腦的戰(zhàn)術(shù)并不單一(但還是有規(guī)律可循,人類玩家尚且如此),這其中固然有多方面 的因素。但隨機數(shù)的功勞也不容抹殺。

    3、隨機數(shù)減少數(shù)據(jù)存儲,一個種子可以代替一批數(shù)據(jù)

    游戲中含有大量數(shù)據(jù),如果每一項都要存取,對時間和空間都是一個考驗。對于場景中隨機生成的怪物信息,如果給定一個相同的種子。呃,是不是要簡單一些呢?

    五、相關(guān)內(nèi)容

    至于為什么PRNGs大多使用素數(shù),需要更多的數(shù)論知識,對密碼學(xué)有了解的TX應(yīng)該能夠體會到安全和數(shù)論之間的緊密聯(lián)系。因為手頭沒有數(shù)論的書,所以不多加妄測了。到時寫論文的時候,再填充上吧。

    六、參考文獻

    1. 構(gòu)建安全的軟件. [美]John Viega, Gary McGraw. 鐘向群, 王鵬 譯.? 北京. 清華大學(xué)出版社, 2003.

    2. Pseudorandom number generator及相關(guān)鏈接. http://en.wikipedia.org/wiki/Pseudorandom_number_generator

    3. PRNG - Pseudo-Random Number Generator. http://statmath.wu-wien.ac.at/prng/

    -------------------------------------------------------------------------

    PS01:手上的幾本書

    從幾位TX那兒拿的幾本書,放在桌上大半年了,一直沒有怎么翻過。想想周末還給他們算了,于是就拿過來翻翻,立此存照,如果以后用到的話,再來查。

    a. 《用TCP/IP進行網(wǎng)際互聯(lián)》Vol. III,主要是針對Linux/POSIX套接字的C/S架構(gòu)實現(xiàn)。因為MMORPG的C/S架構(gòu)多依賴于TCP,上面第8、10-16章的內(nèi)容可以關(guān)注一下。

    b. 《構(gòu)建安全的軟件》,上面關(guān)于開源、閉源的口水(Chap. 4),關(guān)于緩沖區(qū)溢出(Chap. 7),關(guān)于隨機數(shù)(Chap. 10),關(guān)于數(shù)據(jù)庫安全(Chap. 14),關(guān)于客戶端安全(Chap. 15),都是值得一看的東西。

    c. 《UNIX環(huán)境高級編程》,進程控制(Chap. 8)、線程控制(Chap. 12)、進程通信(Chap. 15, 17)、套接字(Chap. 16)、數(shù)據(jù)庫(Chap. 20)。

    d. 《UNIX網(wǎng)絡(luò)編程》Vol.I,如果涉及到泛UNIX操作系統(tǒng)下網(wǎng)絡(luò)編程,可以當(dāng)作參考書翻。

    e. 《計算機安全原理》,加密(Chap. 5)、認(rèn)證(Chap. 6)、協(xié)議安全(Chap. 7)、入侵檢測(Chap. 13)、惡意攻擊(Chap. 15)、災(zāi)難恢復(fù)(Chap. 19)。

    PS02:微軟宣布不會抬高收購Yahoo價格

    消息來自Wall Street Journal,不過當(dāng)天可是April Fool

    PS03:關(guān)于Wikipedia

    不是說Wikipedia被禁的嗎?很久沒有訪問過了,這么好的東西,被封了還是很遺憾的。

    PS04:有問題

    發(fā)現(xiàn)問題,找Google;解決問題,找Wikipedia

    PS05:歡迎補充

    -------------------------------------------------------------------------

    Added on Apr.10th

    今天從CodeProject上看到一篇文章Applied Crypto++: Pseudo Random Number Generators),作者Jeffrey Walton對密碼學(xué)和安全比較有研究。

    Jeffrey Walton對Knuth的The Art of Computer Programming Vol.2中關(guān)于隨機數(shù)的部分作了概括。

    這篇文章從一個工程師的角度給出了隨機數(shù)的應(yīng)用和實現(xiàn),很具有參考性。

    作者還從FIPS 140-2標(biāo)準(zhǔn)中引用了下面一段話:

    Random Number Generators fall into one of two classes: deterministic and nondeterministic. A deterministic RNG consists of an algorithm that produces a sequence of bits from an initial value called a seed. A nondeterministic RNG produces output that is dependent on some unpredictable physical source that is outside human control.

    這一段話很好的說明,依賴于算法的RNG所生成的隨機數(shù)列只可能是偽隨機數(shù),真隨機數(shù)依賴于不可預(yù)測的物理源

    posted @ 2008-04-03 02:35 Fox 閱讀(6776) | 評論 (15)編輯 收藏

    Author: Fox

    在以前寫 MMORPG中游戲世界的構(gòu)建 時,提到服務(wù)器架構(gòu)的分類。大多數(shù)情況下,每一種不同的服務(wù)器只會與其對應(yīng)的一個服務(wù)器和多個客戶端通信。比如,GameServer(GS)只會與WorldServer(WS)通信,也就是說GS只作為WS的客戶端。這次,由于項目需求,新增加了一個SomeServer(SS)作為GS的服務(wù)器。

    一、SS網(wǎng)絡(luò)連接分析

    由于需要和大量GS建立網(wǎng)絡(luò)連接,所以SS使用了IOCP模型。鑒于 上一次寫IOCP 時遭到 Kevin TX的鄙視,所以決定今天多寫一點。SS的網(wǎng)絡(luò)模型大致如下:

    0、服務(wù)器主線程啟動;

    1、初始化Winsock,SDK func: WSAStartup ();

    2、創(chuàng)建一個使用overlapped I/O的socket,SDK func: WSASocket();

    3、綁定端口,將本地地址與創(chuàng)建的socket關(guān)聯(lián)起來,SDK func: bind();

    4、創(chuàng)建IOCP對象,SDK func: CreateIoCompletionPort();

    5、創(chuàng)建工作者線程,CreateWorkerThreads();

    6、開始監(jiān)聽,SDK func: listen();

    7、接受客戶端連接,SDK func: WSAAccept();

    8、當(dāng)有新的連接請求到達時,將WSAAccept返回的對應(yīng)的客戶端socket關(guān)聯(lián)到IOCP;

    9、處理WSASend() or WSARecv()。

    在實際處理時,可能會根據(jù)需要建立額外的線程處理socketopt命令,甚至建立專門處理WSAccept的線程。

    關(guān)于工作者線程WorkerThread:

    通過GetQueuedCompletionStatus(),獲取I/O類型和對應(yīng)的socket,如果為接收則通知接收完成并繼續(xù)新的WSARecv(),如果為發(fā)送則通知發(fā)送完成。

    二、GS網(wǎng)絡(luò)連接分析

    GS上對于SS客戶端采用的是WSAEventSelect模型,通過網(wǎng)絡(luò)事件觸發(fā)相應(yīng)操作。

    0、服務(wù)器主線程啟動;

    1、初始化Winsock,SDK func: WSAStartup ();

    2、創(chuàng)建一個使用overlapped I/O的socket,SDK func: WSASocket();

    4、綁定端口,將本地地址與創(chuàng)建的socket關(guān)聯(lián)起來,SDK func: bind();

    5、創(chuàng)建網(wǎng)絡(luò)事件,SDK func: CreateEvent();

    6、設(shè)置網(wǎng)絡(luò)事件的響應(yīng),SDK func: WSAEventSelect();

    7、等待網(wǎng)絡(luò)事件,SDK func: WSAWaitForMultipleEvents();

    8、分析網(wǎng)絡(luò)事件類型并處理,SDK func: WSAEnumNetworkEvents()。

    這里之所以采用CreateEvent而不是WSACreateEvent,是因為由CreateEvent創(chuàng)建的事件允許為auto reset的,而WSACreateEvent創(chuàng)建的事件是manually reset的。

    三、實現(xiàn)過程中的小插曲

    在GS的客戶端實現(xiàn)中遇到幾個問題。

    首先是在消息處理上,GS發(fā)到SS的消息,SS并沒有完全接受到,而SS發(fā)送到GS的消息一切正常。后來跟了一下SS消息隊列,發(fā)現(xiàn)SS居然可以收到GS發(fā)送到WS的消息!然后就在GS上找原因,原來是WS在和SS共用消息隊列,以前GS只對應(yīng)一個服務(wù)器,無所謂共用。現(xiàn)在加了SS,自然要分開處理,否則WS和SS都可能收到發(fā)給對方的消息。

    后面一個bug從周一開始已經(jīng)強奸了我四天了。即使SS已經(jīng)關(guān)閉,WSAEnumNetworkEvents返回的事件對應(yīng)FD_CONNECT的iErrorCode值始終為0。因為中間涉及到多線程和多個服務(wù)器分別對應(yīng)的客戶端,連接到WS的沒有問題,就是SS的客戶端有問題。到今天上午為止,我已經(jīng)把GS的網(wǎng)絡(luò)處理邏輯全部靜態(tài)分析了一遍,沒有任何發(fā)現(xiàn)。臨近中午吃飯的時候,不得已只好用WS的客戶端socket去連接SS,居然出現(xiàn)同樣問題!而我的WS和SS都是放在我機器上的,這樣來看,就只有端口不同了!

    果然,當(dāng)我把SS的監(jiān)聽端口修改之后,問題解決了。因為我是使用8088端口監(jiān)聽GS連接的。當(dāng)我把端口換成80,同樣問題再次出現(xiàn),而且SS無法通過80端口監(jiān)聽。

    接下來提幾個問題:

    1、 被卡巴斯基監(jiān)控的端口8088和服務(wù)器開啟的監(jiān)聽端口8088有什么聯(lián)系?為什么沒有沖突?卡巴僅僅只是從該端口獲取數(shù)據(jù)嗎?為什么網(wǎng)絡(luò)事件的FD_CONNECT的對應(yīng)iErrorCode為0(表明連接成功)?

    2、 80是常規(guī)http端口,它與8080、8088這些http端口的區(qū)別在哪兒?這些端口綁定成功與否的原則是什么?

    ?

    PS:文中關(guān)于IOCP和WSAEventSelect模型更為詳細(xì)的實現(xiàn),可以參考 Network Programming for Microsoft Windows 2nd 的第五章:Winsock I/O Methods。

    最后寫完了,發(fā)覺自己寫的很垃圾,完全就是記流水帳。轉(zhuǎn)念一想,為什么呢?自己基礎(chǔ)不扎實嘛,第一次接觸IOCP和網(wǎng)絡(luò)模型,也就這樣了。

    今天太晚了,要睡了,上面的問題明天再考慮吧 J

    posted @ 2008-03-28 01:41 Fox 閱讀(3716) | 評論 (8)編輯 收藏

    Author: Fox

    昨天越俎代庖面試了一個家伙。

    看完了他的筆試題目,感覺后背有點涼,但這些東西看看也就過去了,說實話,那些C++的題目多少有點BT

    但我一直覺得DS的東西,如果你當(dāng)初學(xué)的時候是很認(rèn)真學(xué)習(xí)過并思考過的,其實是不需要去記憶的,所以我就問了一個關(guān)于穩(wěn)定排序和不穩(wěn)定排序的問題。我想,只要你理解了各種排序算法的思想,很easy。

    只是這哥們兒忘記了什么是穩(wěn)定排序,我還以為他把快速排序、堆排序當(dāng)作穩(wěn)定排序只是沒記住。看來,老師從小教育的"一道題目你即使不會也要盡量去答"這種思想遺毒頗深。如果抱著這種思想做程序員,公司多半要垮掉。

    想一想穩(wěn)定排序的概念吧:兩個同值元素(不知為什么,我一直記得嚴(yán)老師書上用的是49,看來我還是在讀死書死讀書,最后可能會讀書死L)在排序前后相對位置保持不變,即本來在前面的還在前面(所謂"塵歸塵,土歸土",看來最近思想有點消極,難怪沒有激情L)。再想想各種排序的思想,我們很容易得到這樣的結(jié)論:最普通的O(n2)的算法,一個一個從前比到后,自然不會影響到同值元素的相對位置,而O(nlogn)的算法,由于多路比較,可能導(dǎo)致本來相對位于后面的元素先比較和移動,造成不穩(wěn)定。這樣一想,自然知道簡單的插入、選擇、歸并排序都是穩(wěn)定的,而改進的高效率的算法則不穩(wěn)定。

    后面另一個同事在詢問他做的Demo的事情,因為是DX的東西,我不懂,沒插嘴,就隨便看他的簡歷。

    看到其中一項,有提到他曾經(jīng)給大一、大二的學(xué)生做過C++培訓(xùn)。我本沒打算提他筆試中的C++部分的,但既然曾經(jīng)為人師表(因為我曾經(jīng)做過學(xué)生、也做過老師),C++基礎(chǔ)掌握到這種程度就不對了。尤其對于一個空的C++類默認(rèn)生成哪些成員函數(shù)居然寫的一塌糊涂(友情提示:你也不用BS他,如果你沒有看過Lippman的《Inside of the C++ Object Model》,建議你先不要發(fā)言J)。

    我一般對語言特性不太敢發(fā)表觀點(因為我的C++基礎(chǔ)不扎實L),但我對簡單的算法或思想小有興趣(沒有你想象中那么高)。可是,筆試中唯一的一個需要coding的題目他又沒寫。我只好說,C++的東西你掌握怎么樣我也可以不看,但這個memcpy的實現(xiàn),你怎么也得有點想法吧?不然怎么去寫代碼呢?剛好在面他之前,還和同事討論過memcpy的問題(如果你給出one byte by one byte的實現(xiàn)會遭BS的J,因為你居然沒有考慮過計算機系統(tǒng)本身的數(shù)據(jù)處理)。

    本來還想問他一個關(guān)于sizeof()的問題,后來覺得也沒什么必要,關(guān)于union的對齊,要按照單位最長的成員對齊這一點自己都覺得有點BT就算了。

    其實,我想說的是,很多東西,你不能認(rèn)為你掌握的很好(除非你真的掌握的很好),所謂很好,拿C++來說,就是把你認(rèn)為你好的地方,你可以不翻其他東西,把它寫下來,基本跟ISO C++保持90%以上的相似度就可以了。當(dāng)然,這樣說有點賤了。

    畢竟,做游戲程序員(其他也差不多吧)需要的是:

    帶著激情去編碼,帶著虛心去學(xué)習(xí),帶著挑戰(zhàn)去交流,帶著壓力去工作。

    激情,能讓你的思維滿具創(chuàng)意,代碼極其飄逸;

    虛心,能讓你的知識不斷積累,從而達到厚積薄發(fā);

    挑戰(zhàn),能讓你的團隊充滿活力,交流活潑嚴(yán)謹(jǐn);

    壓力,能讓你的心態(tài)保持平衡,勝不妄喜,敗不惶餒。

    因為自己這兩周心態(tài)受到非智力因素干擾,日子過得有點渾噩。寫下來,主要是為了放松一下,也提醒自己。

    不怕無知,但怕無畏。

    -----------------------------------------------------------------

    PS:補記于2008/03/26

    還是把上午寫的一個mymemcpy放上來吧。里面沒有對des < src + len的重疊情況進行討論,因為大致google了一下,似乎很少人這樣做(倒不是因為不能實現(xiàn))。

    void *mymemcpy( void *src, void *des, size_t len )
    {
    ?char *tempsrc = (char *)src;
    ?char *tempdes = (char *)des;

    ?size_t offset = len / 4;
    ?for( size_t i=0; i<offset; ++i )
    ?{
    ??*(unsigned long *)tempdes = *(unsigned long *)tempsrc;
    ??tempdes += sizeof(unsigned long);
    ??tempsrc += sizeof(unsigned long);
    ?}
    ?
    ?offset = len - len % 4;
    ?for( size_t i=0; i<offset; ++i )
    ?{
    ??*tempdes++ = *tempsrc++;
    ?}
    ?return des;
    }

    剛才想求證一下memcpy在地址重疊的情況下,是否會考慮從后往前copy的情況。結(jié)果看到云風(fēng)的blog上很早的一篇文章,也是講memcpy的,角度不同。

    我想澄清一點,我寫這篇blog的初衷只是總結(jié)幾個技術(shù)問題,因此就沒有把面試的前因后果講一下,反倒讓很多朋友誤解,以為我怎么怎么樣了。

    事實情況是,這幾個問題都是本來的筆試題目當(dāng)中的,面試的TX從上午10:00前后做到11:30過,等我和另一個同事13點過去的時候,我一直沒怎么說話。只是在一邊看他的簡歷和題目,文中已經(jīng)說了,是看到他的簡歷之后才提的問題。當(dāng)時是有10道左右的C++題目,他做對的其實只有一道。

    而且,我在提問題的時候也都將問題跟他一起分析了的(除了memcpy之外),自我感覺說話還是很得體的,寫文章的風(fēng)格是另一碼事兒。

    我沒有絲毫瞧不起這位TX的意思,也完全沒有顯擺的想法。

    PS :忽然想到自己最近為什么癖性十足,因為最近在關(guān)注一個家伙的 Blog ,如果不侵權(quán),我想用用他的 Blog 的名字《 不許聯(lián)想 》,作者是帶三個表王小峰(《三聯(lián)生活周刊》記者)。所以,如果有人想拍我,建議先看看他的東西,學(xué)習(xí)一下措辭 J 。一個同事,說天涯也行,我個人覺得天涯有點相互吹捧的味道。

    惡心,但沒有惡意 J

    posted @ 2008-03-20 21:17 Fox 閱讀(4158) | 評論 (52)編輯 收藏

    僅列出標(biāo)題
    共7頁: 1 2 3 4 5 6 7 
    青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲专区一区| 亚洲欧美春色| 欧美视频中文字幕在线| 欧美激情va永久在线播放| 久久av在线看| 久久久国产精品亚洲一区 | 亚洲大片免费看| 亚洲国产欧美久久| 亚洲人成网站精品片在线观看 | 欧美四级在线观看| 国产精品入口| 亚洲成人在线网| 一区二区三区国产在线| 香蕉成人久久| 欧美激情精品久久久久久黑人| 亚洲裸体俱乐部裸体舞表演av| 亚洲无线观看| 免播放器亚洲一区| 国产精品久久久久aaaa| 在线观看国产一区二区| 在线一区二区三区做爰视频网站 | 久久夜色精品国产欧美乱极品| 免费日韩成人| 国产精品一区二区男女羞羞无遮挡| 韩国精品一区二区三区| 一二三区精品| 狂野欧美激情性xxxx| 日韩天堂在线观看| 久久男人av资源网站| 欧美特黄一级大片| 精品99一区二区| 亚洲性感美女99在线| 欧美大片一区二区三区| 亚洲欧美日韩精品久久亚洲区| 蜜桃久久精品一区二区| 国产麻豆成人精品| 一区二区av在线| 免费在线日韩av| 亚洲一区视频| 欧美色123| 99精品热视频| 亚洲国产一区二区三区高清| 亚洲欧美99| 欧美午夜电影在线| 夜夜嗨网站十八久久| 猛干欧美女孩| 久久精品91| 国产日韩精品电影| 欧美激情2020午夜免费观看| 亚洲人成在线观看| 久久午夜羞羞影院免费观看| 国产精品爽黄69| 亚洲视频久久| 日韩视频一区二区三区| 欧美成人亚洲成人| 亚洲高清资源| 欧美韩日亚洲| 欧美成人首页| 99re这里只有精品6| 亚洲日本va午夜在线电影| 欧美高潮视频| 一区二区三区精品国产| 亚洲精选大片| 欧美亚州在线观看| 亚洲欧美清纯在线制服| 99re热精品| 国产精品久久久久久妇女6080 | 欧美一级片一区| 亚洲欧美日韩精品久久久久| 国产精品中文字幕欧美| 欧美在线视频二区| 久久精品系列| 亚洲第一区色| 亚洲裸体视频| 国产精品嫩草99a| 久久国产精品99精品国产| 欧美一区91| 亚洲国产欧美不卡在线观看| 亚洲国产婷婷| 国产精品久久久久久久app| 午夜精品久久久久久久99水蜜桃| 亚洲一区二区三区午夜| 狠狠干狠狠久久| 亚洲高清资源| 国产精品地址| 久热综合在线亚洲精品| 欧美成人自拍| 欧美一区91| 欧美xxx在线观看| 亚洲欧美在线另类| 开心色5月久久精品| 一区二区三区日韩| 欧美专区18| 一区二区激情| 久久精品123| 亚洲线精品一区二区三区八戒| 性色av一区二区三区在线观看| 91久久在线观看| 午夜在线观看免费一区| 日韩视频免费观看高清在线视频| 亚洲午夜伦理| 亚洲精品国产精品国自产观看浪潮| 一本色道久久综合狠狠躁篇的优点| 国产一区二区黄| 夜夜嗨av色一区二区不卡| 亚洲二区免费| 欧美在线观看一区二区| 在线视频亚洲欧美| 乱中年女人伦av一区二区| 香蕉亚洲视频| 欧美激情国产日韩| 一本色道**综合亚洲精品蜜桃冫 | 久热精品视频| 亚洲永久字幕| 欧美大胆成人| 老司机免费视频久久| 国产精品美女久久久久久久| 亚洲第一天堂av| 激情国产一区| 久久激情五月丁香伊人| 欧美一区日韩一区| 国产精品九九久久久久久久| 亚洲国产婷婷香蕉久久久久久99 | 午夜激情综合网| 欧美日韩精品免费观看| 亚洲国产精品va在线看黑人动漫| 国产综合18久久久久久| 亚洲欧美清纯在线制服| 亚洲欧美日韩网| 国产精品人成在线观看免费| 一级日韩一区在线观看| 亚洲性感激情| 国产精品国产三级国产| 亚洲精品孕妇| 99精品视频一区二区三区| 欧美国产日韩一区| 亚洲人成精品久久久久| 日韩一区二区久久| 欧美日韩日日骚| 夜久久久久久| 午夜日韩在线观看| 国产欧美一区二区三区沐欲| 亚洲制服欧美中文字幕中文字幕| 亚洲欧美日本国产有色| 国产欧美精品一区aⅴ影院| 亚洲女ⅴideoshd黑人| 久久精品国产77777蜜臀| 韩国三级电影一区二区| 久久久女女女女999久久| 欧美成人性生活| 一区二区日韩免费看| 欧美亚洲第一区| 欧美伊人久久久久久午夜久久久久| 久久婷婷丁香| 亚洲人成在线播放| 国产精品久久久久久久久婷婷| 欧美亚洲网站| 亚洲高清视频中文字幕| 亚洲午夜激情| 国产一区二区三区黄视频| 免费观看一级特黄欧美大片| 99热在线精品观看| 久久精品夜夜夜夜久久| 亚洲国产美女久久久久| 欧美视频一区在线| 久久黄色小说| 99在线热播精品免费| 久久久久久久尹人综合网亚洲| 在线看片欧美| 国产精品成人v| 久久影视三级福利片| 日韩亚洲欧美一区二区三区| 欧美专区在线观看| 亚洲精品中文字幕女同| 亚洲国产精品成人综合| 亚洲综合日韩在线| 久久青草欧美一区二区三区| 亚洲国产小视频在线观看| 欧美视频一区二| 另类综合日韩欧美亚洲| 亚洲深夜福利| 欧美国产综合一区二区| 午夜亚洲激情| 9久re热视频在线精品| 黄色av一区| 国产精品在线看| 欧美视频三区在线播放| 欧美寡妇偷汉性猛交| 久久久国产一区二区| 亚洲自拍偷拍色片视频| 亚洲乱码久久| 欧美激情一区二区三区全黄| 久久精品官网| 欧美在线播放一区| 亚洲一区二区精品视频| 日韩午夜在线观看视频| 亚洲国产成人在线视频| 尤物网精品视频| 精品成人国产| 伊人精品在线|