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

posts - 15,comments - 21,trackbacks - 0
      之前因為時間匆忙,大區里產生唯一ID只是簡單寫了一個方法,今天趁著有時間,把里面一些過程寫出來,也算是個總結。
      首先說說需求,現在游戲里數據庫為了分散熱點,都采用分表方式,以前那種一張表AUTO_INCREMENT的方式顯然不行了,那么將唯一ID放到業務進程里可行嗎?這個也不好,因為現在后臺都是多個部署,每個進程可以保證ID唯一,但是存到數據庫的時候就可能重復,所以我們還是得在DB上做文章。
      因此就有了上篇文章的解決方案,單獨一張表,這張表的作用就是專門產生唯一ID,而且為多個業務提供唯一ID,多進程情況下也不需要鎖表,效率比較高,是不是很像設計模式里的工廠。接著我來分析下這張表。我們再來看看表結構和用法
create table SeqTab 
   iSeqNo bigint(20) not null default 0,   //表示唯一ID
   iSeqType int(11) not null default 0,   //表示業務ID
   primary key(iSeqNo,iSeqType));
   insert into SeqTab values(0,1);        //初始化,假設一個業務編號為1
   update SeqTab set iSeqNo=last_insert_id(iSeqNo+1) where iSeqType=1;
   select last_insert_id();                   //這兩句是獲取一個業務的唯一ID,供業務進程使用。
   其實說到這張表,關鍵是說LAST_INSERT_ID()這個方法。它有兩種形式LAST_INSERT_ID(),LAST_INSERT_ID(expr)。
   這個東西的特點是,1.屬于每個CONNECTION,CONNECTION之間相互不會影響 2.不屬于某個具體的表 3.返回最后一次INSERT AUTO_INCREMENT的值 4.假如以此使用INSERT插入    多行數據,只返回第一行數據產生的值 5.如果你UPDATE某個AUTO_INCREMENT的值,不會影響LAST_INSERT_ID()返回值
   LAST_INSERT_ID(expr)與LAST_INSERT_ID()稍有不同,首先它返回expr的值,其次它的返回值會記錄在LAST_INSERT_ID()。
   我們這里主要使用到了第一和第二個特點,每個進程并發執行update SeqTab set iSeqNo=last_insert_id(iSeqNo+1) where iSeqType=1;,就獲取屬于進程自己的iSeqNo并且記錄在 LAST_INSERT_ID中,通過第二句取出該值。   
    接著我們在比較下其他一些辦法。
    第一種是直接一張表,里面有一個ID字段,設置成AUTO_INCREMENT。這個的問題是每個業務ID不是連續的,是離散的。
    第二種是使用GUID或者UUID,但是這個問題我個人覺得是效率上的差異,字符串沒有數字的效率好,另外數字ID今后也可以拼接一些區信息,之后跨區的時候可以方便獲取對象是哪個區的.
posted @ 2012-12-06 23:01 梨樹陽光 閱讀(2253) | 評論 (2)編輯 收藏
Mysql中除了使用auto_increment字段作為自增序號以外 還有另外一種辦法 可以提供唯一序列號并且可以由一張表完成對多個序列
要求的滿足。
大致如下:
1.創建一個簡單表 
   create table SeqTab
   ( iSeqNo int not null default 0,
     iSeqType int not null default 0);
2.插入一調記錄
   insert into SeqTab values(0,13);  // 0表示SeqNo初始值,13為SeqType,同一張表可對多個應用提供Seq序列服務
3.不管多進程 or 單進程對SeqTab表同時進行訪問,使用一下方法獲取序列號
   1st->update SeqTab set iSeqNo=last_insert_id(iSeqNo+1) where iSeqType=13;
   2nd->select last_insert_id();
4.因多進程對SeqTab同時訪問,進行update SeqTab set iSeqNo=last_insert_id(iSeqNo+1);時,數據庫保證了update的事務
完整性,且last_insert_id()是與當前mysql連接相關的,所以多進程同時使用時,也不會沖突。

