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 实战

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