Java隨機演算法(一)(r11筆記第14天)

jeanron100發表於2016-12-16

如何生成一個隨機的字串?答:讓新手退出VIM 。

生成一個隨機數看起來很簡單,一直以來卻深知它的不易,怎麼讓一個確定的值得到一個不確定的值,這個想起來都有點困難,而且這部分內容,自己也花了些時間去看Java原始碼,結果發現遠比自己琢磨的要複雜的多,加上也有些日子沒寫過Java程式碼,可謂是困難重重,寫了一小部分的總結發現,竟然有很多不大理解的地方。帶著問題竟然找到一篇文章說得非常全面,索性就拿過來了。文章的連結如下,感興趣可以看看,我在這個基礎上做了刪減。

http://blog.sina.com.cn/blog_4f925fc30100uvur.html

看內容裡面的隨機演算法在不同Java版本還是有一些差距,比如Random的方法在JDK6中會使用System.nanoTime()的方式,而在早期的版本是使用currentTimeMillis,相比而言,nanoTime是以毫微秒為單位,而currentTimeMillis返回的是系統當前時間和1970-01-01之前間隔時間的毫秒數。

而且在隨機演算法的實現細節上,也有一些差別。以下是舊版本的解讀,而新版本的解讀我還要繼續花一些時間。


大體來說得到隨機數有下面三種方法:


方法1
(資料型別)(最小值+Math.random()*(最大值-最小值+1))
例:
(int)(1+Math.random()*(10-1+1))
從1到10的int型隨數

方法2
獲得隨機數
for (int i=0;i<30;i++)
{System.out.println((int)(1+Math.random()*10));}
(int)(1+Math.random()*10)
通過java.Math包的random方法得到1-10的int隨機數
公式是:最小值---最大值(整數)的隨機數
(型別)最小值+Math.random()*最大值

方法3
Random ra =new Random();
for (int i=0;i<30;i++)
{System.out.println(ra.nextInt(10)+1);}
通過java.util包中的Random類的nextInt方法來得到1-10的int隨機數

 

生成0到1之間的任意隨機小數:

生成[0,d)區間的隨機小數,d為任意正的小數,則只需要將nextDouble方法的返回值乘以d即可。

[n1,n2]

也就是 ra.nextDouble() *(n2-n1)+n1

 

java產生隨機數的幾種方式
一.在j2se裡我們可以使用Math.random()方法來產生一個隨機數,這個產生的隨機數是0-1之間的一個double,我們可以把他乘以一定的數,比如說乘以100,他就是個100以內的隨機,這個在j2me中沒有。

二.在java.util這個包裡面提供了一個Random的類,我們可以新建一個Random的物件來產生隨機數,他可以產生隨機整數、隨機float、隨機double,隨機long,這個也是我們在j2me的程式裡經常用的一個取隨機數的方法。

三.在我們的System類中有一個currentTimeMillis()方法,這個方法返回一個從1970年1月1號0點0分0秒到目前的一個毫秒數,返回型別是long,我們可以拿他作為一個隨機數,我們可以拿他對一些數取模,就可以把他限制在一個範圍之內啦

其實在Random的預設構造方法裡也是使用上面第三種方法進行隨機數的產生的


對於方法二中的Random類有以下說明:

java.util.Random類有兩種方式構建方式:帶種子和不帶種子

不帶種子:
此種方式將會返回隨機的數字,每次執行結果不一樣

public class RandomTest {
public static void main(String[] args) {
java.util.Random r=new java.util.Random();
for(int i=0;i<10;i++){
 
  System.out.println(r.nextInt());
}

}
帶種子:
此種方式,無論程式執行多少次,返回結果都是一樣的

public static void main(String[] args) {
java.util.Random r=new java.util.Random(10);
for(int i=0;i<10;i++){
 
  System.out.println(r.nextInt());
}
}

兩種方式的差別在於

(1) 首先請開啟Java Doc,我們會看到Random類的說明:

此類的例項用於生成偽隨機數流,此類使用 48 位的種子,該種子可以使用線性同餘公式對其進行修改(請參閱 Donald Knuth的《The Art of Computer Programming, Volume 2》,第 3.2.1 節)。

如果用相同的種子建立兩個 Random例項,則對每個例項進行相同的方法呼叫序列,它們將生成並返回相同的數字序列。為了保證實現這種特性,我們為類Random指定了特定的演算法。為了Java 程式碼的完全可移植性,Java 實現必須讓類 Random 使用此處所示的所有演算法。但是允許 Random類的子類使用其他演算法,只要其符合所有方法的常規協定即可。

Java Doc對Random類已經解釋得非常明白,我們的測試也驗證了這一點。

(2) 如果沒有提供種子數,Random例項的種子數將是當前時間的毫秒數,可以通過System.currentTimeMillis()來獲得當前時間的毫秒數。開啟JDK的原始碼,我們可以非常明確地看到這一點。
public Random() { this(System.currentTimeMillis()); }


另外:

random物件的nextInt(),nextInt(int n)方法的說明:

int nextInt()
 