posted @ 2012-12-05 11:54 梨樹陽光 閱讀(1738) | 評論 (2)編輯 收藏
      相信大家在開發后臺的過程中都遇到過中文亂碼的問題,今天我就來講講其中的原因。
      我這建了3張表,test_latin1,test_utf8,test_gbk,表結構如下
      +-------+----------+------+-----+---------+-------+
      | Field | Type     | Null | Key | Default | Extra |
      +-------+----------+------+-----+---------+-------+
      | name  | char(32) | YES  |     | NULL    |       |
      +-------+----------+------+-----+---------+-------+

      我的前端是gbk的編碼
      執行下面的語句
      set names 'latin1'
      insert into test_latin1 set name='王';('王'字是GBK編碼)
      select name from test_latin1;
      結果是否為亂碼?

      執行下面的語句
      set names 'gbk'
      insert into test_latin1 set name='王';('王'字是GBK編碼)
      select name from test_latin1;
      結果是否為亂碼?

      執行下面的語句
      set names 'latin1'
      insert into test_utf8 set name='王';('王'字是GBK編碼)
      select name from test_utf8 ;
      結果是否為亂碼?

      我們舉個例子,假設一個漢字的字符編碼為0xFFFF,它在屏幕上能夠正常顯示,如果漢字存入數據庫的時候和從數據庫中取出的時候,編碼一致,那么它肯定不是亂碼。反過來,如果輸出的時候是亂碼,那么它肯定被轉碼了,至于為什么被轉碼了,我們得看看mysql里面做了什么(mysql難道會把無碼片變成了有碼片?)
      首先mysql里面有2個概念,一個叫character set,一個叫collation。我們先說說character set。字符集就是數字,英文字符,漢字等編碼格式,我們常見的是utf8,gbk,gb2312。mysql里面比較復雜,有4個東西跟它有關,分別是character_set_client,character_set_connection,character_set_database,character_set_results。set names (latin1)其實就是character_set_client=latin1,character_set_connection=latin1,character_set_results=latin1,它的流程是character_set_client ==> character_set_connection ==> Table Character ==> character_set_results。
      我們按照上面的流程,來分析第一個問題。
      set names 'latin1'----執行了character_set_client=latin1,character_set_connection=latin1,character_set_results=latin1;
      insert into test_latin1 set name='王';這句話,mysql做了什么事呢?首先,character_set_client,它會把王字的編碼當成latin1的編碼傳遞給character_set_connection(此時不會轉碼),character_set_connection會把編碼傳遞給Table Character,因為表本身是latin1,所以此時也不需要轉碼,select name from test_latin1;mysql會把test_latin1中的編碼傳遞給前端,此時也不需要轉碼,所以,走個流程下來,我們輸入的是什么編碼,輸出的還是相同的編碼,因此,第一個問題的答案是不會是亂碼。我畫個流程圖latin1==>latin1==>latin1==>latin1,沒有轉碼的過程
      
      我們在來看第二個問題。
      set names 'test_gbk'----執行了character_set_client=gbk,character_set_connection=gbk,character_set_results=gbk;
      insert into test_latin1 set name='王';character_set_client,它會把王字的編碼當成gbk的編碼傳遞給character_set_connection(此時不會轉碼),character_set_connection會把編碼傳遞給Table Character,因為表是lanti1的編碼格式,這個過程的時候就會進行轉碼,但是latin1的字符集小于gbk的字符集,所以它會找不到對應字符的編碼,此時會以?代替。select name from test_latin1,此時會從latin1轉碼成gbk,但是此時latin1已經是錯誤的數據了,所以得到的gbk編碼也是錯誤的了。流程gbk==>gbk==>latin1==>gbk,其中gbk==>latin1出了問題,我們select出來的數據也就不可能是輸入時候的數據了。因此,這個問題的答案是亂碼。

      第三個。
      set names 'test_latin1'
      insert into test_utf8 set name='王';character_set_client,它會把王字的編碼當成latin1的編碼傳遞給character_set_connection(此時不會轉碼),character_set_connection會把編碼傳遞給Table Character,此時表是utf8的格式,因此會進行轉碼,latin1==>utf8,因為utf8的字符集>latin1字符集,因此,轉碼正常。select name from test_utf8;會從utf8轉碼成latin1,此時可以轉碼成功,因此我們最終得到的和輸入的時候是一致的,因此答案不是亂碼。流程latin1==>latin1==>utf8==>latin1,從小的字符集到大的字符集再到小的字符集,轉碼是不會有問題的。
      屁話了這么多,無非想告訴大家一個萬精油方法,表創建的字符集和set names都設置成同一個字符集,就基本可以滿足輸入數據不會在轉換過程中失真,也就是說輸入是什么,輸出就是什么。建議有中文的都設置成utf8字符集,一勞永逸。
posted @ 2012-11-26 19:56 梨樹陽光 閱讀(2566) | 評論 (2)編輯 收藏

這是Riot的Design Director Tom Cadwell專門為中國玩家寫的講解匹配系統工作原理的帖子。


同時為了讓大家更好的理解匹配系統,如果您覺得您遇到了特別不公平的匹配,請回復游戲開始時間和比賽結束截圖,我們會調查該局匹配是如何完成的,坑爹的玩家是為何加入到這一局的。


很多人抱怨看不懂,我來個精簡比喻版的:

有個籃球聯盟,有無數個球員和大概20個等級的聯賽。


所有球員都是10級聯賽的成員,他們自由組合互相比賽,贏的人,升級到11級聯賽,輸的人降到9級聯賽。


然后每個等級聯賽再次開賽,又有的人升級有的人降級,最終這20級的聯賽都有球員參加。


我們的大量的數據證明,一個球員的水平,會讓其穩定在大約3個聯賽之間,也就是科比是參加20級聯賽的,且當他和4個17級聯賽的人組隊,基本不會輸給17級聯賽的人。且,把科比降到10級聯賽,他會輕松的在20局之內回到20級。


理想情況下,球員都是在跟自己同樣經歷的球員玩,一個中等水平玩家完全不會匹配到科比,科比也不會匹配到剛玩游戲的玩家。


事實上匹配系統的分級會比這個更復雜更智能,采用的是國際象棋所采用的elo系統。


再增加個FAQ:

Q:系統為了保持勝率50%,是否會在我連勝后故意塞給我一些菜隊友讓我輸?

A:系統的目的不是為了保持你的勝率,而是讓水平差不多的玩家一起玩。當你和水平差不多的玩家一起玩時勝率會趨近50%,所以,系統是不會故意坑你的。


Q:我才100勝,為什么系統老匹配600勝的玩家給我?

A:勝場并不能反應一個人的水平。如果把匹配系統比作跑步,練習了3年才能跑進11秒的和第一次就跑進11秒的人我們是同等看待的。匹配系統基于水平而不是基于經驗。


Q:我勝率60%,為什么匹配40%勝率的隊友、60%勝率的對手給我?

A:勝率也不能反映水平。匹配系統不但要看你是否贏了,也要看你贏了誰。就像war3的sky在職業圈勝率其實并不高,但是虐一般的玩家勝率是100%。同樣水平的玩家,會因為隨機匹配到對手的關系,勝率會40%~60%不等。


Q:你說水平差不多,為什么我覺得他們這么菜?

A:匹配系統提供的是公平的機會,而未必是你理想的結果。我們能追求系統公正,但是無法預測玩家單局內的表現。

系統100%匹配曼聯對陣皇馬,但是不能保證某一次曼聯不會4:0碾壓皇馬,且在這局中,C羅表現yts,完全就在拖后腿。或者曼聯也可能連勝皇馬3次之類的。但是,系統只會把曼聯去匹配皇馬而不會出現曼聯對陣中超深圳隊。具體到某一局是皇馬贏還是曼聯贏取決于那一場的排兵布陣,臨場發揮,以及戰術意圖。


如果這個坑爹玩家真的不在你的水平等級,他就會一直坑隊友,一直輸,等級一直降低,這樣會讓他離開你的匹配范圍,讓他不再可以和你匹配到。根據我們的數據,玩家的elo基本是穩定在較小范圍內的。這也就是深圳隊和皇馬的差距,也是中國國家隊能贏法國隊,確永遠打不進世界杯的理由。


