數學之美-唯一ID生成算法

一切的合理性都可以通過數學來解釋(自己瞎編的),今天就用數學給大家變個戲法,實現一個唯一ID生成器;而用到的數據知識包括排列組合+質數特性應用:

    先給出一個數學定義:在一個質數集合中隨意取出2n個質數,讓他們兩兩相乘然後加和得m,放入後重新再取出2n個質數,經過相同步驟計算得出z,除非前後取出的質數相同且兩兩相乘的組合也相同,否則m != z(我拿着筆經過一陣噼裏啪啦,驗證得來的)。

   所以當質數集合很大時,可以得到上千萬種的排列組合,即上千萬個唯一id,雖然在看起來還是很雞肋的,但是在分佈式應用,可以作爲snowflake算法生成的唯一id中的seq部分,這樣就不需要依賴任何服務去維護這個seq了,不需要依賴機器配置+中間件,就能實現一個分佈式唯一id生成器(哪天去試着實現試試),通過上千萬種的排列組合將一秒內生成兩個相同id的可能性降到無限接近0。

甩出代碼:

//生成唯一key
package main

import (
    "bufio"
    "fmt"
    "math/rand"
    "os"
    "strconv"
    "time"
)

const MAX_KEYS_CNT = 100000
const PL_BITS = 1024
const PL_BITS_MASK = 990
const PL_BITS2 = 4096
const PL_BITS2_MASK = 4095

var randoms []int64
var xuL     []int64

var keys    map[int64]bool
var cover   int64
var maxKey  int64
var randCt *rand.Rand

func keyRemove(cpRandoms []int64, index int) (int64, []int64) {
    random := cpRandoms[index]
    cpRandoms[len(cpRandoms)-1], cpRandoms[index] = cpRandoms[index], cpRandoms[len(cpRandoms)-1]
    return random, cpRandoms[:len(cpRandoms)-1]
}

func init() {
    randCt = rand.New(rand.NewSource(time.Now().UnixNano()))
    randoms = make([]int64, 0, PL_BITS2)
    xuL     = make([]int64, 0, PL_BITS)
    //測試
    keys    = make(map[int64]bool, MAX_KEYS_CNT)
    fileA, err := os.OpenFile("zs_before.dat", os.O_RDONLY, 0755)
    if err != nil {
        fmt.Printf("file:`zs_before.dat`, err:`%v`\n", err)
    }
    defer fileA.Close()
    fileB, err := os.OpenFile("zs_after.dat", os.O_RDONLY, 0755)
    if err != nil {
        fmt.Printf("file:`zs_after.dat`, err:`%v`\n", err)
    }
    defer fileB.Close()
    scan := bufio.NewScanner(fileA)
    countKey := 0
    for scan.Scan() {
        if countKey >= PL_BITS {
            break;
        }
        intR, err := strconv.ParseInt(scan.Text(), 10, 64)
        if err != nil {
            fmt.Printf("err:`%v`\n", err)
            break;
        }
        countKey++
        xuL = append(xuL, intR)
    }

    scan = bufio.NewScanner(fileB)
    countKey = 0
    for scan.Scan() {
        if countKey >= PL_BITS2 {
            break;
        }
        intR, err := strconv.ParseInt(scan.Text(), 10, 64)
        if err != nil {
            fmt.Printf("err:`%v`\n", err)
            break;
        }
        countKey++
        randoms = append(randoms, intR)
    }
}

func keyCreate() int64 {
    var keyNumber, k int64 = 0, 0
    initCnt := PL_BITS2
    cpRandoms := randoms[:]
    keysN := []int64{}

    xlOffset := randCt.Intn(PL_BITS_MASK)
    cpXuL := xuL[xlOffset:xlOffset+28]    
    for _, key := range cpXuL {
        random := randCt.Intn(initCnt)&PL_BITS2_MASK
        k, cpRandoms = keyRemove(cpRandoms, random)
        keyNumber += key*k
        keysN = append(keysN, k)
        initCnt--
    }
    b, ok := keys[keyNumber]
    if ok && b {
        cover++
    }
    keys[keyNumber] = true;
    if maxKey < keyNumber {
        maxKey = keyNumber
    }
    return keyNumber
}

func main() {
    t := time.Now()
    for i := 0; i < MAX_KEYS_CNT; i++ {
        keyCreate()
    }
    subXuL := xuL[:]
    subRands := randoms[len(randoms)-len(xuL):]
    var sum int64
    for i, num := range subXuL {
        sum += num*subRands[i]
    }
    fmt.Printf("cost:`%v`\n", time.Since(t))
    fmt.Printf("max-key:`%v`, cover-`%d`\n", sum, cover)
}

實測:

看來這個`公式`還是很靠譜的!

    在生成10w個唯一seq測試中,平均出現重複seq的次數爲0.5,平均時間爲174ms;

    在生成100w個唯一seq測試中,平均出現重複seq的次數爲17.6,平均用時僅1.5s (重複次數出現的較多,可能是產生排列組合處的算法可以再優化“這裏依賴產生的隨機數”,因爲集合的大小是1024,排列的長度爲28,有1024*1023*....*997種可能,產生重複seq的概率極低)

總結:

    snowflake算法生成的唯一ID的各個組成部分之間互不依賴,它們共同組成一個固定bit位數的ID,以上算法可構成ID的seq部分;如果ID重複,可重試生成新的ID,這時如再次重複的話,可將請求pass(服務繁忙,請稍後重試--`限流`)。

 

發佈了34 篇原創文章 · 獲贊 3 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章