随机生成Long全范围数
- 前言
- 实现思路
- 主要代码
- 分区
- 随机生成过程
- 案例:随机生成100个数
- 朴素的比较
- 总结
前言
使用自带的Random.nextLong()函数生成Long型的长整数,范围比较小,如下图。100个随机数没看见10以内的数字。所以考虑实现随机化生成大范围的长整数。
实现思路
以十进制位按区间随机生产
-
Long型最大和最小数为19位,所以考虑分成38个区间,例如[0,9],[-9,-1],[10,99]…
-
然而,Math.random生成[0,1)之间的小数,意味着取不到区间的右端点。故增加一个区间存放每个区间的右端点的索引。
-
最后的区间划分为
38个十进制位区间和一个特殊区间
,如此就能保证可取长整数范围内的所有数。
主要代码
分区
首先划分出38个十进制位的区间,最后处理特殊区间即可
private static long[][] groupLongRange() {
int unitCnt = 19;
int len = unitCnt * 2 + 1;
long[][] range = new long[len][2];
long p = 1;
for (int i = 0; i < unitCnt; ++i) {
long l,r;
long nl, nr;
nr = -1 * p;
if (i == 0) {
l = 0;
r = p * 10 - 1;
nl = -1 * p * 10 + 1;
} else if (i == 18) {
l = p;
r = Long.MAX_VALUE;
nl = Long.MIN_VALUE;
} else {
l = p;
r = p * 10 - 1;
nl = -1 * p * 10 + 1;
}
range[i][0] = l; range[i][1] = r;
range[i + unitCnt][0] = nl; range[i + unitCnt][1] = nr;
p *= 10;
}
// 存储区间的末尾数字
range[len - 1][0] = 0;
range[len - 1][1] = len - 1;
return range;
}
为了尽可能随机化,将生产的每个区间随机化排序,注意最后一个区间不参与排序。
/**
* left - 排序的左起端点
* len - 排序的子数组长度
*/
private static void randomSort(long[][] arr, int left, int len) {
long start = System.currentTimeMillis();
boolean[] v = new boolean[len];
int cnt = left;
while (cnt < len) {
int k = (int) (left + Math.random() * len);
if (!v[k]) {
swap(arr, cnt, k);
v[k] = true;
++cnt;
}
}
}
private static void swap(long[][] arr, int i, int j) {
long t1 = arr[i][0];
long t2 = arr[i][1];
arr[i][0] = arr[j][0];
arr[i][1] = arr[j][1];
arr[j][0] = t1;
arr[j][1] = t2;
}
随机生成过程
先随机生产数组下标,再随机生成对应下标范围内的数,二次随机。如果第一次随机到最后一个区间时,取得是右端点值。主要过程如下:
int len = range.length;
int ri = (int) (Math.random() * len);
long num = range[ri][0] + (long) (Math.random() * (range[ri][1] - range[ri][0]));
if (ri == len - 1) {
// 因为每个区间的末尾取不到,所以设计多一个空间来存储,每个区间的末尾的下标
num = range[(int) num][1];
}
// num 就是随机生成的长整数
案例:随机生成100个数
可见范围比自带随机化的要广
朴素的比较
以下是从时间维度进行对比
比较项 | 自定义随机化 | 内置随机化 |
---|---|---|
100000 | 7ms | 2ms |
10000000 | 152ms | 77ms |
1000000000 | 14409ms | 7388ms |
总结
自定义随机化过程耗时长,但是生成的随机数范围广