有没有一种简单的方法可以将唯一的字符串转换为JavaScript中的唯一数字?

时间:2016-03-05 23:31:37

标签: javascript

这几乎是a cross post from Math SE - 虽然对我的问题的解释是相同的,但是在Math.SE上,我要求对我的问题进行数学解决。

我的问题是,我在Math.SE上获得的解决方案是“转换为基础35”,这可能是一个非常好的答案,但我对数学非常可怕,并且不理解如何在我的代码中应用解决方案。我试着抬头a lesson on converting to different bases,这让我很困惑。即使看a question about converting numbers to bases in JavaScript也没有说清楚我是如何将它用于我需要做的事情。

在JavaScript中有一种简单的方法可以解决这个问题吗?以下是完整的问题:

我有一个不寻常的编程问题,它的数学方面让我难过。

我生成了一个包含七个字符的唯一字符串,每个字符都是从这些可能性中随机选择的:ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789例如A6HJ92B我需要将其转换为唯一的数字值。转换后,此随机字符串的两个版本都不能是名称编号。

我可以生成一个数字,而不是在原始id中包含字母,但当然这意味着我必须增加字符串的长度,并且我的应用程序的用户可能想要键入此字符串,如它在应用程序中标识了他的“会话”,所以我想保持简短。

所以我的想法是建立一个这样的表:

A : 1,
B : 2,
C : 3,
D : 4,
E : 5,
F : 6,
G : 7,
H : 8,

... you get the idea ...

5 : 31,
6 : 32,
7 : 33,
8 : 34,
9 : 35

然后我会添加所有数字......

A6HJ92B

A : 1
6 : 32
H : 8
J : 10
9 : 35
2 : 28
B : 2

1+32+8+10+35+28+2 = 116

...但我意识到这是一个有缺陷的想法,因为许多可能的字符串会“碰撞”或等于相同的数字。我需要每个唯一的字符串等于唯一的数字。

所以,即使我将每个角色的值(1*32*8*10*35*28*2 = 5,017,600)相乘,我也认为那里可能存在碰撞。

有没有办法以消除碰撞的方式计算?如果碰撞无法消除,我可以使用哪些方法来最小化碰撞?

4 个答案:

答案 0 :(得分:17)

基本上,您需要injective转换f : S → N,其中S是长度为7且字符为A-Z1-9的JS字符串集合,N为所有JS编号的集合。

正如您所尝试的那样,一种可能的方法是考虑S中的字符串是positional数字编码。

但是,为了获得单射(避免碰撞),你应该将每个字符的值乘以基础的力量。

例如,给出下面的字符值表

0 ⟶  0
1 ⟶  1
⋮      ⋮
9 ⟶  9
A ⟶ 10
B ⟶ 11
⋮      ⋮
Z ⟶ 35

A6HJ92B将变为10×36⁶ + 6×36⁵ + 17×36⁴ + 19×36³ + 9×36² + 2×36 + 11,即22160072099

您可以使用parseInttoString轻松完成转换:

parseInt('A6HJ92B', 36); // 22160072099
(22160072099).toString(36).toUpperCase(); // "A6HJ92B"

如果要使用任意值表,则必须手动编码转换。

请注意,在JS中,数字是64位的双精度浮点数。这意味着有一个有限的精度,你不能存储任意大的整数。它超出此最大值

将无法正常工作
Number.MAX_SAFE_INTEGER; // 9007199254740991
Number.MAX_SAFE_INTEGER.toString(36).toUpperCase(); // "2GOSA7PA2GV"

但是因为你的字符串只有7个字符,所以应该就够了。

答案 1 :(得分:6)

基本上你想要的是Java hashcode的等价物。这是Java中使用的公式:String hashcode formula其中 n 是字符串的总长度, s [ i ]是 i 字符串的字符。基本上你正在做的是:

对于字符串中的每个字符,将它乘以31,将字符串的总长度与字符串中当前字符的索引减1的差值乘以。

答案 2 :(得分:5)

你只是没有以正确的方式思考问题。让我们从你更熟悉的事情开始吧;十进制或基数为10的数字。

号码7158由以下内容组成:

  7 x 10 ^ 3
 +1 x 10 ^ 2
 +5 x 10 ^ 1
 +8 x 10 ^ 0

要表示基数X中的数字,请用X替换10。

所以现在问题变成了如何在基数之间进行转换。最简单的解决方案是重复除以X - 每次将余数转换为基数X并将其添加到输出字符串的前面,直到没有任何剩余,然后使用除法的整数部分进行下一次迭代:

7158 /35 = 204 remainder 18
204 / 35 = 5 remainder 29
5 / 35 = 0 remainder 5

因此在基数35中,十进制数7158由您为[5]选择的符号表示[29] [18]

如果您通过将每个除法的小数部分乘以35来尝试上面的示例,您可能会得到一些非整数的结果 - 计算机以二进制形式工作(并且自动进行基本转换)并且仅使用固定数字数字 - 即取决于你如何进行计算,你可能需要舍入大约0.00001来得到余数为整数。

答案 3 :(得分:4)

你可以乘以位置力:

function StringAdder(obj){
  this.vals = obj;
  this.add = function(str){
    var s = str.split(''), l = s.length, p = Math.pow(10, (l-1)), r = 0;
    for(var i=0; i<l; i++){
      r += this.vals[s[i]]*p; p = p/10;
    }
    return r;
  }
}
var vals = {
  A: 1,
  6: 32,
  H: 8,
  J: 10,
  9: 35,
  2: 28,
  B: 2
}
var sa = new StringAdder(vals);
console.log(sa.add('A6HJ92B')); console.log(sa.add('HJA6B29'));