【问题标题】:Encode/Decode id to int(7)将 id 编码/解码为 int(7)
【发布时间】:2013-07-08 09:35:11
【问题描述】:

我有一个用户表,每个用户都有ID(自动增量)存储在表中。我正在尝试将此数字“重新计算”(编码/解码/散列)到另一个长 7 个数字的数字(UID)(我不知道称为此操作的术语)。这可以猜到,我只需要将 ID 的长度“增长”为 int(7),并且我不想在数据库中将 autoinctement 设置为 100000

我不是想从UID 得到ID 我想达到这个目的

id -> calculation -> unique id for user
8 -> calculation -> 1234567
8 -> calculation -> 1234567 
9 -> calculation -> 2569845
1234567 -> calculation -> "not possible" (not necessary just it is not needed)

我尝试过这样有趣的事情(我尝试过md5sha 的各种推导以不同的顺序,但我认为这不是方法)是否有任何本机 php 函数可以做到这一点? (!uniqid())

$new = substr(preg_replace("/[^0-9]/", "", md5(sha1($i))), 0, 7);

这个很棒,但它有冲突(for loop 1 to 10k)

Already in array new: $filled[4268] = 1050014 array old: $filled[2742] = 1050014
Already in array new: $filled[7278] = 3309143 array old: $filled[1682] = 3309143
Already in array new: $filled[9676] = 1785301 array old: $filled[8310] = 1785301

这意味着带有 4268 和 2742 的 ID 将具有相同的 UID

【问题讨论】:

  • 我想在数据库中有 int(7) 以便我可以创建EAN8 条形码。我已经编辑了原始 Q 中的“目的”。
  • 7 在你做 10k 时很短
  • 你有多少用户?另外,你能提供你的主要问题吗?
  • 系统本身一次不会有这么多用户(最多 50 个)。但是有些可能会被删除/禁止,有些可能会在接下来的 10 年内添加。我需要“防弹”解决方案,以便它可以容纳至少 10k 用户。
  • 您可以使用 str_pad 将数字填充为 7 位数字

标签: php hash encode


【解决方案1】:

正如其他人所提到的,这是一个荒谬的要求。任何健全的系统都只会使用透明的 1:1 映射。但我想你必须按照你的吩咐去做,所以......

让我们扩展它以考虑 65535 个 ID。这是 unsigned short 的上限,需要 2 个字节 - 16 位 - 以整数形式存储。所需的输出是 7 个字节,因此通过移动一些位,我们可以生成唯一的、可逆的 ID,这些 ID 直接从原始输入数字派生而来,从表面上看,这些 ID 与输入无关。

但是...我们仍然需要 ASCII 表示为数字。没问题 - 我们只是将数字编码为 3 位块,然后将其与 0x30 进行或运算 - 这意味着每个编码字节都有一个 0 到 7 之间的 ASCII 码点。

一旦我们意识到这一点,我们需要做的就是选择一个系统。为简单起见,我将逐步遍历第 1-16 位,并将它们均匀地分散在 7 个输出字节中。这仍然会产生一些看起来相当可预测的东西——尤其是在低端,它会有很多零,所以我会用一个已知的键对结果进行异或运算来增加它的趣味性。

<?php

// Produces a key of the supplied length
// This will always produce the same result, it just alternates
// the least significant 3 bits of every output byte
function generate_xor_key($length)
{
    $result = array_fill(0, $length, 0);

    for ($i = 0, $bit = 1; $i < $length; $i++) {
        for ($j = 0; $j < 3; $j++, $bit++) {
            $result[$i] |= ($bit % 2) << $j;
        }
    }

    return implode('', array_map('chr', $result));
}

// Encode an ID
// If using a custom key this can be supplied in the 4th argument
// Keys must always be strings with all the bytes in the range 0x00 - 0x08
function encode_id($id, $encodedLength = 7, $rawBits = 16, $key = null)
{
    // Because we are encoding the number into the least significant 3 bits,
    // it doesn't make sense for $rawBits > $encodedLength * 3
    $maxRawBits = $encodedLength * 3;
    if ($rawBits > $maxRawBits) {
        trigger_error('encode_id(): $rawBits must be no more than 3 times greater than $encodedLength');
        return false;
    }

    // Get a usable key
    if ($key === null) {
        $key = generate_xor_key($encodedLength);
    }

    // Start with all bytes at ASCII 0
    $result = array_fill(0, $encodedLength, 0x30);

    // Extract each relevant bit from the input and store it in the output bytes
    for ($position = 0; $position < $rawBits; $position++) {
        $bit = (($id >> $position) & 0x01) << floor($position / $encodedLength);
        $index = $position % $encodedLength;
        $result[$index] |= $bit;
    }

    // Pad the remaining bits with alternation
    // This is purely cosmetic for the output
    for (; $position < $maxRawBits; $position++) {
        $index = $position % $encodedLength;
        $bit = ($position % 2) << floor($position / $encodedLength);
        $result[$index] |= $bit;
    }

    // Convert the result to an ascii string
    return implode('', array_map('chr', $result)) ^ $key;
}

function decode_id($id, $encodedLength = 7, $rawBits = 16, $key = null)
{
    // Get a usable key
    if ($key === null) {
        $key = generate_xor_key($encodedLength);
    }

    // Convert the string to our original bytes array
    $bytes = array_map(
        'ord',
        str_split(
            str_pad($id, $encodedLength, '0', STR_PAD_LEFT) ^ $key,
            1
        )
    );

    $result = 0;

    // Put the number back together
    for ($position = 0; $position < $rawBits; $position++) {
        $index = $position % $encodedLength;
        $bit = (($bytes[$index] >> floor($position / $encodedLength)) & 0x01) << $position;
        $result |= $bit;
    }

    return $result;
}

http://codepad.org/hfZ4YBKI

每个连续的 ID 都与前一个非常相似 - 通常只有 1 个数字发生了变化 - 但对于未经训练的人来说,它与标准的垂直计数器相差甚远。

如上所述,这种机制实际上可以解释 21 位熵,因此可以生成 2097152 个唯一 ID(包括零)。

【讨论】:

  • 非常好的答案,这就是我“寻找”的。解释得很好,谢谢。
  • 旁注:它可以生成任何数字,而不仅仅是 0-7。 (它只需要 7 个字符(数字)长。例如:1237896。我不知道如何用英语说,因为我不是母语人士,因此我使用了 sql 表示法int(7)。再次漂亮的解决方案。跨度>
【解决方案2】:

我所有现有的 ID 都小于 8999999,然后只需将 1000000 添加到现有的 IDS。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-20
    相关资源
    最近更新 更多