【问题标题】:Alternative to long in order to avoid overflowing Fibonacci Numbers替代 long 以避免溢出斐波那契数
【发布时间】:2016-07-10 06:28:12
【问题描述】:

Stackoverflow 的新手,所以请指出我可以做些什么来提高我的问题的质量。

所以我的代码所做的(或者更确切地说希望做的)是计算巨大的斐波那契数以一个非常大的 m 为模。为了使算法更高效,我使用了pisano periods。本质上,我计算了 m 的 pisano 周期,然后通过使用以下关系使余数的计算更容易:

n个斐波那契数的余数(模m)等于第k个斐波那契数的余数(模m) 使得 k = n % p 其中 p 是 pisano 周期m.

为了计算皮萨诺周期,我使用以下属性:

如果当前的Fib % m = 0,并且到目前为止所有Fib的总和 % m = 0,那么当前Fib的索引是pisano周期。 (注意索引必须大于0)

但是我在这项工作中遇到了一个问题:要计算 pisano 周期,我必须计算连续的斐波那契数。当必须计算的斐波那契数的数量变得非常大(例如 100 000)时,就会出现问题。然后数据类型 long 溢出。

据我所知,任何计算 pisano 周期的努力都需要计算斐波那契,因此唯一的解决方案似乎是将 long 替换为其他东西。如果有人对这个替代品有任何建议,我将不胜感激。

import java.util.*;

public class FibHuge {
    public static void main (String [] args) {
        Scanner in = new Scanner (System.in);
        long num = in.nextLong ();
        long mod = in.nextLong();

        System.out.println ( getMod(num, mod));
    }

    private static int getMod (long num, long mod) {
        Period per = new Period();

        long period = per.getPeriod (mod);
        int newFibNum = (int)(num % period);

        num = (num % mod);

        Integer ia[] = new Integer [per.al.size()];
        ia = per.al.toArray (ia);

        return ia[newFibNum];
    }
}

class Period {

    ArrayList <Long> al;
    long FNum;
    long SNum;

    Period () {
        al = new ArrayList <Long> ();
        FNum = 0;
        SNum = 1;
    }

    private long getFib (long first, long second){
        return first + second;
    }

    long getPeriod (long mod){
        boolean bool = true;
        long fibcount = 0;

        long currentmod = 0;
        long fib = 0;
        long sum = 0;

        while (bool){
            if (fibcount <= 1){
                currentmod = fibcount % mod;

                al.add (currentmod);

                sum += fibcount;
            }

            else {
                fib = getFib (FNum, SNum);
                FNum = SNum;
                SNum = fib;

                currentmod = (fib % mod);
                al.add (currentmod);

                sum += fib;
            }

            if ( (currentmod == 0 & (sum % mod) == 0) & fibcount > 0){
                return fibcount;
            }
            fibcount++;
        }

        return mod; //essentially just to satisfy the return condition
    }
}

【问题讨论】:

  • 当需要计算的斐波那契数变得非常大,比如 100 000 时,就会出现问题。 - 我认为你对斐波那契数有错误的感觉。它们呈指数级快速增长 - 第 93 个斐波那契数超出了多头的范围。
  • @Leon 哦,好吧,这比我想象的要糟糕得多。尽管我知道它们呈指数级增长,但我没想到会这么快就消失了。

标签: java performance math long-integer


【解决方案1】:

使用BigInteger,但请注意它会慢得多,但大小无限。

【讨论】:

  • 慢多少?该问题需要最长 1.5 秒的运行时间。
  • 在不知道处理器、时钟速度以及需要进行多少计算的情况下,1.5 秒是毫无意义的。 BigInteger 可能工作正常,但您可能在尝试之前不会知道。
  • “慢多少?” - 试试看。
  • 注意:你实际上并不需要 BigInteger 来解决这个问题,除非模数非常大(太大而无法将所有结果存储在内存中)请参阅我的答案。
【解决方案2】:

除非您的模数太大而无法放入 long,否则您不需要使用 BigInteger,在这种情况下,我怀疑您在寻找解决方案时会耗尽内存。

您可以使用此属性在取模后计算第 n 个斐波那契数,而不是计算第 n 个斐波那契数然后再进行取模

(a + b) % n = (a % n + b % n) % n;

换句话说,您只需要在每次迭代中不断添加数字的模数。您可以将所有模数值保存在 Set 中,当您获得重复结果时,您有一个句点。您可以将迭代次数与结果一起存储,并使用它来计算周期。

事实上,模数有点贵,但因为你只会对小于 2 * 模数的数字求和,所以你可以简单地做

long c = a + b; // Fibonacci
if (c >= modulus) c -= modulus; // the only real change you need for modulus.

由于 Java 使用条件移动而不是实际分支,这比使用 % 快​​得多

如果不为您编写代码,我想不出您需要了解的更多细节。

【讨论】:

  • 我喜欢这个想法,但不幸的是我不能使用它,除非我创造一个新的属性来识别周期内的重复,即斐波那契数的总和 % m。这是我的代码不可分割的一部分,因为它是表明序列重复的两件事之一。
  • @Airdish 这就是为什么您需要一个值映射到该数字最后出现的最后计数的原因。一旦你得到一个副本,你就知道时间段了。
  • 很抱歉,但我不确定我是否理解。在周期实际重复之前,序列中经常出现重复。唯一的识别模式是每次迭代都从 0、1 开始。
  • @Airdish 忽略设置/地图的想法。只需利用 Peter 引起您注意的模算术属性来运行您的算法。 IE。不要在循环中跟踪fibsum,而是跟踪fib % modsum % mod
  • 非常感谢 Peter Lawrey 和 @Leon 的想法。我的程序现在真的很快。 :)
猜你喜欢
  • 2021-11-30
  • 2017-02-13
  • 2016-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多