要求
- 不重復
- 隨機
- 11位數字
- 不占存儲
我們都知道11位數字(random)對應有最大值max和最小值min99999999999和10000000000.很簡單的從最小值開始按順序分發到最大值,就滿足了不重復,不占存儲,11位數字的特性。那么接下來就要考慮如何生成隨機數字這個特性?
如何隨機
可以考慮將上面分發的11位id數字進行二進制打亂,就可以生成隨機數字啦。但是呢打亂后的數字長度可能低于11位或者高于11位,那么如何有心機的進行二進制打亂才能保證固定11位呢。
11位數字(random) 不小于min
很自然的可以想到id從0到max-min分發再打亂再加上min肯定不會小于min,但是可能會大于max。
id數字特性
random:是要生成的隨機數字 id:是順序分發的數字
min=<random<=max min=<min+id<=max 0=<id<=max-min
復制代碼
11位數字(random) 不大于max
可以先觀察一下max-min對應的二進制
1010011110100011010110000001111111111
如何進制順序打亂不超過max-min,對于上面二進制除高位之外的第一個為1的位之后取最大值,即上述二進制變為(max_diff):
1001111111111111111111111111111111111
這樣打亂順序前三位保持不變,后面打亂就可以不超過max-min.我這里是低8位和除去前三位的高八位交換的。
使用率
變化后的二進制的使用率有:
max_diff/(max-min)=85899345919/89999999999=95.4%
The code
public class ShortChainUtils {private static final long MIN = 10000000000L;private static final long SUFFIX_ID = ((1L << 34) - 1);private static final long PRE_ID = (7L << 34);private static final long MAX_ID = 85899345919L;public static long next(long id) {if (id < 0 || id > MAX_ID) {throw new IllegalArgumentException("Illegal id: " + id);}// 非線性id = swap(id, 7, 0);id = swap(id, 6, 1);// Id后34位long value = id & SUFFIX_ID;// Id后34位的最低八位和高1-9位交換value = (((value >> 8) | ((value & 255) << 26)));// Id前3位|Id后34位id = (id & PRE_ID) | value;// 加上最小值, 保證生成的11位id += MIN;return id;}/*** xxxx xxxx xxxx x0xx xxxx xx0x xxxx xxxx|0000 0000 0000 0b00 0000 00a0* 0000 0000*/private static long swap(long value, int x, int y) {return (value & (~(1 << x)) & (~(1 << y))) | (((value >> x) & 1) << y) | (((value >> y) & 1) << x);}private ShortChainUtils() {};public static void main(String[] args){Set set = new HashSet();for(int i=0;; i++) {long id = ShortChainUtils.next(i);System.out.println(id +"---"+i);if(set.contains(id)){System.out.println("duplicated");break;}set.add(id);}}復制代碼
不重復id
對于不重復id的分發可以參考:
自增ID的實現
總結
該隨機數字算法生成簡單,效率高,容易擴展(比如你想生成8位,9位可以用類似方法),不用記錄已生成數字去重。
另外還要注意一點的是,要想隨機數字不重復,重點是ID分發不重復。