系統沒辦法給你完美隊友,玩家會因為很多原因發揮不好:使用不會的英雄、打了不想打的位置、玩法風格和隊友不夠搭配,前期不利想掛機等等。但是你和對方玩家遇到這種情況的概率是相同的,系統并不會偏袒任何一方。所以想要完美隊友,請和朋友組隊,不過那樣你也會碰見更厲害的對手。


如果大家在如何鑒定玩家水平上有好的想法歡迎提,但是如果想要通過抱怨玩家游戲內表現來證明匹配系統不公平,就是在和風車決斗了。每個人的看法都不一樣的,系統判斷他和你在遇到同樣的隊友和對手時候,勝率差不多,這也是我們目前能做到最好的了。




以下是文章的正文。


概述:

匹配系統的目的如下,優先級從高到低:


1、  保護新手不被有經驗的玩家虐;讓高手局中沒有新手。

2、  創造競技和公平的游戲對局,使玩家的游戲樂趣最大化。

3、  無需等待太久就能找到對手進入游戲。



匹配系統盡其所能的匹配水平接近的玩家,玩家的水平是來自他們在此之前贏了誰以及他們對手的水平。當你戰勝對手,系統會認為你更強,當你輸給對手,系統會認為你更弱。雖然這對于某一局游戲并不是那么的公平,但是長期來看,對于多局游戲是相當的公平:因為好的玩家總會對游戲結果造成正面的、積極的影響。我們使用了這樣一個方法測試:給水平高的玩家一個新帳號,然后看他們游戲數局后的結果。我們通過大量的測試來證明了我們的想法。



并且,匹配系統知道預先組隊的玩家有一些優勢,如果你是預先組隊,會給你一些更強的玩家。我們用一些非常巧妙的數學方法來解決預先組隊的玩家VS solo玩家的匹配公平問題。我甚至讓兩個數學博士來驗證,他們都說給力!



匹配是怎么完成的?


首先,系統將你放進適當的匹配池里——根據游戲模式(匹配模式、排位solo/雙人、排位5人、其他模式等等)


然后,系統會嘗試將匹配池里的人分到更細的匹配池里——5人組隊 VS 5人組隊,低等級新手 vs 其他一些低等級新手,如此這般。


當你在匹配池中,系統會開始嘗試找到合適的配對,目標是撮合一個雙方獲勝機會都為50%的游戲。



第1步:確定你的實力:


*如果你是solo,就直接使用你的個人匹配分(也就是elo值,匹配模式和排位賽有不同的匹配分)

*如果你是預先組隊的,你的匹配分是你隊伍的平均分,并且會根據你組隊的規模稍微提高一些,這樣才能保證你匹配到更強的對手來抵消你組隊的優勢。我和一個計算機生物學的博士(Computational Biology Ph.D)通過研究成百上千的游戲結果,計算出了預先組隊到底有多大的優勢。我們還在幕后做了一些其他調整,比如新手和高玩組隊,比如某地圖上藍隊和紫隊的玩家哪個更有優勢,諸如此類。



第2步:確定你合適的對手:


*首先,系統會基于你的elo值,給你匹配跟你非常相近的玩家。最終,系統會放寬匹配的條件,給你一些不是那么完美的匹配,因為你肯定也不想永遠匹配不到人。

*新手會得到一些特殊的保護,通常新手只會匹配到其他新手(在成熟的服務器里,這個比例達到了99%+。除非這個新手和一個高級玩家朋友預先組隊)



第3步:確定匹配:


*最終,系統會匹配10個大體上同水平、同等級的玩家,促成一個游戲。

*系統會嘗試平衡這個隊伍,盡量使雙方的獲勝機會都為50%。在絕大多數時間,誤差會在3%之內——類似50/50,49/51,48/52。實際上的獲勝機會會有一點點差別(會在Q&A里面回答這個問題),但是我們的研究標明,在絕大多數情況下,這實際上是一個非常精確的預測。



長期來講,我的匹配分(Elo值)是如何被測量的?

我們使用了一個修改過的ELO系統。ELO系統的基本要點通過使用數學比較兩個人的積分,來預測兩人的比賽結果——類似“A和B比賽數局,A會贏掉75%的局”。



然后,比賽結果出來了。如果你贏了,你會加分,如果你輸了,你會被扣分。如果你是“出人意料”的贏了(系統認為你輸的可能性更大),你會贏得更多的分數。額外的,如果你是一個新玩家,你會加分減分更快,以便于你可以快速的進入到你的水平等級。長期來看,這意味著好的玩家會得到高的匹配分,因為他們總是超過系統的預期,他們會不斷加分直到系統可以正確的預測他們的勝率。



我們修改這個系統給團隊比賽使用,基本概念是:基于該團隊的所有玩家,得到一個團隊ELO值。如果你的隊伍勝利,系統會假設該隊伍的所有玩家都要比系統猜測的“更強”,并且加分。雖然有一些問題,但是總體上來講是有效的,特別是玩家預先組隊的時候。



舉例,本人在北美的服務器上有2000的普通匹配模式elo。如果我建一個小號,就算沒有天賦和符文,我打到8級的時候就已經有1800elo了。這個系統并不完美,但是確實能夠讓玩家快速的接近自己水平所在的位置。



當你才開始玩的時候,我們也對ELO做一些微調,讓你更快的進入你水平所在的位置。

*我們有大量的,有優先級的方法來鑒定一個玩家,相比一個標準的新玩家是否更有技巧,更猛。如果發現是的,我們會在幕后提高他的elo一個檔次。

*我們同樣也會分辨真的菜鳥新手。

*提升等級也會極大的提高你的elo值。這個也將幫助系統將30級滿級的召喚師和低等級的召喚師區分開來



如果你想知道ELO系統的理論,以及更多細節,你可以看看這:

http://en.wikipedia.org/wiki/Elo_rating_system

http://zh.wikipedia.org/wiki/ELO



