检查一个数字是否在Fibonacci序列中?

时间:2015-02-20 09:21:39

标签: java algorithm

有人要求找到一种方法来检查一个数字是否在Fibonacci序列中。 约束是
1≤T≤10^ 5

1≤N≤10^ 10

其中T是测试用例的数量,

和N是给定的数字,即要测试的Fibonacci候选者。

我使用以下事实编写了以下数据:当且仅当(5 * n2 + 4)或(5 * n2 - 4)中的一个或两个是完美的正方形时,数字是斐波那契: -

import java.io.*;
import java.util.*;

public class Solution {

 public static void main(String[] args) {

    Scanner sc = new Scanner(System.in);
    int n = sc.nextInt();

    for(int i = 0 ; i < n; i++){
        int cand = sc.nextInt();
        if(cand < 0){System.out.println("IsNotFibo"); return; }
        int aTest =(5 * (cand *cand)) + 4;
        int bTest = (5 * (cand *cand)) - 4;
        int sqrt1 = (int)Math.sqrt(aTest);// Taking square root of aTest, taking into account only the integer part.
        int sqrt2 = (int)Math.sqrt(bTest);// Taking square root of bTest, taking into account only the integer part.
        if((sqrt1 * sqrt1 == aTest)||(sqrt2 * sqrt2 == bTest)){
            System.out.println("IsFibo");
        }else{
            System.out.println("IsNotFibo");
        }
       }
     }
  }

但它没有清除所有测试用例?我可以修复哪些错误?

8 个答案:

答案 0 :(得分:3)

一个更简单的解决方案是基于there are only 49 Fibonacci numbers below 10^10的事实。

预先计算它们并将它们存储在数组或哈希表中以进行一致性检查。

运行时复杂度为O(log N + T):

Set<Long> nums = new HashSet<>();
long a = 1, b = 2;
while (a <= 10000000000L) {
    nums.add(a);
    long c = a + b;
    a = b;
    b = c;
}
// then for each query, use nums.contains() to check for Fibonacci-ness

如果你想沿着完美的方形路线前进,你可能想要使用任意精度的算术:

// find ceil(sqrt(n)) in O(log n) steps
BigInteger ceilSqrt(BigInteger n) {
    // use binary search to find smallest x with x^2 >= n
    BigInteger lo = BigInteger.valueOf(1),
               hi = BigInteger.valueOf(n);
    while (lo.compareTo(hi) < 0) {
        BigInteger mid = lo.add(hi).divide(2);
        if (mid.multiply(mid).compareTo(x) >= 0)
            hi = mid;
        else
            lo = mid.add(BigInteger.ONE);
    }
    return lo;
}
// checks if n is a perfect square
boolean isPerfectSquare(BigInteger n) {
    BigInteger x = ceilSqrt(n);
    return x.multiply(x).equals(n);
}

答案 1 :(得分:2)

您对完美方块的测试涉及浮点计算。这可能会给你错误的答案,因为浮点计算通常会给你不准确的结果。 (浮点数最多与实数相近。)

在这种情况下,sqrt(n*n)可能会为n - epsilon提供一些小epsilon,而(int) sqrt(n*n)将为n - 1而不是预期的n

重构代码,以便使用整数运算执行测试。但请注意N&lt; 10 10 表示N 2 <10。 10 20 。这比long大......所以你需要使用......

<强>更新

除此之外还有更多。首先,Math.sqrt(double)保证会为您提供double结果,该结果四舍五入到与真正平方根最接近的double值。所以你可能会认为我们是清楚的(就像它一样)。

但问题是N乘以N最多有20位有效数字...当您将数字扩展为double以便生成sqrt时,可以表示更多数字呼叫。 (根据维基百科,A double的精确度为15.95十进制数。)

最重要的是,编写的代码执行此操作:

int cand = sc.nextInt();
int aTest = (5 * (cand * cand)) + 4;

