最近公司的项目中需要实现一个防伪查询系统,当时一听到这个任务我就自然而然的想到长连接生成短链接这个系统.然后就习惯性的打开百度.
p1.png
p1.png
然后就找到了两个算法
算法一:
- 将长网址md5生成32位签名串,分为4段, 每段8个字节;
- 对这四段循环处理, 取8个字节, 将他看成16进制串与0x3fffffff(30位1)与操作, 即超过30位的忽略处理;
- 这30位分成6段, 每5位的数字作为字母表的索引取得特定字符, 依次进行获得6位字符串;
- 总的md5串可以获得4个6位串; 取里面的任意一个就可作为这个长url的短url地址;
算法二:
- 直接将数据写入数据库,得到自增的唯一ID
- 对ID进行进制转换,将十进制数字转成62或者60进制(去除大小写字母o),就得到所需要的短码.以6位字符串为例能有500多亿中可能性.
但是这两种算法也存在着许多问题
- 算法一虽然最后能得到多段不同的随机字符串,但是在大量的数据中必然产生重复,就需要对生成的字符进行判重校验.
- 算法二通过数据库的自增ID进行进制转换得到的字符串虽然能保证唯一,并且可逆,但是他生成的字符串太有规律,容易被被人推到查询,不符合我系统所需.
改造进制算法(目前我所实现的思路)
进制算法其实就很符合我心中的设想的算法,只要生成的字符串打乱就能容易的得到我所需要的目标.
- 正常情况下62位进制是以数字0-9,小写字母a-z,大写字母A-Z顺序变化,现在以生成7位长的字符串为例,取其中一位作为种子位(放在哪一位都可以),将62进制对应的字符打乱,生成10个字符串作为映射关系(一位种子最多可以生成64种不同的映射关系)
- 随机生成一个数字除以种子数取余作为种子类型,然后进行进制转换就可以得到随机的字符串,如果需要同一个数字生成的字符串唯一,那么可以直接将数字除以种子数取余.
代码如下(go 语言):
var _seeds = []string{
"hLGBDC3IXiT7Qpntu5r4kZRvzc9sFbVMASjdyql01fHNeKWUwx82gmEYP6aJ",
"3KYLSXtADP25ZaqVHNRkFg68x4jcfwB7zshn0UTIvWdmp1EQ9iGrMybeClJu",
"F4DMKB2S8g6ryCexpXtTwl73AYIZVcHh1vjiz0PsqUaGkbNRQnJ5W9uELmdf",
"QzuC6fhbeSlyRMA1LT9iUc4nBVGWYw2HkI8jsrJqtDN537PZKXmFgEdp0vxa",
"eQ4TJmchW1vC2XPdyFfqR0zYEtkx8KjDV6wnNgaBiuILb7Z3p9Us5lrGHMSA",
"rR8eCfVPZty3TFUWNczxGaqLMm1j6JshiDbgA5ndHv0XEp9uQk4lYBKI2w7S",
"cpxi12IayQ6rqWSn5hTGw84VCgMXPb7sDklYUz3AfBFvEjJ0tHNLZedmRu9K",
"U1Qkve80GClx3dbKzcfwEqInPuMiYaAmShr9pZVy5RTWJXB6N7H4L2FjsgtD",
"sXURIcVCg5ZTktxKEMFyBJn6Pp0lYr2f9diwAhGvSeW3b4jaD1zL7NmuQH8q",
"LlC0SkTrbJP7aI2mgM5j8ywzvW6sHfDRnGteXVU1QxB3EhKYpu4cZqA9NFid",
}
const (
_minNum = 777600000
_maxNum = 46656000000
)
func Encode(id int64) (string, error) {
if id < _minNum || id > _maxNum {
return "", errors.New("id not in range")
}
var (
seedIndex int64
seed string //种子
result string
)
seedIndex = id % 10
seed = _seeds[seedIndex]
for id > 0 {
yu := id % 60
id = id / 60
result = string(seed[yu]) + result
}
return result[:3] + strconv.FormatInt(seedIndex, 10) + result[3:], nil
}
func Decode(code string) (int64, error) {
var (
seedIndex int64
seed string
result int64
err error
)
seedIndex, err = getSeed(code)
if err != nil {
return 0, err
}
seed = _seeds[seedIndex]
var i = 1 * 60 * 60 * 60 * 60 * 60
str := code[:3] + code[4:]
for _, ch := range str {
index := strings.Index(seed, string(ch))
result = int64(index*i) + result
i = i / 60
}
return result, nil
}
func getSeed(code string) (int64, error) {
seedIndex, err := strconv.ParseInt(string(code[3]), 10, 64)
if err != nil {
return 0, err
}
return seedIndex, nil
}
func main() {
var id int64 = 1345632346
var code string
code,_ = Encode(id)
fmt.Println(fmt.Sprintf("原ID为:%d,生成的字符串为:%s", id, code))
id = 1345632347
code,_ = Encode(id)
fmt.Println(fmt.Sprintf("原ID为:%d,生成的字符串为:%s", id, code))
id = 1345632348
code,_ = Encode(id)
fmt.Println(fmt.Sprintf("原ID为:%d,生成的字符串为:%s", id, code))
id = 1345632349
code,_ = Encode(id)
fmt.Println(fmt.Sprintf("原ID为:%d,生成的字符串为:%s", id, code))
}
运行结果如下:
原ID为:1345632346,生成的字符串为:pvH6JjJ
原ID为:1345632347,生成的字符串为:1W77BX6
原ID为:1345632348,生成的字符串为:X318j4D
原ID为:1345632349,生成的字符串为:l3u9Khu
可以看到目前所生成的字符串就没什么规律性了.
github源码