呃,等等,你是怎么處理組隊玩家 vs solo(單排)玩家的?

我們大多數情況下,會通過將5人組隊的隊伍匹配給另外一個5人組隊的隊伍來避免這種情況的發生(幾乎是所有情況下)。



對于“部分”組隊,我們進行了大量的研究,發現優勢并沒有想象的那么大,所以我們也會把他們混到solo(單排)的玩家里。我們發現有大量的因素會影響到組隊優勢的大小:從預先組隊的規模(比如2、3、4、5組隊),到組隊玩家的水平,到高玩帶菜鳥的組合,到玩家水平不同而導致的情況不同,以及其他的一些必須考慮到的微妙因素。這個要比一些我們曾見過的點對點算法-將任意的統計數據雜糅在一起猜測分數-要可靠的多 

發現這些優勢,我們就知道對于預先組隊的隊伍,需要提高多少elo值,來達成一個公平的匹配,確定一個適當的,在數學上合理的調整。結果在有些情況下非常令人驚訝(同時會校正統計數據)。



雖然我們不會給出精確的數值,因為這是商業機密,但是我們可以告訴您:

*5人組隊只是比5個路人稍強。

*部分組隊只是比5個路人略強。

*菜鳥5人組隊并不會帶來太大的優勢,但是高玩組隊會有很大的優勢。

*團隊實力方差高的隊伍,會比方差低的隊伍更強。(方差簡單來說,是在平均值相同的情況下反應各個元素的大小差異,方差大表示差異大,高方差的隊伍類似高玩帶低玩,低方差的隊伍各個隊員實力接近。)

*這說明了大體上,高水平玩家的Carry作用(可以理解為帶領或者大腿),比低水平玩家的送人頭作用(feeder)要強力。



好吧…那為什么要把預先組隊的玩家和非組隊玩家匹配到一起?

這是一些原因:


*這會幫助系統更快的找到適合你的匹配分,讓系統更快的給你公平的匹配。這個的工作原理是,如果你組隊,會減低運氣所帶來的成分,如果你單排,你的隊友的好壞將對你輸贏的影響更大。如果你預先組隊,你會和你水平差不多的玩家組成隊伍,你隨機遇到猛男/坑爹隊友幾率會更小。因為游戲的結果更多來自你和水平相近的朋友的表現,而不是隨機因素,所以你的匹配分會更快的到達精確的值。

*我們希望玩家可以和自己的朋友一起玩,因為這樣會讓他們玩的更有樂趣。你也不可能為5v5的游戲設置單獨的2人匹配池或者3人匹配池,你需要組合他們來讓系統工作。我們選擇包含5人組隊,因為這非常有樂趣。如果我們以后有足夠大的匹配池,我們可能會將5人組隊和部分組隊區分開來,但是數據告訴我們,這基本不會提升匹配的公平程度,兩者的效果基本相同。



其他一些常見的問題:


Q為什么不加入一些其他的細節,類似擊殺數等等來確定我的匹配分?

A因為這是有偏差的,并且因為非常難以給擊殺數這個數值來評分,你使用一個gank英雄的時候(類似老鼠和易大師),要殺多少人才能算是好的呢?而且這會讓好的輔助玩家非常吃虧,因為他們的目的就不是拿人頭,甚至會為了自己的Carry擋死。最后,玩家會為了刷數據,故意拖長游戲時間,然后拿大量farm對方的人頭,而不是為了贏得比賽。我們盡量把測量玩家水平和激勵玩家的機制放到努力取勝上面,我們避免了一些不必要的周邊行為,而這些行為既沒樂趣,還會擾亂匹配系統。



Q我非常憤怒,因為匹配系統老給我坑爹隊友(feeders,送人頭的)。為什么不阻止這種情況發生?

A我們的確有試圖阻止這種情況發生,但是如果你被匹配到一個明顯很弱的玩家,這也說明匹配系統同時匹配給你了一個或者多個強力的玩家。根據我們的研究,我們發現Carry(大腿)對隊伍的帶領作用要比feeder(送人頭,坑爹)的坑爹作用更強。原因是在LOL里,多次擊殺同一個玩家的收益是會遞減的,并不像其他的同類游戲。我們的分析標明,在平均elo相同的情況下,提高或者降低這個隊伍的某個玩家的elo值100(其他玩家相應降低/提高以保持平均分相同),整個隊伍的實力會提高約7點elo值。這也表明,LOL中Carry的作用要比feeder的作用更給力一些。確實,有時候你會因為匹配到feeder而輸掉這一局比賽,但是那是因為你們隊的Carry不夠給力。



Q這樣的話,如果我連勝了數盤,我是不是會被匹配到一些完全不可戰勝的對手?

A不全是。連勝導致你的匹配分會提高,你會不斷遇到更強的對手——但是我們并不是故意的讓你的勝率保持在50%的,我們的目的只是為了系統能夠正確的預測游戲結果。最終,你會達到你的極限,你將會大致保持50%的勝率。比平均水平高的玩家,往往勝率會比50%略高,因為比他們弱的玩家更多,比他們強的玩家更少。所以匹配時,往往會略微“向下匹配”。對于排位頂尖的高端玩家,他們經常會有90%的勝率。



Q你們會如何設計固定的隊伍?類似WOW的競技場隊伍?

A這是一個非常好的想法,并且讓我們有機會設計出更好的匹配系統。我們遲早會做這個,并且使用我們開發的新方法。我們需要檢驗并且搞清楚你大體上有多強力(例如你的個人積分),同時允許你創建/解散隊伍。這是個非常大的工程,但是我們對此非常有激情~



Q如果匹配系統真的那么公平,那為何我老遇見那種一邊倒的比賽?

A有兩個原因。第一,LOL有時候“雪球效應”會非常明顯。前期太差的表現會導致游戲讓人感覺非常一邊倒。特別是某些隊伍,如果他們開始很順風,就會一直很順風。我們遇到過同樣的隊伍,第一局25-5取勝,第2局確以類似的比分輸掉。第二個原因是,玩家發揮的并不好,隊伍選取陣容也不好。要進行一局勢均力敵的比賽,你需要平衡玩家水平和平衡陣容的選取。有時候玩家選了一個比較渣的陣容,比如5個近戰dps,或者3坦克2法師之類的,或者沒選打野英雄而對面有。這樣的話,盡管你的隊伍實力也很不錯,但是情況往往慘不忍睹。



