ski*_*iwi 14 java iterator yield java-8
我今天正在尝试这个问题,来自欧拉问题:
回文数字两种方式相同.由两个2位数字的乘积制成的最大回文是9009 = 91×99.
找到由两个3位数字的乘积制成的最大回文.
我考虑过它,它当然可以用for循环完成,但是我想使用Java 8,因为它打开了新的选项.
但首先,我不知道如何生成IntStream产生这样的元素,所以我仍然使用正常的for循环:
public class Problem4 extends Problem<Integer> {
private final int digitsCount;
private int min;
private int max;
public Problem4(final int digitsCount) {
this.digitsCount = digitsCount;
}
@Override
public void run() {
List<Integer> list = new ArrayList<>();
min = (int)Math.pow(10, digitsCount - 1);
max = min * 10;
for (int i = min; i < max; i++) {
for (int j = min; j < max; j++) {
int sum = i * j;
if (isPalindrome(sum)) {
list.add(sum);
}
}
}
result = list.stream().mapToInt(i -> i).max().getAsInt();
}
private boolean isPalindrome(final int number) {
String numberString = String.valueOf(number);
String reversed = new StringBuilder(numberString).reverse().toString();
return (numberString.equals(reversed));
}
@Override
public String getName() {
return "Problem 4";
}
}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,我可能有点懒惰,真的IntStream::max是一个非常好的方法,我认为最好使用它,因为自己写.
这里谈到的问题,不过,我需要有一个list我们能够获得这样的最大值,这意味着我需要存储的数据,在这里我真的不应该这样做.
那么,现在的问题是,是否可以在Java 8中实现这一点?
for (int i = min; i < max; i++) {
for (int j = min; j < max; j++) {
yield i * j;
}
}
Run Code Online (Sandbox Code Playgroud)
然后从那个方法创建一个PrimitiveIterator.OfInt(unboxes版本Iterator<Integer>,或IntStream直接创建?
然后得到答案streamFromYield.filter(this::isPalindrome).max().getAsInt()将非常容易实现.
最后,我知道这个问题之前已经被问过了,但是最后一次已经很久了,现在Java 8将很快发生,他们已经添加了大概念Stream<T>和新的语言结构,称为lambdas.
因此,现在制作这样的代码可能与人们为Java 6或7制作代码时的情况大不相同.
好吧,我认为我们已经从"外部"使用Streams API,使用flatMap,优化回文查找算法,等等.请参阅Boris the Spider和assylias的答案.但是,我们已经回避了如何使用Python的yield语句编写生成器函数的原始问题.(我认为OP的嵌套 - 例如yield使用Python.)
使用的一个问题flatMap是并行拆分只能在最外层流上进行.内部流(从中返回flatMap)按顺序处理.我们可以尝试使内部流也平行,但它们可能与外部流竞争.我认为嵌套拆分可以工作,但我不太自信.
一种方法是使用Stream.generate或(如assylias'答案)Stream.iterate函数.但是,这些会创建无限的流,因此limit必须提供外部来终止流.
如果我们能够创建一个有限但"平坦化"的流,那么整个值的流可能会分裂,这将是很好的.不幸的是,创建一个流并不像Python的生成器函数那么方便.不过,它可以毫不费力地完成.这是一个使用StreamSupport和AbstractSpliterator类的示例:
class Generator extends Spliterators.AbstractIntSpliterator {
final int min;
final int max;
int i;
int j;
public Generator(int min, int max) {
super((max - min) * (max - min), 0);
this.min = min;
this.max = max;
i = min;
j = min;
}
public boolean tryAdvance(IntConsumer ic) {
if (i == max) {
return false;
}
ic.accept(i * j);
j++;
if (j == max) {
i++;
j = min;
}
return true;
}
}
public static void main(String[] args) {
Generator gen = new Generator(100, 1000);
System.out.println(
StreamSupport.intStream(gen, false)
.filter(i -> isPalindrome(i))
.max()
.getAsInt());
}
Run Code Online (Sandbox Code Playgroud)
不是让迭代变量在堆栈上(如在with yield-for with yield方法中),我们必须使它们成为一个对象的字段,并tryAdvance在迭代完成之前增加它们.现在,这是分离器的最简单形式,并不一定能很好地并行化.通过额外的工作,可以实现该trySplit方法以进行更好的分割,这反过来将实现更好的并行性.
该forEachRemaining方法可以被覆盖,它看起来几乎像嵌套for-loop-with-yield示例,调用IntConsumer而不是yield.遗憾的tryAdvance是它是抽象的,因此必须实现,因此仍然需要使迭代变量成为对象的字段.
我会尝试一下。带有循环的版本,然后带有流的版本。虽然我从另一端开始,所以更容易,因为我可以limit(1)。
public class Problem0004 {
public static void main(String[] args) {
int maxNumber = 999 * 999;
//with a loop
for (int i = maxNumber; i > 0; i--) {
if (isPalindrome(i) && has3DigitsFactors(i)) {
System.out.println(i);
break;
}
}
//with a stream
IntStream.iterate(maxNumber, i -> i - 1)
.parallel()
.filter(i -> isPalindrome(i) && has3DigitsFactors(i))
.limit(1)
.forEach(System.out::println);
}
private static boolean isPalindrome(int n) {
StringBuilder numbers = new StringBuilder(String.valueOf(n));
return numbers.toString().equals(numbers.reverse().toString());
}
private static boolean has3DigitsFactors(int n) {
for (int i = 999; i > 0; i--) {
if (n % i == 0 && n / i < 1000) {
return true;
}
}
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2982 次 |
| 最近记录: |