栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java实现高效随机数算法的示例代码

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java实现高效随机数算法的示例代码

前言

事情起源于一位网友分享了一个有趣的面试题:

生成由六位数字组成的ID,要求随机数字,不排重,不可自增,且数字不重复。ID总数为几十万。

初次解答

我一开始想到的办法是

  • 生成一个足够大的ID池(其实就是需要多少就生成多少)
  • 对ID池中的数字进行随机排序
  • 依次消费ID池中的数字

可惜这个方法十分浪费空间,且性能很差。

初遇梅森旋转算法

后面咨询了网友后得知了一个高效的随机数算法:梅森旋转(Mersenne Twister/MT)。通过搜索资料得知:

梅森旋转算法(Mersenne twister)是一个伪随机数发生算法。由松本真和西村拓士在1997年开发,基于有限二进制字段上的矩阵线性递归。可以快速产生高质量的伪随机数,修正了古典随机数发生算法的很多缺陷。
最为广泛使用Mersenne Twister的一种变体是MT19937,可以产生32位整数序列。

PS:此算法依然无法完美解决面试题,但是也算学到了新知识

MT19937算法实现

后面通过Google,找到了一个高效的MT19937的Java版本代码。原代码链接为http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/JAVA/MTRandom.java

import java.util.Random;


public class MTRandom extends Random {
  
  // Constants used in the original C implementation
  private final static int UPPER_MASK = 0x80000000;
  private final static int LOWER_MASK = 0x7fffffff;

  private final static int N = 624;
  private final static int M = 397;
  private final static int MAGIC[] = { 0x0, 0x9908b0df };
  private final static int MAGIC_FACTOR1 = 1812433253;
  private final static int MAGIC_FACTOR2 = 1664525;
  private final static int MAGIC_FACTOR3 = 1566083941;
  private final static int MAGIC_MASK1  = 0x9d2c5680;
  private final static int MAGIC_MASK2  = 0xefc60000;
  private final static int MAGIC_SEED  = 19650218;
  private final static long DEFAULT_SEED = 5489L;

  // Internal state
  private transient int[] mt;
  private transient int mti;
  private transient boolean compat = false;

  // Temporary buffer used during setSeed(long)
  private transient int[] ibuf;

  
  public MTRandom() { }

  
  public MTRandom(boolean compatible) {
    super(0L);
    compat = compatible;
    setSeed(compat?DEFAULT_SEED:System.currentTimeMillis());
  }

  
  public MTRandom(long seed) {
    super(seed);
  }

  
  public MTRandom(byte[] buf) {
    super(0L);
    setSeed(buf);
  }

  
  public MTRandom(int[] buf) {
    super(0L);
    setSeed(buf);
  }

  // Initializes mt[N] with a simple integer seed. This method is
  // required as part of the Mersenne Twister algorithm but need
  // not be made public.
  private final void setSeed(int seed) {

    // Annoying runtime check for initialisation of internal data
    // caused by java.util.Random invoking setSeed() during init.
    // This is unavoidable because no fields in our instance will
    // have been initialised at this point, not even if the code
    // were placed at the declaration of the member variable.
    if (mt == null) mt = new int[N];

    // ---- Begin Mersenne Twister Algorithm ----
    mt[0] = seed;
    for (mti = 1; mti < N; mti++) {
      mt[mti] = (MAGIC_FACTOR1 * (mt[mti-1] ^ (mt[mti-1] >>> 30)) + mti);
    }
    // ---- End Mersenne Twister Algorithm ----
  }

  
  public final synchronized void setSeed(long seed) {
    if (compat) {
      setSeed((int)seed);
    } else {

      // Annoying runtime check for initialisation of internal data
      // caused by java.util.Random invoking setSeed() during init.
      // This is unavoidable because no fields in our instance will
      // have been initialised at this point, not even if the code
      // were placed at the declaration of the member variable.
      if (ibuf == null) ibuf = new int[2];

      ibuf[0] = (int)seed;
      ibuf[1] = (int)(seed >>> 32);
      setSeed(ibuf);
    }
  }

  
  public final void setSeed(byte[] buf) {
    setSeed(pack(buf));
  }

  
  public final synchronized void setSeed(int[] buf) {
    int length = buf.length;
    if (length == 0) throw new IllegalArgumentException("Seed buffer may not be empty");
    // ---- Begin Mersenne Twister Algorithm ----
    int i = 1, j = 0, k = (N > length ? N : length);
    setSeed(MAGIC_SEED);
    for (; k > 0; k--) {
      mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >>> 30)) * MAGIC_FACTOR2)) + buf[j] + j;
      i++; j++;
      if (i >= N) { mt[0] = mt[N-1]; i = 1; }
      if (j >= length) j = 0;
    }
    for (k = N-1; k > 0; k--) {
      mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >>> 30)) * MAGIC_FACTOR3)) - i;
      i++;
      if (i >= N) { mt[0] = mt[N-1]; i = 1; }
    }
    mt[0] = UPPER_MASK; // MSB is 1; assuring non-zero initial array
    // ---- End Mersenne Twister Algorithm ----
  }

  
  protected final synchronized int next(int bits) {
    // ---- Begin Mersenne Twister Algorithm ----
    int y, kk;
    if (mti >= N) {// generate N words at one time

      // In the original C implementation, mti is checked here
      // to determine if initialisation has occurred; if not
      // it initialises this instance with DEFAULT_SEED (5489).
      // This is no longer necessary as initialisation of the
      // Java instance must result in initialisation occurring
      // Use the constructor MTRandom(true) to enable backwards
      // compatible behaviour.

      for (kk = 0; kk < N-M; kk++) {
 y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
 mt[kk] = mt[kk+M] ^ (y >>> 1) ^ MAGIC[y & 0x1];
      }
      for (;kk < N-1; kk++) {
 y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
 mt[kk] = mt[kk+(M-N)] ^ (y >>> 1) ^ MAGIC[y & 0x1];
      }
      y = (mt[N-1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
      mt[N-1] = mt[M-1] ^ (y >>> 1) ^ MAGIC[y & 0x1];

      mti = 0;
    }

    y = mt[mti++];

    // Tempering
    y ^= (y >>> 11);
    y ^= (y << 7) & MAGIC_MASK1;
    y ^= (y << 15) & MAGIC_MASK2;
    y ^= (y >>> 18);
    // ---- End Mersenne Twister Algorithm ----
    return (y >>> (32-bits));
  }

  // This is a fairly obscure little code section to pack a
  // byte[] into an int[] in little endian ordering.

  
  public static int[] pack(byte[] buf) {
    int k, blen = buf.length, ilen = ((buf.length+3) >>> 2);
    int[] ibuf = new int[ilen];
    for (int n = 0; n < ilen; n++) {
      int m = (n+1) << 2;
      if (m > blen) m = blen;
      for (k = buf[--m]&0xff; (m & 0x3) != 0; k = (k << 8) | buf[--m]&0xff);
      ibuf[n] = k;
    }
    return ibuf;
  }
}

测试

测试代码

    // MT19937的Java实现
    MTRandom mtRandom=new MTRandom();
    Map map=new HashMap<>();
    //循环次数
    int times=1000000;
    long startTime=System.currentTimeMillis();
    for(int i=0;i

测试结果

times:1000000
num:999886
proportion:0.999886
time:374

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持考高分网。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/138938.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号