  返回下一個偽隨機數,它是此隨機數生成器的序列中均勻分佈的 int 值。
int nextInt(int n)
 
  返回一個偽隨機數,它是從此隨機數生成器的序列中取出的、在 0(包括)和指定值(不包括)之間均勻分佈的int值。  

Java隨機數總結

  隨機數在實際中使用很廣泛,比如要隨即生成一個固定長度的字串、數字。或者隨即生成一個不定長度的數字、或者進行一個模擬的隨機選擇等等。Java提供了最基本的工具,可以幫助開發者來實現這一切。

  一、Java隨機數的產生方式

  在Java中,隨機數的概念從廣義上將,有三種。

  1、通過System.currentTimeMillis()來獲取一個當前時間毫秒數的long型數字。

  2、通過Math.random()返回一個0到1之間的double值。

  3、通過Random類來產生一個隨機數,這個是專業的Random工具類,功能強大。

  二、Random類API說明

  1、Java API說明

  Random類的例項用於生成偽隨機數流。此類使用 48 位的種子,使用線性同餘公式對其進行修改(請參閱 Donald Knuth 的《The Art of Computer Programming, Volume 2》,第 3.2.1 節)。

  如果用相同的種子建立兩個 Random 例項,則對每個例項進行相同的方法呼叫序列,它們將生成並返回相同的數字序列。為了保證屬性的實現,為類 Random 指定了特定的演算法。

  很多應用程式會發現 Math 類中的 random 方法更易於使用。

  2、方法摘要

  Random()

  建立一個新的隨機數生成器。

  Random(long seed)

  使用單個 long 種子建立一個新隨機數生成器: public Random(long seed) { setSeed(seed); } next 方法使用它來儲存隨機數生成器的狀態。

  protected int next(int bits)

  生成下一個偽隨機數。

  boolean nextBoolean()

  返回下一個偽隨機數,它是從此隨機數生成器的序列中取出的、均勻分佈的 boolean 值。

  void nextBytes(byte[] bytes)

  生成隨機位元組並將其置於使用者提供的位元組陣列中。

  double nextDouble()

  返回下一個偽隨機數,它是從此隨機數生成器的序列中取出的、在 0.0 和 1.0之間均勻分佈的 double 值。

  float nextFloat()

  返回下一個偽隨機數,它是從此隨機數生成器的序列中取出的、在 0.0 和 1.0 之間均勻分佈的 float 值。

  double nextGaussian()

  返回下一個偽隨機數,它是從此隨機數生成器的序列中取出的、呈高斯(“正常地”)分佈的 double 值,其平均值是 0.0,標準偏差是 1.0。

  int nextInt()

  返回下一個偽隨機數,它是此隨機數生成器的序列中均勻分佈的 int 值。

  int nextInt(int n)

  返回一個偽隨機數,它是從此隨機數生成器的序列中取出的、在 0(包括)和指定值(不包括)之間均勻分佈的 int值。

  long nextLong()

  返回下一個偽隨機數,它是從此隨機數生成器的序列中取出的、均勻分佈的 long 值。

  void setSeed(long seed)

  使用單個 long 種子設定此隨機數生成器的種子。

  三、Random類使用說明

  1、帶種子與不帶種子的區別Random類使用的根本是策略分帶種子和不帶種子的Random的例項。

  通俗說,兩者的區別是:帶種子的,每次執行生成的結果都是一樣的。

  不帶種子的,每次執行生成的都是隨機的,沒有規律可言。

  2、建立不帶種子的Random物件

  Random random = new Random();

  3、建立不帶種子的Random物件有兩種方法:

