有人能解释一下这个基本转换代码吗?

Can someone explain this base conversion code

本文关键字:转换 代码 一下 能解释      更新时间:2023-09-26
var ShortURL = new function() {
    var _alphabet = '23456789bcdfghjkmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ-_',
        _base = _alphabet.length;
    this.encode = function(num) {
        var str = '';
        while (num > 0) {
            str = _alphabet.charAt(num % _base) + str;
            num = Math.floor(num / _base);
        }
        return str;
    };
    this.decode = function(str) {
        var num = 0;
        for (var i = 0; i < str.length; i++) {
            num = num * _base + _alphabet.indexOf(str.charAt(i));
        }
        return num;
    };
};

我理解encode的工作原理是从十进制转换为自定义基数(在这种情况下是自定义字母/数字)

我不太确定解码是如何工作的。为什么我们用当前的数字乘以基数,然后加上字母表的位置号?我知道要将010以2为底转换为小数,我们可以输入

(2 * 0^2) + (2 * 1^1) + (2 * 0^ 0) = 2

不确定在解码算法中如何表示

编辑:我自己的解码版本

this.decode2 = function (str) {
    var result = 0;
    var position = str.length - 1;
    var value;
    for (var i = 0; i < str.length; i++) {
      value = _alphabet.indexOf(str[i]);
      result += value * Math.pow(_base, position--);
    }
    return result;
  }

这就是我如何编写自己的解码版本(就像我想在纸上转换一样)。我希望有人能更详细地解释解码的第一个版本是如何工作的。还是不明白为什么要用num乘以基数,而num以0开始。

好,那么376作为encode()函数的以10为基数的输出意味着什么呢?它的意思是:

  • 1 * 100 +
  • 5 * 10 +
  • 4 * 1

为什么?因为在encode()中,您除以每次迭代的基数。这意味着,隐式地,在早期迭代中压入字符串的字符的重要性在每次循环中增加一个基数。

因此,decode()函数每次看到一个新字符时,都会将乘以基。这样,第一个数字在它所代表的第一个数字之后的每一个数字都要乘以基数一次,其余的数字以此类推。

请注意,在上面的解释中,154来自字符376在"字母表"列表中的位置。这就是编码/解码机制的工作原理。如果你给你的decode()函数提供一个数字字符串,这个字符串是由一些试图产生正常的以10为基数的数字编码的,那么你当然会得到一个奇怪的结果;这可能很明显。

edit要进一步详细说明decode()函数:暂时忘记特殊的碱基和编码字母。不管涉及的基地是什么,这个过程基本上是一样的。那么,让我们来看一个函数,它将以10为基数的数字字符串解释为数字:

function decode10(str) {
  var num = 0, zero = '0'.charCodeAt(0);
  for (var i = 0; i < str.length; ++i) {
    num = (num * 10) + (str[i] - zero);
  }
  return num;
}

累加器变量num首先初始化为0,因为在检查输入的数字字符串的任何字符之前,唯一有意义的开始值是0。

函数然后从左到右遍历输入字符串的每个字符。在每次迭代中,累加器乘以基数,并将当前字符串位置的数字值相加。

如果输入字符串为"214",则迭代将按如下方式进行:

  • num设置为0
  • 第一次迭代:str[i]2,所以(num * 10) + 22
  • 第二次迭代:str[i]1,因此(num * 10) + 121
  • 第三次迭代:str[i]4,因此(num * 10) + 4214

连续乘以10可以实现代码中对Math.pow()的调用。注意,2乘以10 两次,这实际上是乘以100。

原始代码中的decode()例程做同样的事情,只是不是简单的字符代码计算来获得数字的数值,而是在字母表字符串中执行查找。

decode函数的原始版本和您自己的版本都实现了同样的事情,但原始版本做得更有效。

在下面的赋值中:

num = num * _base + _alphabet.indexOf(str.charAt(i));

…有两部分:

  1. _alphabet.indexOf(str.charAt(i))

    indexOf返回以_base为基数的一个数字的值。在你自己的算法中有这一部分,所以应该很清楚。

  2. num * _base

    这将乘以到目前为止累积的结果。我剩下的回答是关于这部分的:

在第一次迭代中,这没有影响,因为num在那时仍然是0。但是在第一次迭代结束时,num包含该值,就好像str只有最左边的字符一样。它是最左位的以51为基数的值。

从下一次迭代开始,结果乘以基数,这为下一个值的添加留出了空间。它的作用类似于数字移位。

把这个例子输入到decode:

bd35

单个字符表示值8,10,1和3。因为字母表中有51个字符,所以我们使用51进制。所以bd35这代表value:

8*51³ + 10*51² +  1*51  +  3

下面是一个表,每次迭代后的值是num:

                           8
                  8*51  + 10
         8*51² + 10*51  +  1
8*51³ + 10*51² +  1*51  +  3

为了使可视化更清晰,让我们将51的功率放在列标题中,并将其从行中删除:

3        2        1        0
----------------------------
                           8
                  8       10
         8       10        1
8       10        1        3

注意8在每次迭代时如何向左移动,并与基数(51)相乘。同样的情况也发生在10上,只要中从右移动,1和3也是如此,尽管这是最后一个并且不再移动。

因此,乘法num * _base表示base-digit向左移动,为新数字从右移动腾出空间(通过简单的加法)。

在最后一次迭代中,所有的数字都在正确的位置移动,即它们已经乘以基数足够多的次数。

把你自己的算法放在相同的方案中,你会得到这个表:

    3        2        1        0
    ----------------------------
    8
    8       10 
    8       10        1
    8       10        1        3

在这里,没有移位:数字立即被放在正确的位置,即它们立即乘以51的正确幂。

你问

我想从逻辑的角度理解解码函数是如何工作的。为什么我们使用num *基数,并以num = 0开头?

,然后写

我不太确定解码是如何工作的。为什么底乘以a当前数字再加上字母表的位置数字?我要将以2为基数的010转换为小数,我们可以使用

(2 * 0^2) + (2 * 1^1) + (2 * 0^ 0) = 2

解码函数使用一种称为霍纳规则的基本转换方法,使用它是因为它的计算效率高:

  1. 以设置为0的变量开始,num = 0
  2. 将变量num乘以基础
  3. 取最高有效数字(最左边的数字)的值,并将其加到num
  4. 重复步骤2和3,直到还有数字要转换,
  5. 变量num现在包含转换值(以10为基数)

以十六进制数A5D为例:

  1. 以变量设置为0开始,num = 0
  2. 乘以基数(16),num现在仍然是0
  3. 取最高有效数字的值(A10的数字值)并将其加到num, num现在是10
  4. 重复步骤2,将变量num乘以基数(16),num现在是160
  5. 重复步骤3,将十六进制数字5添加到num, num现在是165
  6. 重复步骤2,将变量num乘以基数(16),num现在是2640
  7. 重复步骤3,将十六进制数字D添加到num (add 13)
  8. 没有数字可以转换,变量num现在包含转换后的值(以10为基数),即2653

比较标准方法的表达式:

(10报;162) + (5 ×161) + (13 ×160) = 2653

到霍纳法则的使用:

(((10报;16) + 5) &次;16) + 13 = 2653

,这是完全相同的计算,但重新排列在一种形式,使其更容易计算。这就是decode函数的工作原理。

为什么要用num *基数,并以num = 0开头

转换算法需要一个起始值,因此将num设置为0。对于每次重复(每次循环迭代),num乘以base。这只对第二次迭代有任何影响,但这样写是为了更容易将转换编写为for循环。