我正在玩扑克牌游戏,并且必须在MySQL中存储洗牌。
在一列中存储52张牌的最有效方法是什么?并使用PHP保存/检索它们。
我需要6位来表示从0到52的数字,因此想到将套牌保存为二进制数据,但我尝试使用PHP的pack
函数而没有太多运气。我最好的镜头是保存一个104个字符的字符串(52个零填充整数),但这远非最佳。
感谢。
答案 0 :(得分:3)
我同意,做这些事情并不是必要或不切实际,但为了好玩,为什么不呢;)
根据您的问题,我不确定您是否打算将所有卡编码为一个值并相应存储,或者您是否要单独编码卡。所以我假设前者。
此外,我假设您使用1到52之间的整数值表示一组52张(或项目)。
我看到一些方法,概述如下,并非实际上更好地使用更少的空间,但为了完整而包括:
自然还有其他方法,仅考虑实际,技术或理论方面,通过列出的方法也可以看到。
对于理论方法(我认为最有趣),了解一下信息理论和熵是有帮助的。基本上,根据对问题的了解,不需要进一步的信息,只需要提供澄清所有剩余不确定性的信息。 当我们使用位和字节时,对于我们来说,在内存使用方面,实际上基于位或字节(如果只考虑没有底层技术和硬件的位序列),它们对我们来说很有意义;也就是说,位代表两种状态之一,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);
}