Q為什么我作為一個高等級玩家,有時候會匹配到一些低等級玩家?他們看上去都是來送人頭的。

A當一個高等級玩家和一個低等級玩家組隊,這是一個非常令人頭疼的問題。我們希望玩家可以和自己的朋友一起玩,并且希望這是一種愉快的體驗。但是我們并不希望將一部分人的快樂建立在另一部分人的痛苦之上,所以我們往往將這種組合評分更高,保護新玩家不會被高等級玩家虐待。非常不幸的是,不管我們怎么做,我們把這樣的組合匹配到任何的游戲中,都有可能造成不愉快的體驗。因此,我們計劃將實施一個“不平衡組隊”的隊列,類似我們盡量將5人組隊匹配給5人組隊。



Q我20級了,然后我被匹配到了一些10級的和一些29級的,怎么回事?

A當不同等級的玩家組隊,我們會使用他們的平均等級來作為匹配的參考。等級并不是匹配系統的主導參數——匹配系統通常是使用實力來匹配——但是我們也會盡量將等級相近的玩家匹配到一起。在預先組隊的情況下,我們沒法替玩家選擇,所以我們盡我們所能,使用平均等級。我們會在這個計算系統里把30級的玩家看作36級,所以我們通常能讓中等級玩家的游戲沒有30級玩家,然而有時候呢,29級玩家能插進來。

posted @ 2012-11-09 14:20 梨樹陽光 閱讀(1636) | 評論 (1)編輯 收藏
記錄下這個小問題。root下可以顯示路徑但是切換到其他用戶時沒有路徑顯示,很不方便。
在/etc/profile中加入
export PS1='\u@\h:\w>',其中\u顯示當前用戶賬號,\h顯示當前主機名,\W顯示當前路徑。然后source一下就搞定了
posted @ 2012-11-08 11:49 梨樹陽光 閱讀(1406) | 評論 (0)編輯 收藏
開門見山,我先提出幾個問題,大家可以先想想,然后我再說出我的方法
1.如何判斷一個數M是否為2的N次方?
2.一個數N,如何得到一個數是M,M是不小于N的最小2的K次方

先說第一個問題,我有兩個思路
第一,可以通過判斷M的二進制中1的個數。而判斷M中1的個數可以通過下面方法獲得
int GetOneCnt(int m) 
{
    if ( m == 0 )
        return 0;
    
    int cnt = 1;
    while(m & (m-1))
    {
        cnt++;
        m--;
    }
    
    return cnt;
}
很明顯M中1的個數為1和M是2的N次方互為沖要條件
第二個思路,我們可以這樣,還是利用M的二進制表示,從最高位開始,以變量high_pos表示第一個1的下標,接著從最低位開始,變量low_pos表示第一個1的下標,如果high_pos=low_pos,則M為2的N次方
int HighestBitSet(int input)
{
    register int result;

    if (input == 0)
    {
        return  -1;
    }

#ifdef WIN32
    _asm bsr eax, input
    _asm mov result, eax
#else
    asm("bsr %1, %%eax;"
    "movl %%eax, %0"
    :"=r"(result)
    :"r"(input)
    :"%eax");
#endif

    return result;
}
int LowestBitSet(int input)
{
    register int result;

    if (input == 0)
    {
        return  -1;
    }

#ifdef WIN32
    _asm bsf eax, input
    _asm mov result, eax
#else
    asm("bsf %1, %%eax;"
    "movl %%eax, %0"
    :"=r"(result)
    :"r"(input)
    :"%eax");
#endif

    return result;
}