  1) Random random = new Random(555L);

  2) Random random = new Random();random.setSeed(555L);

  四、測試

  通過一個例子說明上面的用法

  import java.util.Random;

  

  public class TestRandomNum {

  public static void main(String[] args) {

  randomTest();

  testNoSeed();

  testSeed1();

  testSeed2();

  }

  public static void randomTest() {

  System.out.println("--------------test()--------------");

  //返回以毫秒為單位的當前時間。

  long r1 = System.currentTimeMillis();

  //返回帶正號的 double 值,大於或等於 0.0,小於 1.0。

  double r2 = Math.random();

  //通過Random類來獲取下一個隨機的整數

  int r3 = new Random().nextInt();

  System.out.println("r1 = " + r1);

  System.out.println("r3 = " + r2);

  System.out.println("r2 = " + r3);

  }

  public static void testNoSeed() {

  System.out.println("--------------testNoSeed()--------------");

  //建立不帶種子的測試Random物件

  Random random = new Random();

  for (int i = 0; i < 3; i++) {

  System.out.println(random.nextInt());

  }

  }

  public static void testSeed1() {

  System.out.println("--------------testSeed1()--------------");

  //建立帶種子的測試Random物件

  Random random = new Random(555L);

  for (int i = 0; i < 3; i++) {

  System.out.println(random.nextInt());

  }

  }

  public static void testSeed2() {

  System.out.println("--------------testSeed2()--------------");

  //建立帶種子的測試Random物件

  Random random = new Random();

  random.setSeed(555L);

  for (int i = 0; i < 3; i++) {

  System.out.println(random.nextInt());

  }

  }

  }

  執行結果:

  --------------test()--------------

  r1 = 1227108626582

  r3 = 0.5324887850155043

  r2 = -368083737

  --------------testNoSeed()--------------

  809503475

  1585541532

  -645134204

  --------------testSeed1()--------------

  -1367481220

  292886146

  -1462441651

  --------------testSeed2()--------------

  -1367481220

  292886146

  -1462441651

  Process finished with exit code 0

  通過testSeed1()與testSeed2()方法的結果可以看到,兩個列印結果相同,因為他們種子相同,再執行一次,結果還是一樣的,這就是帶種子隨機數的特性。

  而不帶種子的,每次執行結果都是隨機的。

  五、綜合應用

  下面通過最近寫的一個隨機數工具類來展示用法:

  import java.util.Random;

  public class RandomUtils {

  public static final String allChar = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

  public static final String letterChar = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

  public static final String numberChar = "0123456789";  

  public static String generateString(int length) {

  StringBuffer sb = new StringBuffer();

  Random random = new Random();

  for (int i = 0; i < length; i++) {

  sb.append(allChar.charAt(random.nextInt(allChar.length())));

  }

  return sb.toString();

  }

  

  public static String generateMixString(int length) {

  StringBuffer sb = new StringBuffer();

  Random random = new Random();

  for (int i = 0; i < length; i++) {

  sb.append(allChar.charAt(random.nextInt(letterChar.length())));

  }

  return sb.toString();

  }

  

  public static String generateLowerString(int length) {

  return generateMixString(length).toLowerCase();

  }

  

  public static String generateUpperString(int length) {

  return generateMixString(length).toUpperCase();

  }

  

  public static String generateZeroString(int length) {

  StringBuffer sb = new StringBuffer();

  for (int i = 0; i < length; i++) {

  sb.append('0');

  }

  return sb.toString();

  }

  

  public static String toFixdLengthString(long num, int fixdlenth) {

  StringBuffer sb = new StringBuffer();

  String strNum = String.valueOf(num);

  if (fixdlenth - strNum.length() >= 0) {

  sb.append(generateZeroString(fixdlenth - strNum.length()));

  } else {

  throw new RuntimeException("將數字" + num + "轉化為長度為" + fixdlenth + "的字串發生異常!");

  }

  sb.append(strNum);

  return sb.toString();

  }

  

  public static String toFixdLengthString(int num, int fixdlenth) {

  StringBuffer sb = new StringBuffer();

  String strNum = String.valueOf(num);

  if (fixdlenth - strNum.length() >= 0) {

  sb.append(generateZeroString(fixdlenth - strNum.length()));

  } else {

  throw new RuntimeException("將數字" + num + "轉化為長度為" + fixdlenth + "的字串發生異常!");

  }

  sb.append(strNum);

  return sb.toString();

  }

  public static void main(String[] args) {

  System.out.println(generateString(15));

  System.out.println(generateMixString(15));

  System.out.println(generateLowerString(15));

  System.out.println(generateUpperString(15));

  System.out.println(generateZeroString(15));

  System.out.println(toFixdLengthString(123, 15));

  System.out.println(toFixdLengthString(123L, 15));

  }

  }

  執行結果:

  vWMBPiNbzfGCpHG

  23hyraHdJkKPwMv

  tigowetbwkm1nde

  BPZ1KNEJPHB115N

  000000000000000

  000000000000123

  000000000000123

  Process finished with exit code 0

  六、總結

  1、隨機數很常用,在Java有三種產生方式,以Random隨機數的使用最為複雜。

  2、Random類物件有是否帶種子之分,帶種子的只要種子相同,多次執行,生成隨機數的結果總是那樣。

  3、帶種子隨機數的帶種子的物件建立方式有兩種,效果一樣。但是帶種子的隨機數用處似乎不大。

  4、Random的功能涵蓋了Math.random()的功能。

  5、可以通過隨機數去做實現隨機字串等複雜的隨機資料。

  6、不要研究不重複的隨機數,意義不大。



      隨機數發生器(Random)物件產生以後,通過呼叫不同的method:nextInt()、nextLong()、nextFloat()、nextDouble()等獲得不同型別隨機數。

 
     1>生成隨機數
 
         Random random = new Random();
 
         Random random = new Random(100);//指定種子數100
 
         random呼叫不同的方法,獲得隨機數。
 
         如果2個Random物件使用相同的種子(比如都是100),並且以相同的順序呼叫相同的函式,那它們返回值完全相同。如下面程式碼中兩個Random物件的輸出完全相同
 
        import java.util.*;
 
        class TestRandom {
 
              public static void main(String[] args) {
 
                   Random random1 = new Random(100);
 
                   System.out.println(random1.nextInt());
 
                   System.out.println(random1.nextFloat());
 
                   System.out.println(random1.nextBoolean());
 
                   Random random2 = new Random(100);
 
                   System.out.println(random2.nextInt());
 
                   System.out.println(random2.nextFloat());
 
                   System.out.println(random2.nextBoolean());
 
              }
 
          }

 
      2>指定範圍內的隨機數
 
           隨機數控制在某個範圍內,使用模數運算子%
 
          import java.util.*;
 
               class TestRandom {
 
                    public static void main(String[] args) {
 
                         Random random = new Random();
 
                         for(int i = 0; i < 10;i++) {
 
                             System.out.println(Math.abs(random.nextInt()));
 
                         }
 
                    }
 
               }
 
           獲得的隨機數有正有負的,用Math.abs使獲取資料範圍為非負數

 
     3>獲取指定範圍內的不重複隨機數
 
          import java.util.*;
 
          class TestRandom {
 
                public static void main(String[] args) {
 
                     int[] intRet = new int[6];
 
                     int intRd = 0; //存放隨機數
 
                     int count = 0; //記錄生成的隨機數個數
 
                     int flag = 0; //是否已經生成過標誌
 
                     while(count<6){
 
                          Random rdm = new Random(System.currentTimeMillis());
 
                          intRd = Math.abs(rdm.nextInt())2+1;
 
                          for(int i=0;i<count;i++){
 
                              if(intRet[i]==intRd){
 
                                  flag = 1;
 
                                  break;
 
                              }else{
 
                                  flag = 0;
 
                              }
 
                          }
 
                          if(flag==0){
 
                              intRet[count] = intRd;
 
                              count++;
 
                          }
 
                 }
 
                for(int t=0;t<6;t++){
 
                    System.out.println(t+"->"+intRet[t]);
 
                }
 
             }
 
          }
