2013-10-01 34 views
1

我正在玩紙牌遊戲,並且必須在MySQL中存儲洗牌的套牌。在MySQL(單列)中存儲撲克牌套牌

什麼是在一個列中存儲52張卡片的最有效方式?並保存/檢索那些使用PHP。

我需要6位來表示從0到52的數字,因此想到將卡組保存爲二進制數據,但我試過使用PHP的pack函數,但沒有多少運氣。我最好的辦法是保存一個由104個字符組成的字符串(52個零填充整數),但這並不是最優的。

謝謝。

+0

您是如何提高效率的?檢索時間?內存空間?隨機播放速度?這聽起來像是你想要在這裏提高內存效率,這是基於你如何提出問題,所以我必須問,爲什麼要使用MySQL數據庫,如果6位和128位之間的差異是交易斷路器? – Magnum

+0

表格會有很多行,但說實話,這是因爲我很好奇:-) - 它看起來很簡單(存儲和檢索一串比特),但我做不到。 –

+0

同意瑪格南。但爲了給出可能的答案,爲什麼不嘗試chr()? (你不能有小於一個字節的任何東西..)如果它不是字符串友好的嘗試一個二進制1字節列?但出於興趣,你的洗牌過程(在db?)看起來像什麼? – Filou

回答

3

我不同意,這是沒有必要或者說不切實際做任何這一點,但爲了好玩,何不;)

從你的問題我不知道,如果你的目標是在所有的卡編碼爲一個值並相應存儲,或者是否要單獨編碼卡。所以我假設前者。

而且我假定你有一組52張(或項目)與你之間1和52