再說第二個問題
其實有了第一個問題的思路,這個問題就更好解決了,先判斷一個數是否為2^N,如果是,直接返回,否則返回2^(N+1)
代碼如下
int CeilingPowerOfTwo(int iInput)
{
    if (iInput <= 1)
        return  1;

    int32_t highestBit = HighestBitSet(iInput);
    int32_t mask = iInput & ((1 << highestBit) - 1); // 相當于input對2^highestBit求余
    highestBit += ( mask > 0 );

    return (1<<highestBit);
}
posted @ 2012-10-01 15:53 梨樹陽光 閱讀(1404) | 評論 (4)編輯 收藏
如果你對能很快回答出unicode和utf-8的關系,你可以直接跳過這篇文章。下面我來說說兩者的關系和轉換。(本文使用符號2字代表所有的漢字,英文,數字等)
首先明確一點,UTF-8是UNICODE一種實現方式。
UNICODE:代表一種符號集合,它規定了一種符合的二進制表示,沒有指明存儲方式。(http://www.unicode.org/)
UTF-8:實現了UNICODE,使用多字節的存儲方式。
我們先來考慮幾個問題。
第一,如果使用單字節表示符號,很明顯,完全不夠用
第二,如果使用多字節表示符號,那么,機器在讀取的時候,它怎么知道3個字節表示一個符號,還是表示3個符號
第三,如果使用2個字節表示一個符號,首先,最多能表示65535個字符還是會不夠用,就算夠用,比如ASCII碼這類僅需1個字節就可以表示的符號,用2個字節表示,浪費空間了。
因此,UTF-8孕育而生。
首先UTF-8使用變長表示符號,簡單的說,有的時候用1個字節表示符號,有的時候用2個字節表示符號,這樣解決了浪費空間的問題。那么,如何解決第二個問題的呢,我們得了解下UFT-8的編碼規則。
1.對于單字節的符號,字節第一個為0,后面7為為這個符號的unicode碼
2.對于N字節的符號(N>1),第一個字節前N位為1,第N+1位為0,后面字節的前兩位設為10,剩下可編碼的位,為該符號的UNICODE編碼。
這里我從網上找了一副圖
               Unicode符號范圍 | UTF-8編碼方式
                        (十六進制) | (二進制)
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
下面我具體解釋下這幅圖。
首先看第一行,它的意思是00000000到0000007F的UNICODE編碼,對應的UTF-8的編碼方式為0XXXXXXX(X表示可編碼位,不足的補0)。
第二行表示00000080到000007FF的UNICODE編碼,對應的UTF-8的編碼方式為110XXXXX 10XXXXXX。以此類推
那么,問題是,這個范圍是怎么定的?
很簡單,我們還是從第一行說起。007F,實際有效位只有7位,所以,0xxxxxxx就足矣。但是0800開始,有效位至少為8位,我們得增加一個字節,按照UTF-8的規定,2字節的表示方式為110XXXXX 10XXXXXX,我們的編碼位為11位(X的個數),所以,我們最多可以表示UNICODE編碼位11位的字符,也就是07FF。07FF過了就是0800,有效位至少為12位,我們得用3字節來表示,按照UTF-8的規定,1110XXXX 10XXXXXX 10XXXXXX,最大編碼位為16位,也就是FFFF,最后一行我就不再解釋了。
通過上面這個過程我們了解了,UNICODE轉UTF-8的過程,當然,逆過來就是UTF-8轉換成UNICODE。
我們通過一個例子來演示上面的過程。漢字“楊”,UNICODE的編碼位0x6768,二進制形式為0110011101101000,根據上面的圖,我們知道它屬于第三行,因此,它應該放入1110XXXX 10XXXXXX 10XXXXXX的模板中,結果是11100110 10011101 10101000,十六進制表示為E69DA8。
另外設計編碼問題,我們繞不開另一個問題,就是大端小端的問題,不過這個問題,網上資料很多,也很好實踐,這里我就不多啰嗦了。
posted @ 2012-09-23 22:56 梨樹陽光 閱讀(1815) | 評論 (1)編輯 收藏
進程間通信方式包括了管道,消息隊列,FIFO,共享內存,而共享內存是其中效率最高的。下圖解釋了其效率最高的原因(圖片截取自《UNIX網絡編程》)

我們可以看到上面的拷貝次數是4次,下面則是2次。
接下來我們看看使用,其實網上和書上都有了很多資料,API就是那么幾個。
int shmget(key_t key,size_t size,int shmflag)。
key:可以指定IPC_PRIVATE,那么系統會為你創建一個key,并且返回一個id號。也可以通過ftok函數生成一個key(不了解ftok的童鞋可以動手man一下)。那么為什么要一個IPC既要一個key又有一個ID呢。這里我覺得是為了方便其他進程訪問。進程A創建了一個共享內存,內核為其分配一個ID,這個時候進程B想要訪問,他怎么獲取這個ID(難道需要A把ID發送給B??)。但是我們用一個key就很方便了。事先A,B進程都知道這個key,那么A創建了,B就可以通過事先知道key找到這塊內存。
size:共享內存的大小。如果是獲得一塊內存,則該值應該為0。
shmflag:讀寫權限的組合,可以與IPC_CREAT和IPC_EXCL按位或。當指定一個key時,IPC_CREAT和IPC_EXCL配合使用可以在存在該key的共享內存時返回-1。
當該函數調用成功后,返回一個系統分配的共享內存,并且size大小的字節被初始化為0

void *shmat(int shmid, const void *shmaddr, int shmflg)
有了一塊共享內存后,進程需要映射該內存到進程的地址空間。這個函數就是作用。
shmid就是之前獲得ID,shmaddr如果指定了,就會配合shmflg確定映射地址,不過一般都不這么干的。返回值就是獲得的地址,你可以往里面寫你或者讀你需要的數據了。同時調用了該函數,系統會修改shmid_ds數據。

int shmdt(const void *shmaddr)
解除綁定關系,參數就是我們之前獲取的那個返回地址。(其實我覺得這里參數如果為ID貌似更統一些吧)

int shmctl(int shmid, int cmd, struct shmid_ds *buf)
這個函數主要做一些修改和查詢。比如設置鎖,解鎖,移除一塊共享內存。

簡單介紹了API,還要說一下一些注意的東西
1.共享內存不會把數據寫入磁盤文件中,這個區別于mmap
2.即使沒有進程綁定在共享內存,共享內存也不會消失的。必須通過shmctl或者ipcrm刪除(或者更暴力的方式關掉電腦)

另外我們可能會考慮,系統最多創建多少個共享內存,一個進程最多可以綁定多少個內存,一個共享內存創建的size最大最小值是多少。其實這些設置在/proc/sys/kernel下面,我們也可以自己寫程序來讀取。貼一段代碼用來獲取上面的信息
#define MAX_SHMIDS        8196

int main(int argc,char *argv[])
{
    int i,j;
    int shmid[MAX_SHMIDS] = {0};
    void *addr[MAX_SHMIDS] = {0};
    
    //測試可以創建多少個共享內存
    for ( i = 0;i < MAX_SHMIDS;++i )
    {
        shmid[i] = shmget(IPC_PRIVATE,1024,0666|IPC_CREAT);
        if ( shmid[i] == -1 )
        {
            printf("create shared memory failed,max create num[%d],%s\r\n",i,strerror(errno));
            break;
        }
    }
    
    for ( int j = 0;j < i;++j )
    {
        shmctl(shmid[j],IPC_RMID,NULL);
    }
    
    //測試每個進程可以attach的最大數
    for ( i = 0;i < MAX_SHMIDS;++i )
    {
        shmid[i] = shmget(IPC_PRIVATE,1024,0666|IPC_CREAT);
        if ( shmid[i] != -1 )
        {
            addr[i] = shmat(shmid[i],0,0);
            if ( addr[i] == (void *)-1 )
            {
                printf("process attach shared memory failed,max num[%d],%s\r\n",i,strerror(errno));
                shmctl(shmid[i],IPC_RMID,NULL);
                break;
            }
        }
        else
        {
            printf("max num of process attach shared memory is[%d]\r\n",i-1);
            break;
        }
    }
    
    for ( j = 0;j < i;++j )
    {
        shmdt(addr[j]);
        shmctl(shmid[j],IPC_RMID,NULL);
    }
    
    //測試一個共享內存創建最小的size
    size_t size = 0;
    for ( ;;size++ )
    {
        shmid[0] = shmget(IPC_PRIVATE,size,0666|IPC_CREAT);
        if ( shmid[0] != -1 )
        {
            printf("create shared memory succeed,min size[%d]\r\n",size);
            shmctl(shmid[0],IPC_RMID,NULL);
            break;
        }
    }
    
    //測試共享內存創建最大的size
    for ( size = 65536;;size += 1024 )
    {
        shmid[0] = shmget(IPC_PRIVATE,size,0666|IPC_CREAT);
        if ( shmid[0] == -1 )
        {
            printf("create shared memory failed,max size[%ld],%s\r\n",size,strerror(errno));
            break;
        }
        
        shmctl(shmid[0],IPC_RMID,NULL);
    }
    
    exit(0);
}
好了,下篇開始介紹如何控制讀寫。
posted @ 2012-09-06 19:26 梨樹陽光 閱讀(2256) | 評論 (0)編輯 收藏

1.拷貝構造函數的形式

對于類X,如果它的函數形式如下

a) X&