Java中的隨機數是否可以重複?Java中產生的隨機數能否可以用來產生資料庫主鍵?帶著這個問題,我們做了一系列測試。
1.測試一: 使用不帶引數的Random()建構函式

public class RandomTest {

public static void main(String[] args) {
 
   java.util.Random r=new java.util.Random();
 
   for(int i=0;i<10;i++){
 
       System.out.println(r.nextInt());
 
   }
}
}
程式執行結果:
-1761145445
-1070533012
216216989
-910884656
-1408725314
-1091802870
1681403823
-1099867456
347034376
-1277853157

再次執行該程式:
-169416241
220377062
-1140589550
-1364404766
-1088116756
2134626361
-546049728
1132916742
-1522319721
1787867608

 
  從上面的測試我們可以看出,使用不帶引數的Random()建構函式產生的隨機數不會重複。那麼,什麼情況下Java會產生重複的隨機數呢?且看下面的測試。

2. 測試二:為Random設定種子數
public class RandomTest_Repeat {
  
   public static void main(String[] args) {
 
     java.util.Random r=new java.util.Random(10);
 
     for(int i=0;i<10;i++){
 
         System.out.println(r.nextInt());
 
     }
 
 }
}

無論程式執行多少次,其結果總是:
-1157793070
1913984760
1107254586
1773446580
254270492
-1408064384
1048475594
1581279777
-778209333
1532292428

甚至在不同的機器上測試,測試結果也不會改變!

結論
 
 通過上面的測試和分析,我們會對Random類有較為深刻的理解。同時,我覺得,通過閱讀Java Doc的API文件,可以很好地提高我們的Java程式設計能力,做到“知其然”;一旦遇到費解的問題,不妨開啟Java的原始碼,這樣我們就能做到“知其所以然”。


個人公眾號的二維碼如下,歡迎關注。

Java隨機演算法(一)(r11筆記第14天)

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/23718752/viewspace-2130836/,如需轉載,請註明出處,否則將追究法律責任。

相關文章