我看到一些方法,概括如下,不是所有實際使用的更好的整數值表示更小的空間,但包括了被起見完成:使用逗號分隔的列表(CSV),9 + 2 * 42 + 51 = 144個字符

  • 車削每個卡入一個字符,即總長度

    1. 一張卡用8位表示,總長度爲52個字符
    2. 使用那些必要的6位對每個卡進行編碼,並且僅僅連接沒有丟失的2位的位(如在第二種方法中),總長度爲39個字符
    3. 將card-id作爲係數表示爲形式p的多項式(卡)=卡(1)* 52^51 +卡(2)* 52^50 +卡(3)* 52^49 + ... +卡(52)* 52^0我們用來識別卡-組。粗略地說,p(卡)必須位於[0,52^52]的值範圍內,這意味着該值可以用log(52^52)/ log(2)= 296.422865343 ..位或用一個字節長度37.052分別38.

    有天然的序列是進一步的辦法,考慮到單純實用,技術或理論方面,如也可通過列出的方法可見。

    對於理論方法(我認爲這是最有意思的方法),瞭解一下信息理論和熵有幫助。基本上,根據已知的問題,不需要進一步的信息,分別只需要信息來澄清所有剩餘的不確定性。由於我們正在處理比特和字節,因此在內存使用方面,我們感興趣的實際上是基於比特或字節的(如果您僅考慮沒有底層技術和硬件的比特序列);也就是說,位代表兩種狀態之一,即允許兩種狀態的區分。這實際上是微不足道的,但很重要:/

    然後,如果您想在基準B中表示N個狀態,那麼您需要在該基礎中或在您的示例日誌中記錄(N)/ log(B)數字52)/ log(2)= 5.70 ...→6位。你會注意到實際上只需要5.70位,這意味着6位實際上會有損失。 問題轉換出現的時間是:代表52個狀態,而不是單獨表示52個狀態,可以表示整個卡片集。多項式方法是一種做到這一點的方法。基本上它的工作原理是假設基數爲52,也就是卡組被表示爲1'4'29'31 ....或數學上說:52 + 1 = 1'1 == 53十進制,1'1 + 1 = 1'2 == 54十進制,1'52'52 + 1 = 2'1'1 == 5408十進制,

    但是如果你進一步看看多項式方法,你會注意到有一個總數52^52可能的值,而我們只會使用52! = 1 * 2 * 3 * ... * 52,因爲一旦卡被固定,剩餘的可能性就會減小,不確定性或熵也會減少。 (請注意,52!/ 52^52 = 4.7257911e-22!這意味着多項式是完全浪費空間)。如果我們現在要在[1,52!]中使用一個非常理論最小值的值,我們可以用log(52!)/ log(2)= 225.581003124來表示卡組。 28.1976字節。與此相關的問題是,表示爲這樣的任何值都不包含任何我們可以從中導出其語義的結構,這意味着對於每個52!可能的值(如果考慮排除原則,那麼52! - 1)我們需要引用它的含義,即查找52!價值觀,這肯定會成爲記憶矯枉過正。

    雖然我們可以與編碼有序集的熵減少的知識做出妥協。舉個例子:我們用序列中當時所需的最小位數順序編碼每張卡片。因此,假設N < = 52卡仍然存在,那麼在每一步中卡可以用log(N)/ log(2)位表示,這意味着所需位的數量減少,直到最後一張卡時,你不需要有點在第一位。這將給出(請更正)。

    20 * 6位+16 * 5位+8 * 4位+4 * 3位+2 * 2位+ 1位= 249位= 31.125 ..字節

    但由於不必要地使用了部分位,所以仍然會有損失,但數據中的結構完全彌補了這一點。

    所以一個問題可能是,我們可以將多項式與這個?!?11 ?!其實,我必須考慮這一點,我感到很累。

    一般來說,瞭解問題的結構會大大減少必要的內存空間。實際上,在這個時代,對於一般的高層開發人員來說,這種低層次的考慮並不是那麼重要(hej,100kByte的浪費空間,所以!)和其他考慮因素的權重更高;也是因爲底層技術通常會自己減少內存使用量,無論是文件系統還是gzip-ed網絡服務器響應等。儘管如此,這些類型的常識仍然有助於創建服務和數據結構。

    但是後面的這些方法是非常具體的問題「壓縮程序」;一般壓縮的工作方式不同,作爲示例方法,程序按順序遍歷數據的字節,並且對於任何看不見的位序列,將這些序列添加到查找表中並用索引(作爲草圖)表示實際序列。

    有趣的談話已經足夠了,讓我們開始技術!

    第一方法 「CSV」

    // your unpacked card set 
    $cards = range(1,52); 
    
    $coded = implode(',',$cards); 
    $decoded = explode(',',$coded); 
    

    第二方法:1卡= 1個字符

    // just a helper 
    // (not really necessary, but using this we can pretty print the resulting string) 
    function chr2($i) 
    { 
        return chr($i + 32); 
    } 
    
    function myPack($cards) 
    { 
        $ar = array_map('chr2',$cards); 
        return implode('',$ar); 
    } 
    
    
    function myUnpack($str) 
    { 
        $set = array(); 
        $len = strlen($str); 
        for($i=0; $i<$len; $i++) 
         $set[] = ord($str[$i]) - 32; // adjust this shift along with the helper 
    
        return $set; 
    } 
    
    $str = myPack($cards); 
    $other_cards = myUnpack($str); 
    

    第三方法中,1卡= 6個比特

    $set = ''; // target string 
    $offset = 0; 
    $carry = 0; 
    
    for($i=0; $i < 52; $i++) 
    { 
        $c = $cards[$i]; 
    
        switch($offset) 
        { 
         case 0: 
          $carry = ($c << 2); 
          $next = null; 
          break; 
    
         case 2: 
          $next = $carry + $c; 
          $carry = 0; 
          break; 
    
         case 4: 
          $next = $carry + ($c>>2); 
          $carry = ($c << 6) & 0xff; 
          break; 
    
         case 6: 
          $next = $carry + ($c>>4); 
          $carry = ($c << 4) & 0xff; 
          break;  
        } 
    
        if ($next !== null) 
        { 
         $set .= chr($next); 
        } 
    
        $offset = ($offset + 6) % 8; 
    } 
    
    // and $set it is! 
    
    
    $new_cards = array(); // the target array for cards to be unpacked 
    $offset = 0; 
    $carry = 0; 
    
    for($i=0; $i < 39; $i++) 
    { 
        $o = ord(substr($set,$i,1)); 
    
        $new = array(); 
        switch($offset) 
        { 
         case 0: 
          $new[] = ($o >> 2) & 0x3f; 
          $carry = ($o << 4) & 0x3f; 
          break; 
         case 4: 
          $new[] = (($o >> 6) & 3) + $carry; 
          $new[] = $o & 0x3f; 
          $carry = 0; 
          $offset += 6; 
          break; 
         case 6: 
          $new[] = (($o >> 4) & 0xf) + $carry; 
          $carry = ($o & 0xf) << 2; 
          break; 
        } 
    
        $new_cards = array_merge($new_cards,$new); 
    
        $offset = ($offset + 6) % 8; 
    } 
    

    第四方法中,多項式,剛剛概述(請考慮使用bigint,因爲整數溢出)

    $encoded = 0; 
    $base = 52; 
    foreach($cards as $c) 
    { 
        $encoded = $encoded*$base + $c; 
    } 
    
    // and now save the binary representation 
    
    
    $decoded = array(); 
    for($i=0; $i < 52; $i++) 
    { 
        $v = $encoded % $base; 
        $encoded = ($encoded - $v)/$base; 
        array_shift($v, $decoded); 
    } 
    
  • +0

    哇,很好的答案。我仍然不明白爲什麼我不能將原始數據保存到MySQL中 - 如果我現在是存儲數據的最佳方式? –

    +0

    mhm我從來沒有做過自己,但我會認爲這是至少可能的二進制類型的MySQL? - > http://dev.mysql.com/doc/refman/5.0/en/binary-varbinary.html還是你的意思是將單個位存儲到字段中? (我認爲這在技術上是不合理的) – Filou

    +0

    是的,我的意思是單個比特。假設我需要存儲一些數據,並且知道一些關於這些數據的信息,這些數據在比特級別進行了非常高效的加密。 –