-1

I have a table of users each of them has ID (autoincrement) stored in table. I am trying to "recalculate" (encode/decode/hash) this number to another number (UID) that is 7 numbers long (I dont know the term that is called this operation). It can be guessable, I just need to "grow" the length of ID to int(7), and I do not want to set autoinctement to 100000 in database.

I am not trying to get ID from UID I am trying to achieve this

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)

I've tried funny thing like this (I've tried all kinds of derivations of md5, sha in different order and I figured that is not the way) is there any native php function that would do this? (!uniqid())

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

This one is great but it has conflicts in it (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

that means that ID with 4268 and 2742 would have same UID

4

2 に答える 2

2

他の人が述べたように、これはばかげた要件です。正常なシステムであれば、透過的な 1:1 マッピングを使用するだけです。でも、言われたことはやらないといけないと思うので…

これを拡張して、65535 個の ID を考慮してみましょう。これは unsigned short の上限であり、整数として格納するには 2 バイト (16 ビット) が必要です。目的の出力は 7 バイトであるため、いくつかのビットを移動することで、元の入力番号から直接派生した一意の可逆 ID を生成できます。これは一見、入力とは関係ありません。

しかし... ASCII 表現を数値にする必要があります。問題ありません - 数値を 3 ビットのチャンクにエンコードし、それを 0x30 で OR するだけです - これは、エンコードされたすべてのバイトが 0 ~ 7 の ASCII コード ポイントを持つことを意味します。

それを理解したら、あとはシステムを選択するだけです。簡単にするために、ビット 1 ~ 16 をステップ実行し、それらを 7 つの出力バイトに均等に分散させます。これはまだかなり予測可能なものを生成します-特にローエンドでは多くのゼロが含まれているため、結果を既知のキーでXORすることで少しスパイスを加えます.

<?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 は、以前の ID と非常に似ています (通常は 1 桁のみが変更されています)。

上記で実装したように、このメカニズムは実際には 21 ビットのエントロピーに対応できるため、2097152 個の一意の ID (ゼロを含む) を生成できます。

于 2013-07-08T12:18:12.080 に答える
0

既存の ID がすべて 8999999 未満の場合は、既存の IDS に 1000000 を追加してください。

于 2013-07-08T09:42:41.663 に答える