IntStream.range(0,1_000_000)止损于113383(Project Euler:Longest Collat​​z Sequence)

时间:2014-02-25 16:52:18

标签: java lambda java-8 java-stream collectors

我的代码中有一个奇怪的问题要回答这个问题:

  

为正整数集定义了以下迭代序列:

     

n→n / 2(n是偶数)
  n→3n + 1(n为奇数)

     

使用上面的规则并从13开始,我们生成以下序列:
  13→40→20→10→5→16→8→4→2→1

     

可以看出,该序列(从13开始,在1结束)包含10个术语。虽然尚未证实(Collat​​z问题),但据认为所有起始数字都以1结束。

     

哪个起始编号低于一百万,产生最长的链?

     

注意:一旦链条启动,条款允许超过一百万。

public class Problem14 extends Problem<Integer> {
    private final int maximum;

    public Problem14(final int maximum) {
        this.maximum = maximum;
    }

    @Override
    public void run() {
        result = IntStream.range(1, maximum).boxed()
                .peek(System.out::println)
                .collect(Collectors.toMap(i -> i, i -> (int)Iterators.intStream(new CollatzGenerator(i)).count()))
                .entrySet().stream()
                .peek(System.out::println)
                .max(Comparator.comparingInt(Map.Entry::getValue))
                .get().getKey();
    }

    @Override
    public String getName() {
        return "Problem 14";
    }
}

public abstract class Problem<T> implements Runnable {
    protected T result;

    public String getResult() {
        return String.valueOf(result);
    }

    abstract public String getName();
}

public class CollatzGenerator implements PrimitiveIterator.OfInt {
    private int current;

    public CollatzGenerator(final int start) {
        if (start < 1) {
            throw new IllegalArgumentException("generators.CollatzGenerator: start < 1: start = " + start);
        }
        this.current = start;
    }

    @Override
    public boolean hasNext() {
        return (current != 1);
    }

    @Override
    public int nextInt() {
        int returnInt = current;
        if (current % 2 == 0) {
            current /= 2;
        }
        else {
            current = 3 * current + 1;
        }
        return returnInt;
    }
}

public abstract class Iterators {
    //...

    public static IntStream intStream(final PrimitiveIterator.OfInt iterator) {
        return StreamSupport.intStream(
                Spliterators.spliteratorUnknownSize(iterator, 0), false
        );
    }

    //...
}

调用代码的方式如下:

new Problem14(1_000_000).run();

所以重新说一下,问题是程序永远不会终止,我所看到的是它打印的所有整数从1到113383,可能是从第一次.peek(System.out::println)调用。

另外一个问题是,目前我将IntStream打包到Stream<Integer>,以便能够.collect(Collectors.toMap(i -> i, i -> (int)Iterators.intStream(new CollatzGenerator(i)).count())) ... 我想摆脱拳击并使用IntStream::collect方法,但我不明白该怎么做。

2 个答案:

答案 0 :(得分:3)

  

所以重新说一下,问题是程序永远不会终止,我所看到的是它打印的所有整数从1到113383,可能是从第一次.peek(System.out::println)调用。

这种情况正在发生,因为您假设将在collat​​z中生成的所有数字都在int类型的范围内。但事实并非如此。这就是失败的原因。尝试将所有内容设为long - PrimitiveIterator.OfLongStreamSupport.longStream等,这样就可以了。

  

我想摆脱拳击并使用IntStream::collect方法,但我不明白该怎么做。

我不明白你为什么要那样做。拳击仍将在某处完成,因为您无法创建原始Map的{​​{1}}。不过,如果你愿意,你实际上还需要做更多的工作。这是你如何做到的:

int

答案 1 :(得分:0)

我用C编写了短代码,可以使用大数字“无限”运行(我以104282959结尾,在这里我杀死了它)。它检查数字是否收敛到一个(证明有效,继续下一个未检查)。

优化很少,可能会有点混乱。但是我还没有读过任何论文,因此可以进行更多优化。

没有保证它能很好地工作。

//gcc main.c -lgmp

#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>

void collatz_conjucture(mpz_t *res, mpz_t n)
{
  mpz_mod_ui(*res, n, 1UL);
  if(mpz_cmp_ui(*res, 1UL) == 0) {
    mpz_mul_ui(*res, n, 3UL);
    mpz_add_ui(*res, *res, 1UL);
    //mpz_fdiv_q_ui(*res, *res, 2UL); //Posible to uncomment, skip one step of computation (always odd after even), but it will slow down increment of next
  } else {
    mpz_fdiv_q_ui(*res, n, 2UL);
  }
}

int main(int argc, char **argv)
{
  // Init
  mpz_t current_b, next_b;
  mpz_init_set_str(current_b, "1", 10);
  mpz_init_set(next_b, current_b);
  //Starts computation - i means step
  for (unsigned long i = 0; i < 0xFFFFF; ++i) {
  //for (;;) { // replace above for infinite
    mpz_set(current_b, next_b);
    do {
      if(mpz_cmp(current_b, next_b) == 0) {
        mpz_add_ui(next_b, next_b, 1UL);
      }
      collatz_conjucture(&current_b, current_b);
    } while (mpz_cmp(current_b, next_b) > 0);
    char *result = mpz_get_str(NULL, 10, next_b);
    printf("%s\n", result);
    free(result);
    result = NULL;
  }
  mpz_clear(current_b);
  mpz_clear(next_b);
  return 0;
}