随机数环设想

本文介绍了一种通过位运算生成不重复随机数的方法,并提供了一个示例代码。该方法适用于需要生成大量不重复随机数的场景,在示例中生成一千万个不重复随机数仅需约1秒。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


突然脑袋发热的一个设想,如果是想单纯生成不重复随机数的,请出门左转有个更好的算法(用数组下标位随机,随机到的下标位对应数据扔到最后位置),什么?你要生成的随机数范围很大,浪费空间?来来来,坐下喝茶。
基本思路:
定义一个位长度大于bitNum的数组位环(使用位运算)每次取的随机数要先去数组对应下标位中判断下标位的数是否为0,若为0则直接返回 并把这个位置置1,如果是1,则寻找1前面的那个位,如果找了一定的数量destoryLen都是1那么将该随机数返回,环终结个数+1,数组元素全部置0,将新数组该随机下标位 置1。

代码实现:

import java.util.concurrent.ThreadLocalRandom;

/**
 * Created by Kowalski on 2017/1/22.
 * 
 */
public class RandomIdWorker {

    /**位运算数组*/
    private static final int[] arr_32 = {
            0x80000000,0x40000000,0x20000000,0x10000000,
            0x08000000,0x04000000,0x02000000,0x01000000,
            0x00800000,0x00400000,0x00200000,0x00100000,
            0x00080000,0x00040000,0x00020000,0x00010000,
            0x00008000,0x00004000,0x00002000,0x00001000,
            0x00000800,0x00000400,0x00000200,0x00000100,
            0x00000080,0x00000040,0x00000020,0x00000010,
            0x00000008,0x00000004,0x00000002,0x00000001,
    };
    /**随机数最大值*/
    private static int bitNum;
    /**随机数长度*/
    private static int circleLenNum;
    /**当前圈数*/
    private static int circleNum;
    /**圈最大值*/
    private static int maxCircleNum;
    /**摧毁圈长度*/
    private static int destoryLen;
    /**环数组*/
    private static int[] arr;
    /**随机数*/
    private static ThreadLocalRandom random = ThreadLocalRandom.current();

    public static void init(int bitNum, int maxCircleNum, int destoryLen) {

        if(destoryLen > bitNum) {
            throw new IllegalArgumentException("destoryLen can not big than bitNum");
        } else {
            /**初始化数组*/
            arr = new int[bitNum / 32 + 1];
            /**初始化参数*/
            RandomIdWorker.bitNum = bitNum;
            RandomIdWorker.maxCircleNum = maxCircleNum;
            RandomIdWorker.destoryLen = destoryLen;

            for(circleLenNum = 10; bitNum / 10 != 0; bitNum /= 10) {
                circleLenNum *= 10;
            }

        }
    }

    public static void main(String... args){

        init(9999, 9999, 100);

        int res = 0;
        long time = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            res = generate();
        }
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(res);
        System.out.println("圈数:" + circleNum);
    }

    public static synchronized int generate(){

        /**取随机数*/
        int ran = random.nextInt(bitNum);
        /**核心代码*/
        int i;
        for(i = 0; i < destoryLen; ++i) {
            if ((arr[ran / 32] & arr_32[ran % 32]) == 0) {

                arr[ran / 32] |= arr_32[ran % 32];

                return circleLenNum*circleNum + ran;
            }
            /**数已存在就取下一个*/
            ran = (ran + 1) % bitNum;
        }
        /**到达最大圈数 从头开始*/
        if(circleNum == maxCircleNum){
            circleNum = 0;

        }else {
            ++circleNum;
        }
        /**清空数组*/
        for (i = 0; i < bitNum / 32 + 1; ++i) {
            arr[i] = 0;
        }
        /**将新数组中ran位设成1*/
        arr[ran / 32] |= arr_32[ran % 32];

        return circleLenNum*circleNum + ran;
    }

}

备注:
1.代码中main方法跑的是随机数最大9999,最大圈数9999,连续100个1出现则环终结的示例
2.该示例在本人电脑中跑,生成10000000个最大值为99999999的不重复随机数大概需要1s左右的时间,期中共消耗了约1272个环,环的平均利用率在80%左右

该想法的调参还有更多可能性,有兴趣或有更好方案的小伙伴欢迎交流~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值