隨機數環設想


突然腦袋發熱的一個設想,如果是想單純生成不重複隨機數的,請出門左轉有個更好的算法(用數組下標位隨機,隨機到的下標位對應數據扔到最後位置),什麼?你要生成的隨機數範圍很大,浪費空間?來來來,坐下喝茶。
基本思路:
定義一個位長度大於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%左右

該想法的調參還有更多可能性,有興趣或有更好方案的小夥伴歡迎交流~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章