b) const X&

c) volatile X&

d) const volatile X&

且沒有其他參數或其他參數都有默認值,那么這個函數是拷貝構造函數

X::X(const X&);是拷貝構造函數

X::X(const X&,int val = 10);是拷貝構造函數

 

2.一個類中可以存在超過一個拷貝構造函數

class X {      

public:      

  X(const X&);      

  X(X&);            // OK   

}; 

編譯器根據實際情況調用const拷貝構造函數或非const的拷貝構造函數

 

3.默認的拷貝構造函數行為

a)先調用父類的拷貝構造函數

b)如果數據成員為一個類的實例,則調用該類的拷貝構造函數

c)其他成員按位拷貝

 

4.默認的賦值構造函數行為

a)先調用父類的賦值構造函數

b)如果數據成員為一個類的實例,則調用該類的賦值構造函數

 c)其他成員按位拷貝

 

5.提供顯示的拷貝和賦值構造函數

基本的原則是子類一定要調用父類的相應函數,參考方式

Derive(const Derive& obj):Base(obj)

{

…...

}

 

Derive& operator =(const Derive &obj)

{

if ( this == &obj )

return *this;

 

//方式一

Base::operator =(obj);

 

//方式二

static_cast<Base&>(*this) = obj;

return *this;

}

另外當你的成員變量有const或者引用,系統無法為你提供默認的拷貝和賦值構造函數,我們必須自己處理這些特殊的情況

posted @ 2012-08-31 17:13 梨樹陽光 閱讀(1743) | 評論 (0)編輯 收藏
我們在類里面會定義成員函數,同時我們也希望定義成員函數指針。因此需要解決3個問題,第一怎么定義類的函數指針,第二賦值,第三使用。
我定義一個類,
class A
{
public:
    
int add(int,int);
    
int mul(int,int);
    
int div(int,int);
};

int A::add(int a,int b)
{
    
return a + b;
}

int A::mul(int a,int b)
{
    
return a * b;
}

int A::div(int a,int b)
{
    
return (b !=0 ? a/b : a);
}
我現在想要定義一個指針,指向A類型中返回值為int,參數為兩個int的函數指針。熟悉C的同學馬上會寫出typedef int (*oper)(int,int)。但是這個用到C++里就有問題了,
因為我們知道,類的非static成員方法實際上還有this指針的參數。比如add方法,它實際的定義可能會這樣int add(A* this,int a,int b);因此,我們不能簡單把C語言里的那套東西搬過來,我們需要重新定義這種類型的指針。正確做法是加上類型,typedef int (A::*Action)(int,int)
typedef int (A::* Action)(int,int);

