在MySQL中存储扑克牌套牌(单列)

时间:2013-10-01 22:21:36

标签: php mysql playing-cards

我正在玩扑克牌游戏,并且必须在MySQL中存储洗牌。

在一列中存储52张牌的最有效方法是什么?并使用PHP保存/检索它们。

我需要6位来表示从0到52的数字,因此想到将套牌保存为二进制数据,但我尝试使用PHP的pack函数而没有太多运气。我最好的镜头是保存一个104个字符的字符串(52个零填充整数),但这远非最佳。

感谢。

1 个答案:

答案 0 :(得分:3)

我同意,做这些事情并不是必要或不切实际,但为了好玩,为什么不呢;)

根据您的问题,我不确定您是否打算将所有卡编码为一个值并相应存储,或者您是否要单独编码卡。所以我假设前者。

此外,我假设您使用1到52之间的整数值表示一组52张(或项目)。

我看到一些方法,概述如下,并非实际上更好地使用更少的空间,但为了完整而包括:

  1. 使用逗号分隔列表(CSV),总长度为9 + 2 * 42 + 51 = 144个字符
  2. 将每张卡变成一个字符,即一张卡用8位表示,总长度为52个字符
  3. 使用必要的6位编码每个卡并仅连接位而不丢失2位(如第2种方法),总长度为39个字符
  4. 将card-id视为形式为p的多项式中的系数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。
  5. 自然还有其他方法,仅考虑实际,技术或理论方面,通过列出的方法也可以看到。

    对于理论方法(我认为最有趣),了解一下信息理论和熵是有帮助的。基本上,根据对问题的了解,不需要进一步的信息,只需要提供澄清所有剩余不确定性的信息。 当我们使用位和字节时,对于我们来说,在内存使用方面,实际上基于位或字节(如果只考虑没有底层技术和硬件的位序列),它们对我们来说很有意义;也就是说,位代表两种状态之一,ergo允许两种状态的区分。这很简单但很重要,实际上:/

    然后,如果要在基数B中表示N个状态,则需要该基数中的log(N)/ log(B)数字,或者在示例日志(52)/ log(2)= 5.70中。 。 - > 6位。你会注意到实际上只需要5.70 ..位,这意味着6位我们实际上有一个损失。 这是问题转换的时刻:不是单独代表52个州,而是可以表示整个卡片组。多项式方法是一种方法。基本上它起作用,因为它假设52的基数,即卡集表示为1' 29' 31 ....或数学上说:52 + 1 = 1' 1 = = 15十进制,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 .. bits = 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 ..字节

    但由于不必要地使用了部分比特,但仍然会有损失,但数据中的结构完全弥补了这一点。

    所以问题可能是,hej我们可以将多项式与这个结合起来吗?!?11 ?!实际上,我必须考虑到这一点,我已经累了。

    一般来说,了解问题的结构会大大减少必要的内存空间。实际上,在这个时代,对于普通的高级开发人员来说,这种低级别的考虑因素并不那么重要(hej,100kByte的浪费空间,那么什么!)和其他考虑因素加权更高;另外,因为底层技术通常会减少内存使用量,无论是文件系统还是gzip-ed Web服务器响应等等。虽然这些事情的一般知识仍然有助于创建服务和数据结构。

    但后面这些方法非常特定于问题&#34;压缩程序&#34 ;;一般压缩的工作方式不同,作为示例方法,过程按顺序运行数据的字节,对于任何看不见的位序列,将这些添加到查找表中,并用索引(作为草图)表示实际序列。

    有趣的谈话,让我们获得技术!

    第一种方法&#34; csv&#34;

    // 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);
    

    第3种方法,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;
    }
    

    第4种方法,多项式,刚刚概述(因为整数溢出,请考虑使用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);
    }