随机数据的 MD5 哈希值列表不统一 (PHP)
List of MD5 Hashes for Random Data Does Not Appear Uniform (PHP)
如果我对 PHP 中的一堆随机数进行哈希处理并将哈希值转换为以 10 为基数,结果是数字 9 永远不会出现在结果整数的末尾。我想我一定遗漏了一些明显的 MD5 哈希工作方式或 PHP 处理它的方式。
我注意到这一点是因为我有一个(所有不同的)字符串列表,需要将它们随机分为两组(90% 的字符串在桶 A 中,10% 的字符串在桶 B 中)。我想我可以散列字符串,转换为 base-10,然后做这样的事情:
if( ( md5_hash_in_base_ten % 100 ) < 90 ) 使用桶 A
否则使用桶 B
但事实证明,第 9 位数字从未出现在结果整数的末尾附近,因此从未选择 B 桶。
我知道可能有上百万种随机分组字符串的方法,但我对该问题的不同解决方案不感兴趣。我只是对我的测试代码的(可能的)奇怪结果感到好奇。
for( $i = 0; $i < 10000; $i++ ) {
$r = rand();
$bc = base_convert( md5( $r ), 16, 10 );
echo $bc . '<br>';
}
一部分结果如下所示:
302600829905161600608260662606624826442
59585669553455458666446844468880068068
330999075520965568846868468088242088640
192131673950084244086840262480428482262
219128507900677482440460800240644480082
255318670176792246888206600668682602264
240208061481025440684246208684488420642
294217394926758646048046684044640488204
278449747058183168002848628868886688226
195713211929924564840668644204640202264
249037264096573760228220842660668480862
207646493898559360028468248404088664884
169051134173421386202080006468046600882
91273057168422960202446286266888840680
289959365012917366428044866648660802042
172462762250895562808826226442626868482
21264346015514864044284484068442686886
37414331404805136842220266424646680664
76064003552382186484240646428006806660
316804269790551588866666266482482808288
142781990240421424242486286048486626288
12211092583070068208404402226428806286
164064659807615146666228064640060626026
336702095492281784288600868224440806802
264447819530445920480408448628866828002
127283138187204864060642440804622660688
220658311731241408862084402042406680248
71873545317929552826606228242842664868
如果我们对数字 9 按 ctrl-F,它看起来像这样:
想法?
PHP 的 base_convert
函数 不适用于任意大的数字 ,如 red box on the function's documentation page 所示。
您可以通过以下方式验证自己:
echo ('a' == base_convert(base_convert('a', 16, 10), 10, 16)) ? 1 : 0;
echo ('abcdefabcdefabcdef' == base_convert(base_convert('abcdefabcdefabcdef', 16, 10), 10, 16)) ? 1 : 0;
这将打印 1
和 0
:将数字 a
(十六进制)转换为十进制并按预期返回,但数字 abcdefabcdefabcdef
(十六进制)导致 base_convert
精度下降。
要解决此问题,您需要使用能够处理任意长度数字的函数。例如,查看文档页面上的 one of the comments(函数 convBase
)。
您使用的数字超出 base_convert 所能处理的范围。请参阅 PHP 文档 here。
并且 MD5 哈希为 128 位,超出了大多数库的预期处理能力。您转换后的结果无法保持精度。在您的情况下,您可以使用 GNU Multiple Precision 库。
<?php
/*use gmp library to convert base. gmp will convert numbers > 32bit*/
function gmp_convert($num, $base_a, $base_b)
{
return gmp_strval ( gmp_init($num, $base_a), $base_b );
}
for( $i = 0; $i < 5; $i++ ) {
$r = rand();
$h = md5($r);
$bc = base_convert( $h, 16, 10 );
$gmp = gmp_convert( $h, 16, 10 );
echo "Random value: " . $r . PHP_EOL;
echo "MD5 Hash: " . $h . PHP_EOL;
echo "Base converted: " . $bc . PHP_EOL;
echo "GMP converted: " . $gmp . PHP_EOL;
}
?>
将输出:
$ php -f foo.php
Random value: 1198279904
MD5 Hash: 714ae450dedfd56314b47f84e1922c9a
Base converted: 150591624287845962826662264228684068862
GMP converted: 150591624287845974934538261676650802330
Random value: 2000471768
MD5 Hash: 6359b22761538dd02822732ba45c66bf
Base converted: 132059299392045104262828066404880468000
GMP converted: 132059299392045115619248080281367504575
Random value: 851022648
MD5 Hash: 1e95df1b73599a92637982bab7814fc4
Base converted: 40655017257670256242606204044284220868
GMP converted: 40655017257670268183638196631434776516
Random value: 711523039
MD5 Hash: e23aff29be3bb611abbb3736fbdd4d07
Base converted: 300711855586863926204240426446264628688
GMP converted: 300711855586863939825593015112763788551
Random value: 953421999
MD5 Hash: a5990cd2bbab7707db05ebd3b468df17
Base converted: 220117300808777322406606064084840664268
GMP converted: 220117300808777304730115892103715806999
如果我对 PHP 中的一堆随机数进行哈希处理并将哈希值转换为以 10 为基数,结果是数字 9 永远不会出现在结果整数的末尾。我想我一定遗漏了一些明显的 MD5 哈希工作方式或 PHP 处理它的方式。
我注意到这一点是因为我有一个(所有不同的)字符串列表,需要将它们随机分为两组(90% 的字符串在桶 A 中,10% 的字符串在桶 B 中)。我想我可以散列字符串,转换为 base-10,然后做这样的事情:
if( ( md5_hash_in_base_ten % 100 ) < 90 ) 使用桶 A
否则使用桶 B
但事实证明,第 9 位数字从未出现在结果整数的末尾附近,因此从未选择 B 桶。
我知道可能有上百万种随机分组字符串的方法,但我对该问题的不同解决方案不感兴趣。我只是对我的测试代码的(可能的)奇怪结果感到好奇。
for( $i = 0; $i < 10000; $i++ ) {
$r = rand();
$bc = base_convert( md5( $r ), 16, 10 );
echo $bc . '<br>';
}
一部分结果如下所示:
302600829905161600608260662606624826442 59585669553455458666446844468880068068 330999075520965568846868468088242088640 192131673950084244086840262480428482262 219128507900677482440460800240644480082 255318670176792246888206600668682602264 240208061481025440684246208684488420642 294217394926758646048046684044640488204 278449747058183168002848628868886688226 195713211929924564840668644204640202264 249037264096573760228220842660668480862 207646493898559360028468248404088664884 169051134173421386202080006468046600882 91273057168422960202446286266888840680 289959365012917366428044866648660802042 172462762250895562808826226442626868482 21264346015514864044284484068442686886 37414331404805136842220266424646680664 76064003552382186484240646428006806660 316804269790551588866666266482482808288 142781990240421424242486286048486626288 12211092583070068208404402226428806286 164064659807615146666228064640060626026 336702095492281784288600868224440806802 264447819530445920480408448628866828002 127283138187204864060642440804622660688 220658311731241408862084402042406680248 71873545317929552826606228242842664868
如果我们对数字 9 按 ctrl-F,它看起来像这样:
想法?
PHP 的 base_convert
函数 不适用于任意大的数字 ,如 red box on the function's documentation page 所示。
您可以通过以下方式验证自己:
echo ('a' == base_convert(base_convert('a', 16, 10), 10, 16)) ? 1 : 0;
echo ('abcdefabcdefabcdef' == base_convert(base_convert('abcdefabcdefabcdef', 16, 10), 10, 16)) ? 1 : 0;
这将打印 1
和 0
:将数字 a
(十六进制)转换为十进制并按预期返回,但数字 abcdefabcdefabcdef
(十六进制)导致 base_convert
精度下降。
要解决此问题,您需要使用能够处理任意长度数字的函数。例如,查看文档页面上的 one of the comments(函数 convBase
)。
您使用的数字超出 base_convert 所能处理的范围。请参阅 PHP 文档 here。
并且 MD5 哈希为 128 位,超出了大多数库的预期处理能力。您转换后的结果无法保持精度。在您的情况下,您可以使用 GNU Multiple Precision 库。
<?php
/*use gmp library to convert base. gmp will convert numbers > 32bit*/
function gmp_convert($num, $base_a, $base_b)
{
return gmp_strval ( gmp_init($num, $base_a), $base_b );
}
for( $i = 0; $i < 5; $i++ ) {
$r = rand();
$h = md5($r);
$bc = base_convert( $h, 16, 10 );
$gmp = gmp_convert( $h, 16, 10 );
echo "Random value: " . $r . PHP_EOL;
echo "MD5 Hash: " . $h . PHP_EOL;
echo "Base converted: " . $bc . PHP_EOL;
echo "GMP converted: " . $gmp . PHP_EOL;
}
?>
将输出:
$ php -f foo.php
Random value: 1198279904
MD5 Hash: 714ae450dedfd56314b47f84e1922c9a
Base converted: 150591624287845962826662264228684068862
GMP converted: 150591624287845974934538261676650802330
Random value: 2000471768
MD5 Hash: 6359b22761538dd02822732ba45c66bf
Base converted: 132059299392045104262828066404880468000
GMP converted: 132059299392045115619248080281367504575
Random value: 851022648
MD5 Hash: 1e95df1b73599a92637982bab7814fc4
Base converted: 40655017257670256242606204044284220868
GMP converted: 40655017257670268183638196631434776516
Random value: 711523039
MD5 Hash: e23aff29be3bb611abbb3736fbdd4d07
Base converted: 300711855586863926204240426446264628688
GMP converted: 300711855586863939825593015112763788551
Random value: 953421999
MD5 Hash: a5990cd2bbab7707db05ebd3b468df17
Base converted: 220117300808777322406606064084840664268
GMP converted: 220117300808777304730115892103715806999