【问题标题】:IntStream.range(0, 1_000_000) stops at 113383 (Project Euler: Longest Collatz Sequence)IntStream.range(0, 1_000_000) 在 113383 处停止(Project Euler:最长的 Collat​​z 序列)
【发布时间】:2014-02-25 16:52:17
【问题描述】:

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

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

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

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

可以看出,这个序列(从 13 开始,到 1 结束)包含 10 个术语。虽然还没有被证明(Collat​​z Problem),但人们认为所有的起始数字都以 1 结束。

100 万以下的哪个起始数字产生最长的链?

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

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&lt;Integer&gt; 以便能够执行.collect(Collectors.toMap(i -&gt; i, i -&gt; (int)Iterators.intStream(new CollatzGenerator(i)).count()))...
我想摆脱拳击并使用IntStream::collect 方法,但是我不明白该怎么做。

【问题讨论】:

  • 那么问题出在哪里?
  • @EdCottrell 问题出在标题中,我会更清楚地更新正文。
  • @skiwi 请也发布Problem 课程。
  • @RohitJain 也添加了,我一直忘记它,因为它对我来说太微不足道了

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


【解决方案1】:

换句话说,问题在于程序永远不会终止,我所看到的是它打印了从 1 到 113383 的所有整数,大概来自第一个 .peek(System.out::println) 调用。

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

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

我不明白你为什么要这样做。拳击仍然会在某处进行,因为您无法创建原始intMap。尽管如此,如果你愿意,你实际上还需要做更多的工作。以下是你的做法:

@Override
public void run() {

    ObjIntConsumer<Map<Integer, Long>> objIntConsumer = 
            (map, value) -> map.put(value, Iterators.longStream(new CollatzGenerator(value)).count());

    BiConsumer<Map<Integer, Long>, Map<Integer, Long>> biConsumer = 
            (targetMap, accumulatorMap) -> targetMap.putAll(accumulatorMap);

    result = IntStream.range(1, maximum)
            .collect(LinkedHashMap::new, objIntConsumer, biConsumer)
            .entrySet().stream()
            .peek(System.out::println)
            .max(Comparator.<Entry<Integer, Long>>comparingLong(entry -> entry.getValue()))
            .get().getKey();
}

【讨论】:

  • 事实上,即使unsigned long,这个数字的计算也会溢出
【解决方案2】:

我用 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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多