如何生成随机不重复的11位数字

6,123 阅读3分钟

要求

  • 不重复
  • 随机
  • 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分发不重复。