我一直在处理youtube样式网址的代码,但我发现了一个错误,我希望有人能告诉我最有效的解决方法。
function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
static $passcache;
if(empty($passcache))
$passcache = array();
$index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$i = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
if (!empty($passKey)) {
// Although this function's purpose is to just make the
// ID short - and not so much secure,
// with this patch by Simon Franz (http://blog.snaky.org/)
// you can optionally supply a password to make it harder
// to calculate the corresponding numeric ID
if(isset($passcache[$passKey]))
$index = $passcache[$passKey];
else {
if(strlen($passhash = hash('sha256',$passKey)) < strlen($index))
$passhash = hash('sha512',$passKey);
$p = str_split($passhash);
array_multisort($p, SORT_DESC, $i);
$index = implode($i);
$passcache = $index;
}
}
$base = strlen($index);
if ($to_num) {
// Digital number <<-- alphabet letter code
$in = strrev($in);
$out = 0;
$len = strlen($in) - 1;
for ($t = 0; $t <= $len; $t++) {
$bcpow = bcpow($base, $len - $t);
$out += strpos($index, $in[$t]) * $bcpow;
}
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$out -= pow($base, $pad_up);
}
}
} else {
// Digital number -->> alphabet letter code
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$in += pow($base, $pad_up);
}
}
$out = "";
for ($t = floor(log10($in) / log10($base)); $t >= 0; $t--) {
$bcp = bcpow($base, $t);
$a = floor($in / $bcp);
$out .= $index[$a];
$in -= $a * $bcp;
}
$out = strrev($out); // reverse
}
return $out;
}
该错误仅在编码单个数字238328时,因为它是我的三个权力的基础。结果它完全分开,因为使用'floor'它没有被注意到,脚本试图添加不存在的第62个字符,只产生一个三字符代码而不是四个...因此'aa'是结果而不是'aaab'。
以下是代码的问题部分:
for ($t = floor(log10($in) / log10($base)); $t >= 0; $t--) {
$bcp = bcpow($base, $t);
$a = floor($in / $bcp);
$out .= $index[$a];
$in -= $a * $bcp;
为了使这更容易,这是获得错误的调用
echo alphaID(238328);
信用:由Kevin Vanzonneveld撰写:kevin dot vanzonneveld dot net,由Simon Franz修改:blog dot snaky dot org并由Stackoverflows优化自己的mattbasta
答案 0 :(得分:1)
你走了:
function preciseDivision($x,$y)
{
// Correct floor's failures by adding a bit of overhead
$epsilon = 0.00000001;
return floor(($x/$y) + $epsilon);
}
function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
static $passcache;
if(empty($passcache))
$passcache = array();
$index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$i = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
if (!empty($passKey)) {
// Although this function's purpose is to just make the
// ID short - and not so much secure,
// with this patch by Simon Franz (http://blog.snaky.org/)
// you can optionally supply a password to make it harder
// to calculate the corresponding numeric ID
if(isset($passcache[$passKey]))
$index = $passcache[$passKey];
else {
if(strlen($passhash = hash('sha256',$passKey)) < strlen($index))
$passhash = hash('sha512',$passKey);
$p = str_split($passhash);
array_multisort($p, SORT_DESC, $i);
$index = implode($i);
$passcache = $index;
}
}
$base = strlen($index);
if ($to_num) {
// Digital number <<-- alphabet letter code
$in = strrev($in);
$out = 0;
$len = strlen($in) - 1;
for ($t = 0; $t <= $len; $t++) {
$bcpow = bcpow($base, $len - $t);
$out += strpos($index, $in[$t]) * $bcpow;
}
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$out -= pow($base, $pad_up);
}
}
} else {
// Digital number -->> alphabet letter code
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$in += pow($base, $pad_up);
}
}
$out = "";
for ($t = preciseDivision(log10($in),log10($base)); $t >= 0; $t--) {
$bcp = bcpow($base, $t);
$a = preciseDivision($in, $bcp);
$out .= $index[$a];
$in -= $a * $bcp;
}
$out = strrev($out); // reverse
}
return $out;
}
这里的问题不是地板,而是浮点精度。除法得到2.99999999,而得失(2.999999)等于2,而不是3.这是因为浮点变量的大小有限。
这就是为什么它不起作用。
我编写了一个函数preciseDivision,它会自动为该部门添加一个非常小的值,以便完成此任务。
我仍然相信这个网址散列问题应该存在更清晰的解决方案。我会看到我能做些什么。
答案 1 :(得分:1)
根据您对其他问题的my answer,尝试将log10($in) / log10($base)
替换为log($in, $base)
。
这避免了将两个对数的结果除以浮点数相关联的不准确性,并为您提供正确的结果。
答案 2 :(得分:0)
添加另一个答案作为第一个答案也有效,尽管这个答案更清晰。
我摆脱了BC数学函数。如果你要使用非常大的整数,这可能不起作用。否则,这是一个更清洁的解决方案:
function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
static $passcache;
if(empty($passcache))
$passcache = array();
$index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$i = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
if (!empty($passKey)) {
// Although this function's purpose is to just make the
// ID short - and not so much secure,
// with this patch by Simon Franz (http://blog.snaky.org/)
// you can optionally supply a password to make it harder
// to calculate the corresponding numeric ID
if(isset($passcache[$passKey]))
$index = $passcache[$passKey];
else {
if(strlen($passhash = hash('sha256',$passKey)) < strlen($index))
$passhash = hash('sha512',$passKey);
$p = str_split($passhash);
array_multisort($p, SORT_DESC, $i);
$index = implode($i);
$passcache = $index;
}
}
$base = strlen($index);
if ($to_num) {
// Digital number <<-- alphabet letter code
// A conversion from base $base to base 10
$out = 0; // End number
$shift = 1; // Starting shift
$len = strlen($in); // Length of string
for ($t = 0; $t < $len; $t++)
{
$out += strpos($index, $in[$t]) * $shift; // $out is a number form alphabet * base^shift
$shift *= $base; // increase shift
}
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$out -= pow($base, $pad_up);
}
}
} else {
// Digital number -->> alphabet letter code
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$in += pow($base, $pad_up);
}
}
$out = "";
// A simple conversion from base 10 to base $base
while ($in > 0)
{
$remainder = $in % $base;
$in = intval(($in-$remainder)/$base);
$out .= $index[$remainder];
}
}
return $out;
}
代码更清晰,也应该更快。 现在更容易看出这只是从基数10转换为基数$ base(62?)而反之亦然。 它不涉及浮点除法,因此它没有上面提到的错误。
如果你需要乘以大整数等等,这可以通过这种方式实现,并且有一些聪明的想法。
添加了BC数学,正如您所说,您需要大整数
function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
static $passcache;
if(empty($passcache))
$passcache = array();
$index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$i = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
if (!empty($passKey)) {
// Although this function's purpose is to just make the
// ID short - and not so much secure,
// with this patch by Simon Franz (http://blog.snaky.org/)
// you can optionally supply a password to make it harder
// to calculate the corresponding numeric ID
if(isset($passcache[$passKey]))
$index = $passcache[$passKey];
else {
if(strlen($passhash = hash('sha256',$passKey)) < strlen($index))
$passhash = hash('sha512',$passKey);
$p = str_split($passhash);
array_multisort($p, SORT_DESC, $i);
$index = implode($i);
$passcache = $index;
}
}
$base = strlen($index);
if ($to_num) {
// Digital number <<-- alphabet letter code
// A conversion from base $base to base 10
$out = '0'; // End number
$shift = 1; // Starting shift
$len = strlen($in); // Length of string
for ($t = 0; $t < $len; $t++)
{
$out = bcadd($out, bcmul(strpos($index, $in[$t]),$shift)); // $out is a number from alphabet * base^shift
$shift = bcmul($shift, $base); // increase shift
}
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$out -= pow($base, $pad_up);
}
}
} else {
// Digital number -->> alphabet letter code
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$in += pow($base, $pad_up);
}
}
$out = "";
// A simple conversion from base 10 to base $base
while ($in > '0') // We're treating integer as a string, so BC math works
{
$remainder = bcmod($in,$base);
$in = bcdiv($in, $base);
$out .= $index[$remainder];
}
}
return $out;
}