【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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM