我正在玩纸牌游戏,并且必须在 MySQL 中存储洗牌的牌组。
在单列中存储一副 52 张牌的最有效方法是什么?并使用 PHP 保存/检索那些。
我需要 6 位来表示从 0 到 52 的数字,因此我想将卡片组保存为二进制数据,但我尝试过使用 PHP 的pack
函数,但运气不佳。我最好的方法是保存一串 104 个字符(52 个零填充整数),但这远非最佳。
谢谢。
我正在玩纸牌游戏,并且必须在 MySQL 中存储洗牌的牌组。
在单列中存储一副 52 张牌的最有效方法是什么?并使用 PHP 保存/检索那些。
我需要 6 位来表示从 0 到 52 的数字,因此我想将卡片组保存为二进制数据,但我尝试过使用 PHP 的pack
函数,但运气不佳。我最好的方法是保存一串 104 个字符(52 个零填充整数),但这远非最佳。
谢谢。
我同意这样做没有必要或不切实际,但为了好玩,为什么不这样做;)
根据您的问题,我不确定您是否打算将所有卡片编码为一个值并相应地存储,或者您是否要单独对卡片进行编码。所以我假设前者。
此外,我假设您有一组 52 张卡片(或物品),用 1 到 52 之间的整数值表示。
我看到了一些方法,概述如下,实际上并非都是为了更好地使用更少的空间,而是为了完整而包括在内:
当然还有其他方法,仅考虑实际、技术或理论方面,通过所列方法也可以看出。
对于理论方法(我认为这是最有趣的),了解一些信息论和熵是有帮助的。本质上,根据对问题的了解,不需要进一步的信息,分别只需要澄清所有剩余不确定性的信息。当我们使用位和字节时,我们最感兴趣的是内存使用,实际上是基于位或字节的(如果您只考虑没有底层技术和硬件的位序列);也就是说,位代表两种状态之一,因此允许区分两种状态。这实际上是微不足道但重要的:/
然后,如果您想在基数 B 中表示 N 个状态,您将需要该基数中的 log(N) / log(B) 数字,或者在您的示例中 log(52) / log(2) = 5.70.. -> 6 位。您会注意到实际上只需要 5.70.. 位,这意味着我们实际上有 6 位损失。这就是问题转换出现的时刻:不是单独表示 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.. bits = 28.1976.. bytes 来表示卡片集. 问题在于,任何这样表示的值都不包含任何我们可以从中得出其语义的结构,这意味着对于 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 Web 服务器响应等。不过,这些事情的一般知识仍然有助于创建您的服务和数据结构。
但是后一种方法是针对特定问题的“压缩程序”;一般压缩的工作方式不同,其中作为示例方法,程序顺序运行数据的字节,对于任何看不见的位序列,将它们添加到查找表并用索引表示实际序列(作为草图)。
够了有趣的谈话,让我们获得技术!
第一种方法“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;
}
第四种方法,多项式,刚刚概述(请考虑使用 bigints,因为整数溢出)
$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);
}