Java8 數值流應用

Java8 數值流應用

  1. 勾股數

那麼什麼時勾股數呢?我們得回到從前。在一堂激動人心的數學課上,你瞭解到,古希臘數學家畢達哥拉斯發現了某些三元數(a, b, c)滿足公式a * a + b * b = c * c,其中a, b, c都是整數。例如(3,4,5)就是一組有效的勾股數,因爲 3*3 + 4*4 = 5*5或者9 + 16 = 25.這樣的三元數有無限組。例如,(5, 12, 13),(6,8,10)和(7,24,25)都是有效的勾股數。勾股數很有用,因爲它們描述的正好是直角三角形的三條邊,如圖下圖所示:

2. 表示三元數

   那麼,怎麼入手呢?第一步是定義一個三元數。雖然更恰當的做法是定義一個新的類來表示三元數,但這裏你可以使用具有三個元素的int數組,比如new int[] {3,4,5},來表示勾股數(3,4,5)。現在你就可以用數組索引訪問每一個元素。

3. 篩選成立的組合

   假如有人爲你提供了三元數中的前2個數字:a和b。怎麼知道他是否能形成一組勾股數呢?你需要測試a*a + b*b 的平方根是不是整數,那就是說它沒有小數部分——在java裏可以使用expr % 1表示。如果他不是整數,那就說c不是整數。你可以用filter操作表達這個要求

Filter(b -> Math.sqrt(a*a + b*b)%1==0)

  假設周圍的代碼給a提供了一個值,並且stream提供了b可能出現的值,filter將只會選出那些可以與a組成勾股數的b。你可能在想math.sqrt(a*a + b*b) == 0這一行是怎麼回事。簡單的說,這是一種測試Math.sqrt(a*a + b*b) 返回的結果是不是整數的方法,如果平方根的結果帶了小數,如9.1,這個條件就不成立。

4. 生成三元組

在篩選之後,你知道a和b能夠組成一個正確的組合。現在需要創建一個三元組。你可以使用map操作,像下面這樣把每個元素轉換成一個勾股數組:

Stream.filter(b -> Math.sqrt(a*a + b*b)%1==0)

.map( b -> new int[]{a, b, (int)Math.sqrt(a*a + b*b)}

 

5. 生成b值

   勝利在望,現在你需要生成b的值。前面已經看到,stream.rangeClosed讓你可以在給定區間內生成一個數值流。你可以用它來給b提供數值,這裏是1到100:

IntStream.rangeClosed(1,100)

.filter(b -> Math.sqrt(a*a + b*b)%1==0)

    .boxed()

.map(b->new int[]{a, b, (int)Math.sqrt(a*a + b*b)})

   請注意,你在filter之後調用boxed,從rangeClosed返回的IntStream生成一個Stream<Integer>。這是因爲你的map會爲流中的每個元素返回一個int數組。而IntStream中的map方法只能爲流中的每個元素返回另一個int,這可不是你想要的,你可以用IntStream的mapToObj方法改寫它,這個方法會返回一個對象值流:

   IntStream.rangeClosed(1,100)

.filter(b -> Math.sqrt(a*a + b*b)%1==0)

.mapToObj(b-> new int[]{a, b, (int)Math.sqrt(a*a + b*b)}};

6. 生成值

  這裏有一個關鍵的假設,給出了a的值。現在,只要已知a的值,你就有了一個可以生成勾股數的流。如何解決這個問題呢?就像b一樣,你需要爲a生成數值!最終的解決方案如下所示:

Stream<int[]> pythagoreanThriples =

     IntStream.rangeClosed(1,100)

.flatMap(a->IntStream.rangeClosed(a, 100)

.filter(b ->Math.sqrt(a*a +b*b)%1==0)

.mapToObj(b -> new int[]{a, b, (int)Math.sqrt(a*a + b*b)}));

   好的,flatMap又是怎麼回事呢?首先,創建一個從1到100的數值範圍來生成a的值。對每個給定的a值,創建一個三元數流。要是把a的值映射到三元數流的話,就會得到一個由流構成的流。FlatMap方法在做映射的同時,還會把所有生成的三元數據流扁平化成一個流。這樣你就得到了一個三元數流。還要注意,我們把b的範圍改成a到100。沒有必要再從1開始了,否則就會造成重複的三元數,例如(3, 4,5)和(4,3,5)。

7. 運行代碼

   現在你可以運行解決方案,並且可以利用我們前面看到的limit命令,明確限定從生成的流中要返回多少組勾股流了:

pythagoreanThriples.limit(5)

.forEach( t ->

System.out.println(t[0] +   + t[1] +   + t[2])

  這樣就會打印:

   3,4,5

    5,12,13

    6,8,10

    7,24,25

    8,15,17

8. 你還能做的更好嘛?

  目前的解決方法並不是最優的,因爲你每次都要兩次平方根。讓代碼更爲緊湊的一種可能的方法是,先生成所有的三元數(a*a , b*b, c*c),然後再篩選符合條件的:

Stream<doouble> pythagoreanThriples  =

     IntStream.rangeClosed(1, 100).boxed()

.flatMap( a -> IntStream.rangeClosed(a, 100)

.mapToObj( b-> new Double[]{a, b, Math.sqrt(a*a+b*b)}).filter( t-> t[2]%1==0));

參考書:java8 實戰

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