对于cand的大值,可能会溢出。如果您使用long代替int,它甚至会溢出......假设cand值可能高达10 ^ 10。 (A long可以表示高达+ 9,223,372,036,854,775,807的数字......小于10 20 。然后我们必须将N 2 乘以5。

总之,虽然代码应适用于小型候选者,但对于非常大的候选者,它可能会在您尝试读取候选者(作为int)时中断,或者由于整数溢出而导致错误答案(作为long)。

解决此问题需要重新考虑。 (或者比我更深入的分析表明计算危险不会导致对可能输入范围内任何大N的错误答案。)

答案 2 :(得分:1)

根据this链接,当且仅当(5 * n2 + 4)或(5 * n2 - 4)中的一个或两个是完美的正方形时,数字才是斐波那契你基本上可以做这个检查。

希望这会有所帮助:)

答案 3 :(得分:1)

如果您使用binary search,请在每个测试案例中使用Fibonacci Q-matrixexponentiation by squaring O((log n)^2)解决方案。

你的解决方案不起作用,因为它涉及舍入大数字的浮点平方根(可能足够大,甚至不适合long),这有时不准确。

二进制搜索的工作方式如下:查找Q^m:如果m - 斐波纳契数大于你的,请设置right = m,如果等于,则返回true },否则设置left = m + 1

答案 4 :(得分:1)

正如所说,sqrt可以向下舍入。所以:

  • 即使您使用long而不是int,它也有18位数字。
  • 即使您使用Math.round(),也不仅仅是(int)或(long)。请注意,由于这个原因,即使是小数字,你的功能也无法正常工作。

double有14位数,long有18位,所以你不能使用正方形,你需要20位数。

BigInteger和BigDecimal没有sqrt()函数。

所以,你有三种方法:

  • 为BigInteger编写自己的sqrt。
  • 检查找到的不准确的双sqrt()周围的所有数字是否为真正的sqrt。这意味着同时处理数字及其错误。 (这很恐怖!)
  • 将所有斐波那契数字计算在10 ^ 10以下并与之进行比较。

最后一个变体是迄今为止最简单的变体。

答案 5 :(得分:0)

在我看来,for-loop没有任何意义吗?

当你为我删除for循环时,该程序按照宣传的方式工作:

    import java.io.*;
    import java.util.*;

    public class Solution {

     public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int cand = sc.nextInt();

            if(cand < 0){System.out.println("IsNotFibo"); return; }
            int aTest = 5 * cand *cand + 4;
            int bTest = 5 * cand *cand - 4;
            int sqrt1 =  (int)Math.sqrt(aTest);
            int sqrt2 = (int)Math.sqrt(bTest);
            if((sqrt1 * sqrt1 == aTest)||(sqrt2 * sqrt2 == bTest)){
                System.out.println("IsFibo");
            }else{
                System.out.println("IsNotFibo");
            }


    }

}

答案 6 :(得分:0)

你只需要测试给定的候选人,是吗?什么是for循环完成?循环结果可能会导致您的测试程序失效吗?

此外,代码中缺少}。如果没有在末尾添加另一个},它将不会像已发布的那样运行,之后它可以正常运行以下输入:

10 1 2 3 4 5 6 7 8 9 10
IsFibo
IsFibo
IsFibo
IsNotFibo
IsFibo
IsNotFibo
IsNotFibo
IsFibo
IsNotFibo
IsNotFibo

答案 7 :(得分:0)

考虑到以上所有建议,我写了以下通过所有测试用例

import java.io.*;

import java.util.*;

public class Solution {

public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    long[] fib = new long[52];
    Set<Long> fibSet = new HashSet<>(52);
    fib[0] = 0L;
    fib[1] = 1L;
    for(int i = 2; i < 52; i++){
        fib[i] = fib[i-1] + fib[i - 2];
        fibSet.add(fib[i]);
    }

    int n = sc.nextInt();
    long cand;

    for(int i = 0; i < n; i++){
        cand = sc.nextLong();
        if(cand < 0){System.out.println("IsNotFibo");continue;}
        if(fibSet.contains(cand)){
            System.out.println("IsFibo");
        }else{
            System.out.println("IsNotFibo");
        }  
     }
   }
 }

我希望处于更安全的一面,因此我选择52作为所考虑的Fibonacci序列中的元素数量。