zoukankan      html  css  js  c++  java
  • 【JUC源码解析】Exchanger

    简介

    Exchanger,并发工具类,用于线程间的数据交换。

    使用

    两个线程,两个缓冲区,一个线程往一个缓冲区里面填数据,另一个线程从另一个缓冲区里面取数据。当填数据的线程将缓冲区填满时,或者取数据的线程将缓冲区里的数据取空时,就主动向对方发起交换缓冲区的动作,而交换的时机是,一个缓冲区满,另一个缓冲区空。代码如下,很简单,没有加注释。

      1 public class FillAndEmpty {
      2     Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>();
      3     DataBuffer initialEmptyBuffer = DataBuffer.allocate(1024);
      4     DataBuffer initialFullBuffer = DataBuffer.allocate(1024);
      5 
      6     class FillingLoop implements Runnable {
      7         public void run() {
      8             DataBuffer currentBuffer = initialEmptyBuffer;
      9             try {
     10                 while (currentBuffer != null) {
     11                     addToBuffer(currentBuffer);
     12                     if (currentBuffer.isFull()) {
     13                         System.out.println("[FillingLoop](Before)" + currentBuffer);
     14                         currentBuffer = exchanger.exchange(currentBuffer);
     15                         System.out.println("[FillingLoop](After)" + currentBuffer);
     16                     }
     17                 }
     18             } catch (InterruptedException ex) {
     19                 Thread.currentThread().interrupt();
     20             }
     21         }
     22     }
     23 
     24     class EmptyingLoop implements Runnable {
     25         public void run() {
     26             DataBuffer currentBuffer = initialFullBuffer;
     27             try {
     28                 while (currentBuffer != null) {
     29                     takeFromBuffer(currentBuffer);
     30                     if (currentBuffer.isEmpty()) {
     31                         System.out.println("[EmptyingLoop](Before)" + currentBuffer);
     32                         currentBuffer = exchanger.exchange(currentBuffer);
     33                         System.out.println("[EmptyingLoop](After)" + currentBuffer);
     34                     }
     35                 }
     36             } catch (InterruptedException ex) {
     37                 Thread.currentThread().interrupt();
     38             }
     39         }
     40     }
     41 
     42     void start() {
     43         Thread fillingLoopThread = new Thread(new FillingLoop());
     44         Thread emptyingLoopThread = new Thread(new EmptyingLoop());
     45 
     46         fillingLoopThread.start();
     47         emptyingLoopThread.start();
     48 
     49         try {
     50             Thread.sleep(10);
     51         } catch (InterruptedException e) {
     52             // do nothing
     53         }
     54         fillingLoopThread.interrupt();
     55         emptyingLoopThread.interrupt();
     56     }
     57 
     58     public void takeFromBuffer(DataBuffer buf) {
     59         buf.take();
     60     }
     61 
     62     public void addToBuffer(DataBuffer buf) {
     63         buf.add(1);
     64     }
     65 
     66     private static class DataBuffer {
     67         private final int[] buf;
     68         private final int size;
     69         private int index;
     70 
     71         private DataBuffer(int size) {
     72             this.size = size;
     73             this.buf = new int[size];
     74         }
     75 
     76         public static DataBuffer allocate(int size) {
     77             return new DataBuffer(size);
     78         }
     79 
     80         public boolean isEmpty() {
     81             return index == 0;
     82         }
     83 
     84         public boolean isFull() {
     85             return index == size - 1;
     86         }
     87 
     88         public int take() {
     89             if (index > 0) {
     90                 return buf[index--];
     91             }
     92 
     93             return -1;
     94         }
     95 
     96         public void add(int data) {
     97             if (index < size - 1) {
     98                 buf[index++] = data;
     99             }
    100         }
    101     }
    102 
    103     public static void main(String[] args) {
    104         FillAndEmpty fae = new FillAndEmpty();
    105         fae.start();
    106     }
    107 }

    输出如下,交换前后,两个线程所持的数据缓冲区对调。(部分输出未给出)

     1 [EmptyingLoop](Before)com.luoluo.exchanger.FillAndEmpty$DataBuffer@1733c6a5
     2 [FillingLoop](Before)com.luoluo.exchanger.FillAndEmpty$DataBuffer@39bcfec1
     3 [FillingLoop](After)com.luoluo.exchanger.FillAndEmpty$DataBuffer@1733c6a5
     4 [EmptyingLoop](After)com.luoluo.exchanger.FillAndEmpty$DataBuffer@39bcfec1
     5 ......

    源码解析

    常量介绍

    1     private static final int ASHIFT = 7; // 两个有效槽(slot -> Node)之间的字节地址长度(内存地址,以字节为单位),1 << 7至少为缓存行的大小,防止伪共享 
    2     private static final int MMASK = 0xff; // 场地(一排槽,arena -> Node[])的可支持的最大索引,可分配的大小为 MMASK + 1
    3     private static final int SEQ = MMASK + 1; // bound的递增单元,确立其唯一性
    4     private static final int NCPU = Runtime.getRuntime().availableProcessors(); // CPU的个数,用于场地大小和自旋控制
    5     static final int FULL = (NCPU >= (MMASK << 1)) ? MMASK : NCPU >>> 1; // 最大的arena索引
    6     private static final int SPINS = 1 << 10; // 自旋次数,NCPU = 1时,禁用
    7     private static final Object NULL_ITEM = new Object();// 空对象,对应null
    8     private static final Object TIMED_OUT = new Object();// 超时对象,对应timeout

    ASHIFT,两个有效的槽之间的地址长度是1 << 7(至少为缓存行的大小,避免伪共享问题,见下面说明)

    MMASK,多槽交换可支持的最大索引,大小为MMASK + 1(index从0开始)

    SEQ,bound的递增单元,确定其唯一性(高位)

    NCPU,CPU的个数

    FULL,最大的arena索引,不大于MMASK;arena,一排slot,为的是获得良好的伸缩性,避免所有的线程争用同一个槽位。

    SPINS,自旋次数,用于自旋等待,是最轻量的等待,依次是 spin -> yield -> block

    伪共享,高速缓存与内存之间是以缓存行为单位交换数据的,根据局部性原理,相邻地址空间的数据会被加载到高速缓存的同一个数据块上(缓存行),而数组是连续的(逻辑,涉及到虚拟内存)内存地址空间,因此,多个slot会被加载到同一个缓存行上,当一个slot改变时,会导致这个slot所在的缓存行上所有的数据(包括其他的slot)无效,需要从内存重新加载,影响性能。

    所以,为了避免这种情况,需要填充数据,使得有效的slot不被加载到同一个缓存行上。填充的大小即为1 << 7,如下图所示

    数据结构Node

    1     static final class Node {
    2         int index; // arena的索引
    3         int bound; // 记录上次的bound
    4         int collides; // 当前bound下CAS失败的次数
    5         int hash; // 伪随机,用于自旋
    6         Object item; // 当前线程携带的数据
    7         volatile Object match; // 存放释放线程携带的数据
    8         volatile Thread parked; // 挂在此结点上阻塞着的线程
    9     }

    index,arena的索引

    bound,记录上次的bound

    collides,当前bound下CAS失败的次数,最大为m,m(bound & MMASK)为当前bound下最大有效索引,从右往左遍历,等到collides == m时,有效索引的槽位也已经遍历完了,这时需要增长槽位,增长的方式是重置bound(依赖SEQ更新其版本,高位;+1,低位),同时collides重置

    hash,伪随机,用于自旋

    item,当前线程携带的数据

    match,存放释放线程(来交换的线程)携带的数据

    parked,挂在此结点上阻塞着的线程,等待被释放

    见下图

    数据结构Participant

    1     // 每个线程携带一个Node
    2     static final class Participant extends ThreadLocal<Node> {
    3         public Node initialValue() {
    4             return new Node();
    5         }
    6     }

    Participant直接继承自ThreadLocal保存当前线程携带的Node,交换操作主要依赖Node的行为

    属性介绍

    1     private final Participant participant;// 每个线程携带一个Node
    2     private volatile Node[] arena; // 场地,Node数组
    3     private volatile Node slot;// 槽,单个Node
    4     private volatile int bound;// 当前最大有效arena索引,高8位+SEQ确立其唯一性,低8位记录有效索引

    bound,记录最大有效的arena索引,动态变化,竞争激烈时(槽位全满)增加, 槽位空旷时减小。bound + SEQ +/- 1,其高位+ 1(SEQ,oxff + 1)确定其版本唯一性(比如,+1后,又-1,实际上是两个版本的bound,collides要重置的,而且从右向左遍历的索引也要更新,一般来讲,左边槽位比右边槽位竞争激烈,所以要从右向左找,为的是快速找到一个空位置,并尝试占领它,当bound加一又减一后,遍历索引右侧的槽位应该就空出来了,因为大家都往左边靠拢,所以要更新到最右侧,如果没有bound的版本唯一性,便没有索引更新,就一直往左遍历竞争激烈的槽位,还会误判,本来bound应该缩减的,反而又使其增加,于是会很影响效率的。),低位+/-1实际有效的索引(&MMASK)

     
    如下图

     

     exchange方法

    1     public V exchange(V x) throws InterruptedException {
    2         Object v;
    3         Object item = (x == null) ? NULL_ITEM : x; // 转换成空对象
    4         // arena == null, 路由到slotExchange(单槽交换), 如果arena != null或者单槽交换失败,且线程没有被中断,则路由到arenaExchange(多槽交换),返回null,则抛出中断异常
    5         if ((arena != null || (v = slotExchange(item, false, 0L)) == null)
    6                 && ((Thread.interrupted() || (v = arenaExchange(item, false, 0L)) == null)))
    7             throw new InterruptedException();
    8         return (v == NULL_ITEM) ? null : (V) v;
    9     }

    首先判断arena是否为null,如果为null,则调用slotExchange方法,如果arena不为null,或者slotExchange方法返回null,然后判断当前线程是否被中断(中断标记),有则抛出中断异常,没有则继续调用arenaExchange方法,如果该方法返回null,抛出中断异常,最后返回结果。

     带超时的exchange方法

     1     public V exchange(V x, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {
     2         Object v;
     3         Object item = (x == null) ? NULL_ITEM : x;// 转换成空对象
     4         long ns = unit.toNanos(timeout);
     5         // arena == null, 路由到slotExchange(单槽交换), 如果arena != null或者单槽交换失败,且线程没有被中断,则路由到arenaExchange(多槽交换),返回null,则抛出中断异常
     6         if ((arena != null || (v = slotExchange(item, true, ns)) == null)
     7                 && ((Thread.interrupted() || (v = arenaExchange(item, true, ns)) == null)))
     8             throw new InterruptedException();
     9         if (v == TIMED_OUT)// 超时
    10             throw new TimeoutException();
    11         return (v == NULL_ITEM) ? null : (V) v;
    12     }

    同上,加了超时的判断。 

    slotExchange方法

     1     private final Object slotExchange(Object item, boolean timed, long ns) {
     2         Node p = participant.get(); // 获取当前线程携带的Node
     3         Thread t = Thread.currentThread(); // 当前线程
     4         if (t.isInterrupted()) // 保留中断状态,以便调用者可以重新检查,Thread.interrupted() 会清除中断状态标记
     5             return null;
     6         for (Node q;;) {
     7             if ((q = slot) != null) { // slot不为null, 说明已经有线程在这里等待了
     8                 if (U.compareAndSwapObject(this, SLOT, q, null)) { // 将slot重新设置为null, CAS操作
     9                     Object v = q.item; // 取出等待线程携带的数据
    10                     q.match = item; // 将当前线程的携带的数据交给等待线程
    11                     Thread w = q.parked; // 可能存在的等待线程(可能中断,不等了)
    12                     if (w != null)
    13                         U.unpark(w); // 唤醒等待线程
    14                     return v; // 返回结果,交易成功
    15                 }
    16                 // CPU的个数多于1个,并且bound为0时创建 arena,并将bound设置为SEQ大小
    17                 if (NCPU > 1 && bound == 0 && U.compareAndSwapInt(this, BOUND, 0, SEQ))
    18                     arena = new Node[(FULL + 2) << ASHIFT]; // 根据CPU的个数估计Node的数量
    19             } else if (arena != null)
    20                 return null; // 如果slot为null, 但arena不为null, 则转而路由到arenaExchange方法
    21             else { // 最后一种情况,说明当前线程先到,则占用此slot
    22                 p.item = item; // 将携带的数据卸下,等待别的线程来交易
    23                 if (U.compareAndSwapObject(this, SLOT, null, p)) // 将slot的设为当前线程携带的Node
    24                     break; // 成功则跳出循环
    25                 p.item = null; // 失败,将数据清除,继续循环
    26             }
    27         }
    28         // 当前线程等待被释放, spin -> yield -> block/cancel
    29         int h = p.hash; // 伪随机,用于自旋
    30         long end = timed ? System.nanoTime() + ns : 0L; // 如果timed为true,等待超时的时间点; 0表示没有设置超时
    31         int spins = (NCPU > 1) ? SPINS : 1; // 自旋次数
    32         Object v;
    33         while ((v = p.match) == null) { // 一直循环,直到有线程来交易
    34             if (spins > 0) { // 自旋,直至spins不大于0
    35                 h ^= h << 1; // 伪随机算法, 目的是等h小于0(随机的)
    36                 h ^= h >>> 3;
    37                 h ^= h << 10;
    38                 if (h == 0) // 初始值
    39                     h = SPINS | (int) t.getId();
    40                 else if (h < 0 && (--spins & ((SPINS >>> 1) - 1)) == 0)
    41                     Thread.yield(); // 等到h < 0, 而spins的低9位也为0(防止spins过大,CPU空转过久),让出CPU时间片,每一次等待有两次让出CPU的时机(SPINS >>> 1)
    42             } else if (slot != p) // 别的线程已经到来,正在准备数据,自旋等待一会儿,马上就好
    43                 spins = SPINS;
    44             // 如果线程没被中断,且arena还没被创建,并且没有超时
    45             else if (!t.isInterrupted() && arena == null && (!timed || (ns = end - System.nanoTime()) > 0L)) {
    46                 U.putObject(t, BLOCKER, this); // 设置当前线程将阻塞在当前对象上
    47                 p.parked = t; // 挂在此结点上的阻塞着的线程
    48                 if (slot == p)
    49                     U.park(false, ns); // 阻塞, 等着被唤醒或中断
    50                 p.parked = null; // 醒来后,解除与结点的联系
    51                 U.putObject(t, BLOCKER, null); // 解除阻塞对象
    52             } else if (U.compareAndSwapObject(this, SLOT, p, null)) { // 超时或其他(取消),给其他线程腾出slot
    53                 v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null;
    54                 break;
    55             }
    56         }
    57         // 归位
    58         U.putOrderedObject(p, MATCH, null);
    59         p.item = null;
    60         p.hash = h;
    61         return v;
    62     }

     总结

    1. 检查slot是否为空(null),不为空,说明已经有线程在此等待,尝试占领该槽位,如果占领成功,与等待线程交换数据,并唤醒等待线程,交易结束,返回。

    2. 如果占领槽位失败,创建arena,但要继续【步骤1】尝试抢占slot,直至slot为空,或者抢占成功,交易结束返回。

    3. 如果slot为空,则判断arena是否为空,如果arena不为空,返回null,重新路由到arenaExchange方法

    4. 如果arena为空,说明当前线程是先到达的,尝试占有slot,如果成功,将slot标记为自己占用,跳出循环,继续【步骤5】,如果失败,则继续【步骤1】

    5 当前线程等待被释放,等待的顺序是先自旋(spin),不成功则让出CPU时间片(yield),最后还不行就阻塞(block),spin -> yield -> block

    6. 如果超时(设置超时的话)或被中断,则退出循环。

    7. 最后,重置数据,下次重用,返回结果,结束。

     见下图

    arenaExchange方法

     1     private final Object arenaExchange(Object item, boolean timed, long ns) {
     2         Node[] a = arena; // 交换场地,一排slot
     3         Node p = participant.get(); // 获取当前线程携带的Node
     4         for (int i = p.index;;) { // arena的索引,数组下标
     5             int b, m, c;
     6             long j; // 原数组偏移量,包括填充值
     7             // 从场地中选出偏移地址为(i << ASHIFT) + ABASE的内存值,也即真正可用的Node
     8             Node q = (Node) U.getObjectVolatile(a, j = (i << ASHIFT) + ABASE);
     9             if (q != null && U.compareAndSwapObject(a, j, q, null)) { // 此槽位不为null, 说明已经有线程在这里等了,重新将其设置为null, CAS操作
    10                 Object v = q.item; // 取出等待线程携带的数据
    11                 q.match = item; // 将当前线程携带的数据交给等待线程
    12                 Thread w = q.parked; // 可能存在的等待线程
    13                 if (w != null)
    14                     U.unpark(w); // 唤醒等待线程
    15                 return v; // 返回结果, 交易成功
    16             } else if (i <= (m = (b = bound) & MMASK) && q == null) { // 有效交换位置,且槽位为空
    17                 p.item = item; // 将携带的数据卸下,等待别的线程来交易
    18                 if (U.compareAndSwapObject(a, j, null, p)) { // 槽位占领成功
    19                     long end = (timed && m == 0) ? System.nanoTime() + ns : 0L; // 计算出超时结束时间点
    20                     Thread t = Thread.currentThread(); // 当前线程
    21                     for (int h = p.hash, spins = SPINS;;) { // 一直循环,直到有别的线程来交易,或超时,或中断
    22                         Object v = p.match; // 检查是否有别的线程来交换数据
    23                         if (v != null) { // 有则返回
    24                             U.putOrderedObject(p, MATCH, null); // match重置,等着下次使用
    25                             p.item = null; // 清空,下次接着使用
    26                             p.hash = h;
    27                             return v; // 返回结果,交易结束
    28                         } else if (spins > 0) { // 自旋
    29                             h ^= h << 1;
    30                             h ^= h >>> 3;
    31                             h ^= h << 10; // 移位加异或,伪随机
    32                             if (h == 0) // 初始值
    33                                 h = SPINS | (int) t.getId();
    34                             else if (h < 0 && // SPINS >>> 1, 一半的概率
    35                                     (--spins & ((SPINS >>> 1) - 1)) == 0)
    36                                 Thread.yield(); // 每一次等待有两次让出CPU的时机
    37                         } else if (U.getObjectVolatile(a, j) != p)
    38                             spins = SPINS; // 别的线程已经到来,正在准备数据,自旋等待一会儿,马上就好
    39                         else if (!t.isInterrupted() && m == 0 && (!timed || (ns = end - System.nanoTime()) > 0L)) {
    40                             U.putObject(t, BLOCKER, this); // 设置当前线程将阻塞在当前对象上
    41                             p.parked = t; // 挂在此结点上的阻塞着的线程
    42                             if (U.getObjectVolatile(a, j) == p)
    43                                 U.park(false, ns); // 阻塞, 等着被唤醒或中断
    44                             p.parked = null; // 醒来后,解除与结点的联系
    45                             U.putObject(t, BLOCKER, null); // 解除阻塞对象
    46                         } else if (U.getObjectVolatile(a, j) == p && U.compareAndSwapObject(a, j, p, null)) {
    47                             if (m != 0) // 尝试缩减
    48                                 U.compareAndSwapInt(this, BOUND, b, b + SEQ - 1); // 更新bound, 高位递增,低位 -1
    49                             p.item = null; // 重置
    50                             p.hash = h;
    51                             i = p.index >>>= 1; // 索引减半,为的是快速找到汇合点(最左侧)
    52                             if (Thread.interrupted())// 保留中断状态,以便调用者可以重新检查,Thread.interrupted() 会清除中断状态标记
    53                                 return null;
    54                             if (timed && m == 0 && ns <= 0L) // 超时
    55                                 return TIMED_OUT;
    56                             break; // 重新开始
    57                         }
    58                     }
    59                 } else
    60                     p.item = null; // 重置
    61             } else {
    62                 if (p.bound != b) { // 别的线程更改了bound,重置collides为0, i的情况如下:当i != m, 或者m = 0时,i = m; 否则,i = m-1; 从右往左遍历
    63                     p.bound = b;
    64                     p.collides = 0;
    65                     i = (i != m || m == 0) ? m : m - 1; // index 左移
    66                 } else if ((c = p.collides) < m || m == FULL || !U.compareAndSwapInt(this, BOUND, b, b + SEQ + 1)) { // 更新bound, 高位递增,低位 +1
    67                     p.collides = c + 1;
    68                     i = (i == 0) ? m : i - 1; // 左移,遍历槽位,m == FULL时,i == 0(最左侧),重置i = m, 重新从右往左循环遍历
    69                 } else
    70                     i = m + 1; // 槽位增长
    71                 p.index = i;
    72             }
    73         }
    74     }

    总结

    1. 从场地中选出偏移地址为(i << ASHIFT) + ABASE的内存值,也即第i个真正可用的Node,判断其槽位是否为空,为空,进入【步骤2】;不为空,说明有线程在此等待,尝试抢占该槽位,抢占成功,交换数据,并唤醒等待线程,返回,结束;没有抢占成功,进入【步骤9】

    2. 检查索引(i vs m)是否越界,越界,进入【步骤9】;没有越界,进入下一步。

    3. 尝试占有该槽位,抢占失败,进入【步骤1】;抢占成功,进入下一步。

    4. 检查match,是否有线程来交换数据,如果有,交换数据,结束;如果没有,进入下一步。

    5. 检查spin是否大于0,如果不大于0,进入下一步;如果大于0,检查hash是否小于0,并且spin减半或为0,如果不是,进入【步骤4】;如果是,让出CPU时间,过一会儿,进入【步骤4】

    6. 检查是否中断,m达到最小值,是否超时,如果没有中断,没有超时,并且m达到最小值,阻塞,过一会儿进入【步骤4】;否则,下一步。

    7. 没有线程来交换数据,尝试丢弃原有的槽位重新开始,丢弃失败,进入【步骤4】;否则,下一步。

    8. bound减1(m>0),索引减半;检查是否中断或超时,如果没有,进入【步骤1】;否则,返回,结束。

    9. 检查bound是否发生变化,如果变化了,重置collides,索引重置为m或左移,转向【步骤1】;否则,进入下一步。

    10. 检查collides是否达到最大值,如果没有,进入【步骤13】,否则下一步。

    11. m是否达到FULL,是,进入【步骤13】;否则,下一步。

    12. CAS bound加1是否成功,如果成功,i置为m+1,槽位增长,进入【步骤1】;否则,下一步。

    13. collides加1,索引左移,进入【步骤1】

     

    见下图(看不清图片?鼠标放在图片上面,【右键】 -> 【在新标签页中打开图片(I)】 -> 【点击(+)矢量放大】)

    Unsafe

     1     private static final sun.misc.Unsafe U;
     2     private static final long BOUND;
     3     private static final long SLOT;
     4     private static final long MATCH;
     5     private static final long BLOCKER;
     6     private static final int ABASE;
     7     static {
     8         int s;
     9         try {
    10             U = sun.misc.Unsafe.getUnsafe();
    11             Class<?> ek = Exchanger.class;
    12             Class<?> nk = Node.class;
    13             Class<?> ak = Node[].class;
    14             Class<?> tk = Thread.class;
    15             BOUND = U.objectFieldOffset(ek.getDeclaredField("bound"));
    16             SLOT = U.objectFieldOffset(ek.getDeclaredField("slot"));
    17             MATCH = U.objectFieldOffset(nk.getDeclaredField("match"));
    18             BLOCKER = U.objectFieldOffset(tk.getDeclaredField("parkBlocker"));
    19             s = U.arrayIndexScale(ak); // 数组增量地址
    20             ABASE = U.arrayBaseOffset(ak) + (1 << ASHIFT); // 数组首元素偏移地址
    21         } catch (Exception e) {
    22             throw new Error(e);
    23         }
    24         if ((s & (s - 1)) != 0 || s > (1 << ASHIFT))
    25             throw new Error("Unsupported array scale");
    26

    s为数组中每个元素占用的地址空间大小,ABASE为数组首元素偏移地址,防止伪共享

     

    最后,arena = new Node[(FULL + 2) << ASHIFT],FULL,<= MMASK,scale,<= 1 << ASHIFT,说明(FULL + 2)<< ASHIFT 个Node,真正可用的是FULL + 2个,实际上是FULL + 1 个,最后一个没有用,也是为了防止伪共享,如果最后一个也使用,那么,其右边并没有填充,别的数据修改可能会影响到它,也即是发生伪共享问题。最大的有效索引是MMASK(bound & MMASK),但m(实际的最大索引)增长到FULL时,不再增长,会循环遍历槽位,尝试交换数据。

    伪随机

    h ^= h << 1; h ^= h >>> 3; h ^= h << 10;

    实际上是xorshift算法,T = (I + La)(I + Rb)(I + Lc),其中,L代表左移,R代表右移,a, b, c分别代表上式的1,3,10,I代表矩阵{0,1}共32位(int),也即是二进制int,T代表的就是随机算法。翻译过来就是上面的式子:h ^= h << 1; h ^= h >>> 3; h ^= h << 10.

    为什么要选用1,3,10呢?

    其实,伪随机数,并不是真正的随机,而是通过算法模拟出来的,为了达到随机的效果,希望是周期越大越好。所谓周期指的是,当给定一个输入,得到的输出再作为下一次的输入,如此反复,直到某次输出恰巧等于最初的输入,可以作为随机算法关于随机数的周期。有了这个概念,我们就可以写代码测试下。

    直观地推测,int类型最大周期应该是遍历该类型所有的值(0除外,【奇异矩阵】,如果是0的话,输出便一直是0,谈不上随机了),即是max - min = 232 - 1

    Java代码

     1 public class PseudoRandom {
     2     private static final Map<Long, StringBuilder> map = new ConcurrentHashMap<>();
     3 
     4     public static void random(int a, int b, int c) {
     5         long cnt = 0;
     6         int h = 1;
     7         do {
     8             h ^= h << a;
     9             h ^= h >>> b;
    10             h ^= h << c;
    11             cnt++;
    12         } while (h != 1);
    13 
    14         StringBuilder builder = map.get(cnt);
    15         if (builder == null) {
    16             builder = new StringBuilder();
    17             map.put(cnt, builder);
    18         }
    19 
    20         builder.append(" (" + a + ", " + b + ", " + c + ")");
    21     }
    22 
    23     public static void main(String[] args) {
    24         CountDownLatch latch = new CountDownLatch(11 * 11 * 11);
    25         ExecutorService s = Executors.newFixedThreadPool(10);
    26         for (int i = 1; i < 11; i++) { // i, j ,k实际上应该是31,这里仅为了说明问题,当改成31时,CountDownLatch应该初始化为31 * 31 * 31
    27             for (int j = 1; j < 11; j++) {
    28                 for (int k = 1; k < 11; k++) {
    29                     final int ii = i;
    30                     final int jj = j;
    31                     final int kk = k;
    32                     s.execute(new Runnable() {
    33                         @Override
    34                         public void run() {
    35                             random(ii, jj, kk);
    36                             latch.countDown();
    37                         }
    38                     });
    39                 }
    40             }
    41         }
    42 
    43         s.shutdown();
    44         try {
    45             latch.await(300, TimeUnit.SECONDS);
    46         } catch (InterruptedException e) {
    47             Thread.currentThread().interrupt();
    48         }
    49 
    50         TreeMap<Long, StringBuilder> t = new TreeMap<Long, StringBuilder>(Collections.reverseOrder());
    51         t.putAll(map);
    52 
    53         for (Map.Entry<Long, StringBuilder> entry : t.entrySet()) {
    54             System.out.println("[" + entry.getKey() + "]" + entry.getValue().toString());
    55         }
    56     }
    57 }

    输出,按周期次数倒序排列,即最大的在前

      1 [4294967295] (1, 3, 10) (2, 7, 7) (2, 7, 9) (5, 9, 7) (7, 1, 9) (7, 7, 2) (7, 9, 5)
      2 [4160749537] (1, 7, 9) (4, 1, 9) (6, 5, 9)
      3 [3900702255] (1, 3, 4) (5, 5, 7) (7, 5, 5)
      4 [3758096377] (1, 9, 2) (2, 9, 1) (7, 7, 9)
      5 [2147483647] (1, 5, 5) (1, 9, 6) (2, 5, 5) (2, 5, 7) (5, 5, 1) (5, 5, 2) (6, 5, 7) (6, 9, 1) (7, 5, 2) (7, 5, 6)
      6 [2147483644] (1, 9, 10)
      7 [2147213313] (2, 5, 3) (3, 5, 2)
      8 [2147188740] (4, 5, 5) (4, 9, 1) (5, 5, 4)
      9 [2145385473] (7, 9, 9)
     10 [2145382404] (1, 5, 9)
     11 [2143288833] (5, 1, 6) (6, 1, 5)
     12 [2139094020] (1, 7, 6)
     13 [2113929153] (1, 5, 4) (4, 5, 1)
     14 [2080374753] (2, 3, 3) (3, 3, 2)
     15 [1997533470] (2, 9, 9)
     16 [1879048185] (2, 5, 9) (4, 7, 9)
     17 [1747831785] (8, 9, 5)
     18 [1610612733] (7, 3, 10)
     19 [1560280902] (3, 5, 5) (5, 5, 3)
     20 [1431655765] (1, 7, 7) (2, 9, 5) (5, 1, 8) (5, 9, 2) (7, 7, 1) (8, 1, 5)
     21 [1431562923] (1, 1, 2) (2, 1, 1)
     22 [1430257323] (3, 9, 7) (7, 9, 3)
     23 [1409286123] (5, 3, 7) (7, 3, 5) (9, 1, 10)
     24 [1339553285] (1, 9, 5) (5, 9, 1)
     25 [1242911789] (3, 7, 10) (5, 3, 10)
     26 [1174405085] (1, 3, 5) (5, 3, 1) (9, 3, 4)
     27 [1073741823] (3, 1, 6) (6, 1, 3)
     28 [1073594370] (1, 9, 4)
     29 [1064182911] (4, 3, 7) (7, 3, 4)
     30 [1006632930] (3, 1, 10)
     31 [714429611] (3, 1, 4) (4, 1, 3)
     32 [713031595] (1, 7, 5) (5, 7, 1) (7, 7, 10)
     33 [704642988] (3, 9, 10)
     34 [626349395] (9, 5, 3)
     35 [621455450] (2, 3, 9)
     36 [613543351] (1, 5, 3) (3, 5, 1)
     37 [602795529] (1, 1, 9) (7, 3, 9) (9, 1, 1) (9, 3, 7)
     38 [536870911] (3, 5, 7) (6, 9, 7) (7, 5, 3) (7, 9, 6)
     39 [536772612] (1, 1, 3)
     40 [534773505] (6, 7, 1)
     41 [528482241] (8, 3, 9)
     42 [520093634] (1, 5, 10)
     43 [469762041] (1, 7, 4) (4, 1, 7) (7, 1, 4)
     44 [459276069] (4, 7, 5)
     45 [453248985] (1, 3, 7)
     46 [429286605] (5, 7, 6) (6, 7, 5)
     47 [426141261] (1, 3, 8) (8, 3, 1)
     48 [390070086] (1, 1, 6)
     49 [389118324] (3, 3, 10)
     50 [352321494] (6, 7, 9)
     51 [352106517] (3, 7, 5) (5, 7, 3)
     52 [341310837] (8, 7, 1)
     53 [335544315] (4, 9, 7) (7, 7, 8) (7, 9, 4) (8, 7, 7)
     54 [335360010] (3, 9, 5)
     55 [310727725] (9, 3, 2)
     56 [286331153] (5, 3, 8) (8, 3, 5)
     57 [268435455] (1, 9, 3) (3, 9, 1)
     58 [268435454] (3, 1, 8) (7, 9, 8) (8, 9, 7)
     59 [268435452] (3, 1, 7) (7, 1, 3)
     60 [268435448] (2, 3, 7)
     61 [267386370] (5, 7, 7) (7, 7, 5)
     62 [260046817] (4, 3, 1)
     63 [259507262] (9, 5, 5)
     64 [252645135] (3, 1, 5) (5, 1, 3)
     65 [249690255] (5, 9, 8)
     66 [234637326] (4, 1, 5)
     67 [201326586] (5, 3, 6) (5, 7, 9) (6, 3, 5)
     68 [201222147] (3, 7, 8) (8, 7, 3)
     69 [195225786] (8, 1, 7)
     70 [178924204] (3, 1, 1)
     71 [167772155] (4, 3, 9)
     72 [167680005] (5, 9, 3)
     73 [153391689] (1, 5, 2) (2, 5, 1)
     74 [153092023] (5, 7, 4)
     75 [142501905] (2, 3, 5) (5, 3, 2)
     76 [134217727] (8, 1, 3)
     77 [134217726] (7, 5, 8)
     78 [134150145] (3, 7, 9)
     79 [134085633] (3, 7, 6) (6, 7, 3)
     80 [133693185] (1, 9, 7) (7, 9, 1)
     81 [129753631] (3, 9, 4) (4, 9, 3) (5, 5, 9)
     82 [117318663] (5, 1, 4)
     83 [100663293] (8, 9, 9)
     84 [97612893] (7, 1, 8)
     85 [97517382] (1, 7, 8)
     86 [94371795] (1, 7, 3) (3, 7, 1)
     87 [93323175] (6, 1, 7) (7, 1, 6)
     88 [89478485] (3, 5, 9)
     89 [87951402] (5, 9, 10)
     90 [82993665] (4, 3, 5) (5, 3, 4)
     91 [78212442] (1, 7, 10) (7, 5, 9) (9, 5, 7)
     92 [75497463] (9, 3, 8)
     93 [69273666] (7, 5, 1)
     94 [67108863] (4, 7, 1) (5, 9, 9)
     95 [67108862] (7, 3, 2)
     96 [67084290] (9, 5, 10)
     97 [66584449] (9, 3, 10)
     98 [66059784] (4, 5, 9) (9, 5, 4)
     99 [65536191] (2, 1, 5) (5, 1, 2)
    100 [65011681] (6, 1, 1)
    101 [62914530] (1, 7, 2) (2, 7, 1)
    102 [58260615] (2, 9, 3) (3, 9, 2)
    103 [57252195] (3, 5, 4) (4, 5, 3)
    104 [56884380] (1, 1, 5) (5, 1, 1)
    105 [55050135] (3, 1, 9) (9, 1, 3)
    106 [47439707] (1, 5, 8) (8, 5, 1)
    107 [44739242] (8, 5, 7)
    108 [42105595] (1, 9, 8) (8, 9, 1)
    109 [41287365] (5, 9, 6)
    110 [34636833] (1, 3, 6) (1, 5, 7) (6, 3, 1)
    111 [33554430] (3, 3, 8) (8, 3, 3)
    112 [33554416] (6, 5, 3)
    113 [30593745] (6, 7, 7) (7, 7, 6)
    114 [23194290] (7, 3, 6)
    115 [22282155] (1, 3, 2) (2, 3, 1)
    116 [19473111] (1, 1, 4) (4, 1, 1)
    117 [19168695] (1, 1, 8) (8, 1, 1)
    118 [17284575] (5, 7, 8) (8, 7, 5)
    119 [16777215] (1, 3, 3) (3, 3, 1) (5, 3, 9) (9, 3, 5)
    120 [16777208] (3, 5, 6)
    121 [16129169] (5, 1, 7) (7, 1, 5)
    122 [14351946] (3, 7, 7)
    123 [11597145] (6, 3, 7)
    124 [11184810] (2, 7, 5) (5, 7, 2)
    125 [11180715] (3, 7, 4) (4, 7, 3)
    126 [9266985] (3, 3, 7) (7, 3, 3)
    127 [8382465] (1, 1, 10)
    128 [8257473] (6, 9, 5)
    129 [7798308] (5, 5, 6)
    130 [7427385] (4, 9, 9)
    131 [7339976] (8, 1, 9) (9, 1, 8)
    132 [5963685] (4, 9, 5) (5, 9, 4)
    133 [5832615] (7, 1, 10)
    134 [5592405] (2, 1, 3) (3, 1, 2)
    135 [5374005] (5, 1, 9) (9, 1, 5)
    136 [5332341] (7, 3, 1)
    137 [5158440] (2, 1, 9)
    138 [4783982] (7, 7, 3)
    139 [3997791] (1, 9, 9)
    140 [2936010] (5, 1, 10)
    141 [2790571] (2, 9, 7) (7, 9, 2)
    142 [2579220] (9, 1, 2)
    143 [2162622] (3, 3, 5)
    144 [2149602] (2, 1, 7) (7, 1, 2)
    145 [1179612] (5, 5, 8) (8, 5, 5)
    146 [1081311] (5, 3, 3)
    147 [1048575] (1, 3, 9) (1, 5, 6) (6, 5, 1) (9, 3, 1)
    148 [1043970] (8, 5, 3)
    149 [1016379] (7, 9, 10)
    150 [1003935] (6, 1, 9) (9, 1, 6)
    151 [573405] (2, 7, 3) (3, 7, 2)
    152 [557039] (1, 1, 7) (7, 1, 1)
    153 [522753] (3, 3, 4) (4, 3, 3)
    154 [521985] (3, 5, 8)
    155 [458724] (7, 3, 8) (8, 3, 7)
    156 [390915] (4, 5, 7) (7, 5, 4)
    157 [278511] (6, 5, 5)
    158 [131070] (1, 4, 7) (1, 8, 9) (1, 10, 10) (2, 4, 9) (2, 5, 6) (2, 7, 4) (2, 7, 8) (2, 7, 10) (2, 8, 7) (4, 6, 7) (4, 7, 2) (4, 9, 4) (5, 2, 9) (5, 4, 7) (5, 6, 8) (5, 8, 7) (5, 8, 10) (6, 5, 2) (6, 8, 7) (7, 4, 1) (7, 4, 5) (7, 6, 4) (7, 8, 2) (7, 8, 5) (7, 8, 6) (8, 6, 5) (8, 7, 2) (8, 7, 10) (9, 2, 5) (9, 4, 2)
    159 [129794] (2, 5, 4) (2, 9, 8) (3, 5, 3) (4, 5, 2) (4, 5, 6) (5, 8, 9) (6, 5, 4) (8, 9, 2)
    160 [128961] (7, 5, 10)
    161 [126914] (6, 3, 10) (7, 6, 9) (7, 10, 9) (8, 6, 9) (9, 6, 7) (9, 6, 8)
    162 [114674] (1, 2, 7) (1, 2, 9) (3, 4, 10) (5, 10, 7) (7, 2, 1) (7, 2, 8) (7, 10, 5) (8, 2, 7) (9, 2, 1)
    163 [110670] (3, 2, 5) (5, 2, 3)
    164 [98301] (4, 7, 7) (7, 7, 4)
    165 [95046] (4, 4, 7) (5, 2, 2) (5, 6, 10) (7, 4, 4)
    166 [85974] (2, 4, 7) (6, 6, 1) (7, 4, 2)
    167 [65535] (2, 10, 4) (4, 10, 2) (5, 7, 10)
    168 [65534] (1, 3, 1) (1, 6, 5) (3, 4, 9) (3, 10, 5) (4, 7, 4) (5, 6, 1) (5, 6, 7) (5, 10, 3) (6, 3, 8) (7, 6, 5) (8, 3, 6) (9, 4, 3)
    169 [65532] (4, 10, 3)
    170 [65528] (1, 2, 3) (4, 5, 10) (7, 4, 9) (9, 4, 7)
    171 [64770] (1, 4, 9) (9, 4, 1)
    172 [63240] (3, 4, 4) (4, 4, 3) (8, 2, 9) (9, 2, 8)
    173 [61410] (2, 2, 7) (7, 2, 2)
    174 [61320] (9, 2, 10)
    175 [57316] (2, 3, 2) (6, 5, 8) (8, 5, 6)
    176 [57288] (3, 8, 7) (7, 8, 3)
    177 [55335] (4, 2, 6) (6, 2, 4) (8, 7, 9)
    178 [55118] (3, 8, 8) (8, 8, 3)
    179 [49146] (1, 8, 7) (2, 3, 8) (3, 4, 5) (5, 4, 3) (7, 8, 1) (8, 3, 2)
    180 [47523] (2, 2, 8) (8, 2, 2)
    181 [47244] (4, 7, 6) (6, 7, 4) (6, 10, 9)
    182 [43690] (1, 2, 5) (1, 10, 3) (3, 10, 1) (3, 10, 7) (5, 2, 1) (5, 4, 6) (6, 4, 5) (7, 2, 9) (7, 8, 8) (7, 10, 3) (8, 8, 7) (9, 2, 7) (9, 4, 10)
    183 [42966] (1, 8, 3) (2, 8, 5) (3, 8, 1) (3, 8, 5) (5, 8, 2) (5, 8, 3) (6, 7, 10)
    184 [40955] (3, 9, 8) (8, 9, 3)
    185 [39370] (5, 2, 6) (6, 2, 5)
    186 [32767] (2, 2, 6) (6, 2, 2)
    187 [32766] (2, 3, 6) (2, 9, 10) (2, 10, 9) (3, 2, 9) (3, 8, 6) (3, 10, 4) (3, 10, 10) (4, 4, 5) (4, 7, 10) (4, 9, 10) (5, 4, 4) (6, 3, 2) (6, 8, 3) (9, 2, 3)
    188 [32764] (3, 2, 1)
    189 [32752] (2, 6, 7) (4, 8, 5) (5, 8, 4) (7, 6, 2)
    190 [31682] (2, 2, 5)
    191 [31248] (6, 6, 5)
    192 [30660] (3, 7, 3)
    193 [28658] (1, 6, 6) (5, 4, 8) (8, 4, 5) (8, 10, 6)
    194 [28644] (3, 2, 10)
    195 [26670] (2, 10, 3) (3, 10, 2) (5, 10, 8) (8, 10, 5)
    196 [26214] (2, 2, 9) (2, 9, 4) (4, 9, 2) (9, 2, 2)
    197 [26040] (2, 8, 3)
    198 [24573] (2, 6, 10)
    199 [24528] (2, 5, 10) (5, 3, 5)
    200 [23622] (7, 6, 1)
    201 [22134] (3, 8, 4) (4, 8, 3)
    202 [21844] (3, 2, 4) (4, 2, 3)
    203 [21590] (3, 10, 9)
    204 [21483] (4, 6, 6) (6, 6, 4)
    205 [21420] (5, 2, 8) (8, 2, 5)
    206 [21336] (1, 10, 9)
    207 [20470] (7, 10, 10) (8, 10, 9)
    208 [20460] (7, 8, 9)
    209 [16383] (4, 6, 10) (4, 10, 10)
    210 [16002] (5, 10, 6) (6, 10, 5)
    211 [15810] (3, 5, 10)
    212 [15748] (1, 6, 7)
    213 [15624] (5, 6, 6)
    214 [15330] (1, 4, 10) (3, 10, 6) (4, 9, 6) (6, 9, 4) (6, 10, 3)
    215 [14329] (6, 10, 8)
    216 [14322] (2, 10, 10) (3, 6, 10) (4, 3, 6) (4, 6, 9) (6, 3, 4) (9, 6, 4) (9, 6, 10)
    217 [14280] (8, 6, 10)
    218 [13020] (3, 8, 2)
    219 [10922] (2, 4, 5) (5, 4, 2)
    220 [10710] (6, 6, 7) (7, 6, 6)
    221 [10668] (2, 10, 8)
    222 [10416] (4, 3, 4)
    223 [10230] (1, 5, 1)
    224 [9362] (1, 4, 6) (2, 3, 4) (3, 6, 7) (3, 10, 8) (4, 3, 2) (6, 4, 1) (6, 9, 8) (7, 6, 3) (8, 9, 6) (8, 10, 3)
    225 [9198] (3, 2, 6) (4, 10, 7) (6, 2, 3) (7, 10, 4)
    226 [9052] (2, 8, 9)
    227 [8190] (1, 6, 10) (1, 10, 6) (2, 6, 9) (4, 3, 10) (5, 4, 10) (5, 8, 6) (6, 4, 7) (6, 8, 5) (6, 10, 1) (6, 10, 7) (7, 2, 10) (7, 4, 6) (7, 10, 6) (9, 6, 2)
    228 [8184] (4, 2, 5) (5, 4, 9) (7, 6, 7) (9, 4, 5)
    229 [7905] (8, 6, 2)
    230 [7710] (2, 10, 7) (7, 10, 2)
    231 [7140] (6, 2, 9) (9, 2, 6)
    232 [7112] (5, 8, 1)
    233 [6510] (6, 10, 10)
    234 [5460] (3, 8, 10)
    235 [5334] (1, 10, 5) (5, 10, 1) (6, 8, 9)
    236 [5208] (3, 2, 7)
    237 [4774] (1, 2, 10)
    238 [4526] (4, 10, 6)
    239 [4284] (8, 3, 10)
    240 [4095] (6, 2, 10) (7, 2, 7)
    241 [4094] (2, 2, 10)
    242 [4092] (3, 2, 8) (5, 2, 4) (7, 10, 7) (8, 2, 3)
    243 [4088] (8, 8, 9)
    244 [3906] (4, 10, 9)
    245 [3810] (3, 6, 8) (8, 6, 3)
    246 [3556] (2, 5, 2) (9, 2, 9)
    247 [3472] (2, 6, 3) (3, 6, 2)
    248 [3276] (1, 4, 3) (3, 4, 1)
    249 [3069] (3, 10, 3) (5, 6, 5)
    250 [3066] (7, 8, 10)
    251 [2920] (2, 5, 8) (8, 5, 2)
    252 [2730] (1, 8, 10) (5, 2, 7) (6, 10, 2) (7, 2, 5) (7, 6, 10)
    253 [2570] (4, 8, 9)
    254 [2520] (4, 8, 7) (7, 8, 4)
    255 [2286] (1, 6, 9) (9, 6, 1)
    256 [2263] (6, 10, 4)
    257 [2142] (1, 10, 7) (2, 4, 3) (3, 4, 2) (7, 10, 1)
    258 [2114] (3, 4, 7) (7, 4, 3)
    259 [2044] (6, 5, 10)
    260 [1953] (3, 6, 3) (3, 9, 3) (6, 6, 8) (6, 9, 6) (8, 6, 6)
    261 [1778] (1, 8, 5)
    262 [1533] (3, 9, 9) (6, 6, 10) (9, 6, 3)
    263 [1530] (3, 4, 6) (5, 2, 10) (6, 4, 3)
    264 [1524] (2, 2, 3) (3, 2, 2) (7, 10, 8)
    265 [1365] (1, 10, 1) (2, 10, 6) (5, 2, 5)
    266 [1302] (3, 6, 5) (5, 6, 3) (7, 2, 3) (8, 2, 6)
    267 [1190] (2, 6, 4) (4, 6, 2)
    268 [1116] (5, 10, 9)
    269 [1068] (3, 4, 8) (8, 4, 3)
    270 [1023] (3, 3, 6) (6, 3, 3)
    271 [1022] (8, 2, 10)
    272 [1020] (4, 4, 9) (4, 10, 5) (5, 8, 8) (5, 10, 4) (6, 2, 7) (7, 2, 6) (8, 8, 5) (9, 4, 4)
    273 [1008] (1, 2, 6) (6, 2, 1)
    274 [930] (3, 6, 4) (4, 2, 7) (4, 6, 3) (7, 2, 4)
    275 [889] (8, 10, 2)
    276 [868] (1, 6, 2) (1, 10, 4) (2, 6, 1) (4, 2, 9) (4, 10, 1) (9, 2, 4)
    277 [840] (8, 6, 7)
    278 [762] (1, 4, 5) (5, 4, 1) (8, 10, 7)
    279 [682] (7, 4, 10)
    280 [630] (1, 6, 3) (2, 3, 10) (3, 6, 1) (5, 6, 9) (6, 4, 9) (9, 4, 6) (9, 6, 5)
    281 [511] (3, 6, 9)
    282 [510] (2, 6, 5) (2, 6, 8) (2, 8, 10) (2, 9, 6) (3, 8, 9) (4, 6, 5) (5, 6, 2) (5, 6, 4) (6, 9, 2)
    283 [508] (1, 2, 2) (1, 10, 8) (2, 2, 1) (4, 3, 8) (4, 9, 8) (6, 7, 8) (6, 9, 9) (8, 3, 4) (8, 7, 6) (8, 9, 4) (8, 10, 1) (9, 3, 9)
    284 [496] (1, 2, 8) (8, 2, 1)
    285 [476] (2, 7, 6) (6, 7, 2)
    286 [434] (6, 2, 8)
    287 [420] (7, 6, 8)
    288 [315] (8, 10, 10)
    289 [280] (8, 5, 10)
    290 [255] (4, 4, 8) (8, 4, 4)
    291 [254] (3, 8, 3) (4, 4, 10) (4, 6, 4) (4, 10, 8) (6, 8, 10) (8, 10, 4)
    292 [252] (1, 7, 1) (1, 10, 2) (2, 7, 2) (2, 10, 1) (4, 8, 10) (6, 4, 10) (7, 3, 7) (8, 8, 10) (8, 9, 10)
    293 [248] (4, 6, 8) (6, 6, 9) (9, 6, 6)
    294 [240] (1, 2, 4) (4, 2, 1)
    295 [234] (4, 5, 8) (8, 5, 4)
    296 [210] (1, 8, 6) (2, 6, 6) (6, 6, 2) (6, 8, 1)
    297 [186] (2, 8, 6) (2, 10, 2) (5, 4, 5) (6, 8, 2)
    298 [170] (3, 3, 9)
    299 [146] (3, 6, 6) (6, 6, 3)
    300 [127] (6, 9, 3)
    301 [126] (6, 4, 8) (7, 4, 7) (7, 4, 8) (8, 4, 6) (8, 4, 7)
    302 [124] (8, 6, 4)
    303 [120] (1, 4, 8) (1, 6, 4) (1, 6, 8) (4, 6, 1) (5, 7, 5) (5, 9, 5) (6, 5, 6) (6, 7, 6) (8, 4, 1) (8, 6, 1) (9, 6, 9)
    304 [105] (5, 10, 10)
    305 [102] (4, 8, 6) (6, 8, 4) (6, 8, 8) (6, 9, 10) (8, 8, 6)
    306 [93] (1, 6, 1) (3, 2, 3) (6, 3, 6)
    307 [85] (4, 2, 10) (9, 3, 3)
    308 [84] (2, 4, 6) (2, 6, 2) (2, 10, 5) (3, 4, 3) (5, 10, 2) (6, 4, 2) (8, 4, 10)
    309 [63] (6, 4, 6)
    310 [60] (2, 4, 4) (2, 4, 8) (4, 4, 2) (4, 10, 4) (5, 8, 5) (8, 4, 2)
    311 [56] (1, 4, 4) (4, 4, 1)
    312 [51] (6, 3, 9) (9, 3, 6)
    313 [48] (4, 4, 6) (4, 7, 8) (6, 4, 4) (8, 7, 4)
    314 [42] (2, 4, 10) (5, 5, 10)
    315 [35] (5, 5, 5)
    316 [32] (1, 1, 1) (1, 4, 2) (1, 8, 2) (1, 8, 4) (1, 9, 1) (2, 1, 2) (2, 1, 4) (2, 1, 6) (2, 1, 8) (2, 1, 10) (2, 4, 1) (2, 8, 1) (2, 9, 2) (3, 1, 3) (4, 1, 2) (4, 1, 4) (4, 1, 6) (4, 1, 8) (4, 1, 10) (4, 5, 4) (4, 8, 1) (5, 1, 5) (6, 1, 2) (6, 1, 4) (6, 1, 6) (6, 1, 8) (6, 1, 10) (7, 1, 7) (7, 5, 7) (7, 9, 7) (8, 1, 2) (8, 1, 4) (8, 1, 6) (8, 1, 8) (8, 1, 10) (8, 3, 8) (8, 4, 9) (8, 5, 8) (9, 1, 9) (9, 4, 8) (9, 5, 9)
    317 [31] (1, 2, 1) (3, 3, 3) (7, 7, 7)
    318 [30] (6, 8, 6)
    319 [24] (2, 8, 8)
    320 [21] (2, 2, 4) (4, 2, 2)
    321 [16] (1, 4, 1) (1, 8, 1) (1, 8, 8) (2, 2, 2) (4, 2, 8) (6, 2, 6) (6, 10, 6) (8, 2, 4) (8, 2, 8) (8, 8, 1) (9, 4, 9)
    322 [15] (2, 4, 2)
    323 [14] (6, 6, 6)
    324 [12] (4, 8, 8) (8, 8, 2) (8, 8, 4)
    325 [8] (2, 8, 2) (2, 8, 4) (4, 2, 4) (4, 4, 4) (4, 8, 2) (7, 8, 7) (8, 4, 8) (8, 6, 8) (8, 9, 8) (8, 10, 8)
    326 [7] (4, 8, 4) (5, 10, 5)
    327 [5] (3, 9, 6)
    328 [4] (8, 7, 8)
    329 [2] (8, 8, 8)
    ......5min timeout

    可以看到,排在第一的恰巧是(1,3,10)周期为4294967295,正好是 232 - 1

    一排多组,表示周期相等。

    问题,为什么要有两次左移和一次右移呢?其实只一次左移加异或就能达到随机的效果。

    猜测,之所以这样,大概是因为,第一次左移,是为了让高位多1,右移,是为了让低位多1,这样,高位低位都参与进来,增加随机性,第二次左移,便是真正的随机了。

    行文至此结束。

    尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_exchanger.html

  • 相关阅读:
    Visual Studio 2019 XAML Hot Reload功能介绍
    C#开启和关闭UAC功能
    使用Powershell启用/关闭Windows功能
    解决C#调用COM组件异常来自 HRESULT:0x80010105 (RPC_E_SERVERFAULT)的错误
    ComPtr的介绍以及使用
    C#使用Selenium
    estimateAffinePartial2D 替代 estimateRigidTransform
    mtcnn
    pytorch 指定GPU
    cv2.imread()与PIL中Image.open(),以及相互转换
  • 原文地址:https://www.cnblogs.com/aniao/p/aniao_exchanger.html
Copyright © 2011-2022 走看看