int main()
{
    A a;
    Action p 
= &A::add;
    cout 
<< (a.*p)(1,2<< endl;
    
return 0;
}
Action p = &A::add;這個就是賦值語句
調用的時候注意,直接這樣(*p)(1,2)是不行的,它必須綁定到一個對象上
(a.*p)(1,2);我們也可以把類的函數指針定義在類的聲明里。
class A
{
public:
    
int add(int,int);
    
int mul(int,int);
    
int div(int,int);

    
int (A::*oper)(int,int);
};
上面這種方式是針對非static成員函數的,那么static成員函數的函數指針應該怎么定義呢,還能用上面這種方式嗎?我們知道static成員函數是沒有this指針的,所以不會加上的類的限定,它的函數指針定義方式和C里面的函數指針定義方式一樣的。
class A
{
public:
    
int add(int,int);
    
int mul(int,int);
    
int div(int,int);

    
static int sub(int,int);
};

int A::sub(int a,int b)
{
    
return a - b;
}

int main()
{
    
int (*pp)(int,int= &A::sub;
    cout 
<< (*pp)(10,5<< endl;
    
    
return 0;
}
總結起來,非static成員函數的函數指針需要加上類名,而static的成員函數的函數指針和普通的函數指針一樣,沒有什么區別。
另外注意一點的是
如果函數指針定義在類中,調用的時候有點區別。
class A
{
public:

    
int add(int,int);
    
int mul(int,int);
    
int div(int,int);

    
int (A::*pfunc)(int,int);
};

int main()
{
    A a;
    a.pfunc 
= &A::add;
    cout 
<< (a.*(a.pfunc))(1,2<< endl;
    
return 0;
}
posted @ 2012-08-14 17:17 梨樹陽光 閱讀(1428) | 評論 (0)編輯 收藏
僅列出標題  下一頁
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美视频一区| 99精品国产在热久久| 小黄鸭精品aⅴ导航网站入口| 欧美午夜一区| 亚洲性图久久| 亚洲男人的天堂在线| 国产一区二区三区在线观看免费| 欧美主播一区二区三区| 久久国产高清| 亚洲国产高清高潮精品美女| 亚洲国产综合视频在线观看| 欧美不卡高清| 亚洲一区二区三区乱码aⅴ蜜桃女| 亚洲一区二区精品视频| 国产一区二区欧美日韩| 亚洲电影成人| 国产精品免费一区二区三区在线观看 | 久久久av毛片精品| 亚洲精品免费电影| 99在线热播精品免费99热| 国产精品免费小视频| 欧美日本韩国一区二区三区| 西西人体一区二区| 久热精品视频在线观看一区| 亚洲一区二区三区在线视频| 欧美有码在线观看视频| 日韩视频在线免费| 久久人人看视频| 在线视频欧美精品| 久久国产精品久久久久久电车 | 美日韩精品免费| 亚洲视频www| 久久久久久9| 亚洲视频高清| 久久婷婷成人综合色| 亚洲永久免费| 欧美国产1区2区| 久久夜色精品国产亚洲aⅴ| 欧美日韩1080p| 免费不卡亚洲欧美| 国产精品稀缺呦系列在线| 亚洲福利免费| 狠狠色2019综合网| 亚洲视频在线视频| 99爱精品视频| 免费观看一区| 久久久久久日产精品| 国产精品理论片| 亚洲乱码国产乱码精品精可以看| 黄网动漫久久久| 亚洲女人小视频在线观看| 一区二区三区国产在线观看| 久久综合九色综合久99| 久久综合九色综合欧美狠狠| 国产九区一区在线| 亚洲一区三区电影在线观看| 一区二区冒白浆视频| 欧美高清在线精品一区| 欧美国产日本在线| 亚洲动漫精品| 久久久久国产精品www| 久久精品99无色码中文字幕| 国产精品免费看| 亚洲综合首页| 久久riav二区三区| 国产欧美一区二区三区在线老狼| 亚洲视频精选在线| 欧美一区二区三区免费在线看| 欧美午夜片在线观看| 宅男精品视频| 亚洲欧美第一页| 国产精品视频yy9099| 亚洲欧美激情四射在线日| 午夜精品一区二区三区在线| 国产精品久久亚洲7777| 亚洲一区二区三区国产| 欧美在线免费视屏| 黄色亚洲网站| 欧美第一黄网免费网站| 亚洲精品乱码久久久久| 亚洲综合电影| 国产午夜精品久久久久久免费视| 欧美在线观看一二区| 欧美电影在线免费观看网站| 亚洲美女毛片| 国产精品久久久久久久app| 亚洲专区在线视频| 久久亚洲综合| 亚洲毛片av| 国产精品视频久久久| 亚洲一级网站| 国产日韩在线播放| 久久人人爽人人爽| 亚洲一区二区高清| 亚洲精品一区在线| 欧美在线视频一区二区| 永久555www成人免费| 欧美日韩91| 久久aⅴ国产欧美74aaa| 亚洲国产毛片完整版 | 亚洲美女诱惑| 国产精品区免费视频| 久久久人成影片一区二区三区 | 翔田千里一区二区| 在线看不卡av| 国产精品久久久一本精品| 久久在精品线影院精品国产| 一本色道久久综合亚洲精品不卡 | 午夜国产精品视频| 亚洲国产婷婷综合在线精品 | 久热这里只精品99re8久| 一区二区三区久久久| 欧美成人精品在线播放| 亚洲欧美在线aaa| 亚洲美女啪啪| 亚洲国产成人精品女人久久久| 欧美午夜一区二区| 欧美精品国产| 美女主播精品视频一二三四| 亚洲欧美日韩网| 日韩亚洲视频| 亚洲国产婷婷| 老鸭窝91久久精品色噜噜导演| 亚洲天天影视| 99国产欧美久久久精品| 精品1区2区3区4区| 国产日韩一区二区三区在线播放 | 狼狼综合久久久久综合网 | 亚洲小说欧美另类社区| 亚洲国内精品在线| 亚洲电影第1页| 欧美大香线蕉线伊人久久国产精品| 午夜视频一区在线观看| 亚洲女人天堂成人av在线| 一本色道**综合亚洲精品蜜桃冫 | 国产日韩av一区二区| 欧美日韩一二三区| 欧美激情亚洲精品| 欧美精品久久久久久| 欧美激情综合网| 欧美电影免费观看高清完整版| 久久亚洲欧美| 久久综合五月| 欧美成人一二三| 欧美国产日本韩| 欧美国产精品中文字幕| 女女同性女同一区二区三区91| 老司机67194精品线观看| 久久综合久久88| 欧美~级网站不卡| 欧美激情中文字幕乱码免费| 欧美日韩国产色综合一二三四 | 日韩视频中午一区| 欧美日韩亚洲另类| 国产精品扒开腿做爽爽爽软件| 欧美午夜宅男影院| 国产精品成人一区二区艾草| 欧美香蕉大胸在线视频观看| 国产精品久久久久91| 国产日韩欧美精品在线| 国产一区二区剧情av在线| 精品成人一区二区三区四区| 亚洲国产经典视频| 日韩网站在线观看| 亚洲女爱视频在线| 久久精品论坛| 亚洲第一综合天堂另类专| 亚洲另类自拍| 欧美一区二区三区播放老司机| 久久精品色图| 欧美片在线观看| 国产精品一区二区三区成人| 一区二区三区在线视频播放| 亚洲日本视频| 欧美一级成年大片在线观看| 久久综合成人精品亚洲另类欧美| 亚洲国产精品电影在线观看| 一区二区三区 在线观看视频| 欧美尤物巨大精品爽| 欧美激情在线| 国产综合激情| 亚洲色图自拍| 老司机午夜精品| 在线视频中文亚洲| 看片网站欧美日韩| 国产精品久久网站| 亚洲精品影院| 久久久久网站| 99伊人成综合| 久热精品视频在线观看| 国产精品永久| 国产精品99久久久久久久女警| 老司机aⅴ在线精品导航| 中文精品一区二区三区| 久久综合99re88久久爱| 国产精品一区二区视频| 在线一区亚洲| 亚洲福利视频网站| 久久精品日韩欧美| 国产日韩欧美一区二区|