繁体   English   中英

检查数字是否在斐波那契数列中?

[英]Checking whether a number is in Fibonacci Sequence?

有人要求找到一种检查数字是否在斐波那契数列中的方法。 约束是
1≤T≤10^ 5

1≤N≤10^ 10

T是测试用例的数量,

N是给定的数字,即要测试的斐波那契候选人。

我使用以下事实写了它:(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");
        }
       }
     }
  }

但是它不能清除所有测试用例吗? 我可以做哪些错误修复?

一个更简单的解决方案基于以下事实: 在10 ^ 10以下只有49个斐波那契数

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

运行时复杂度为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);
}

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

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

重组代码,以便使用整数算术执行测试。 但是请注意,N <10 10表示N 2 <10 20 那比long还大...所以您将需要使用...

更新

不仅如此。 首先, Math.sqrt(double)确保为您提供一个double Math.sqrt(double)结果,该结果将四舍五入为最接近真平方根的double值。 因此,您可能会认为我们很清楚。

但是问题在于,N乘以N最多有20个有效数字……这比将数字扩展为double数以进行sqrt调用时所能表示的数字还要多。 (根据Wikipedia的说法, double精度数的精度为15.95个十进制数字。)

最重要的是,编写的代码可以做到这一点:

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

对于较大的cand值,容易溢出。 如果您使用long而不是int甚至可能溢出,因为cand值可能高达10 ^ 10。 (一个long整数可以表示最多+9,223,372,036,854,775,807 ...的数字,该数字小于10 20。 )然后我们必须将N 2乘以5。

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

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

根据链接, 当且仅当(5 * n2 + 4)或(5 * n2 – 4)中的一个或两个为完美平方时数字才是斐波那契数,因此您基本上可以进行此检查。

希望这可以帮助 :)

如果您通过平方运算使用幂运算,请对每个测试用例使用二进制搜索Fibonacci Q-矩阵求解 O((log n)^2)

您的解决方案不起作用,因为它涉及到舍入大量的浮点平方根(可能足够大,甚至无法容纳long ),有时这并不精确。

二进制搜索将像这样工作:find Q^m :如果第m个斐波那契数大于您的斐波那契数,则设置right = m ,如果相等,则返回true ,否则设置left = m + 1

正确地说,sqrt可以四舍五入。 所以:

  • 即使使用long而不是int,它也有18位数字。
  • 即使您使用Math.round(),也不能简单地使用(int)或(long)。 请注意,因此,即使是很小的数字,您的函数也无法正常工作。

双精度数有14位数字,长整数有18位数字,因此您不能使用正方形,需要20位数字。

BigInteger和BigDecimal没有sqrt()函数。

因此,您有以下三种方式:

  • 为BigInteger编写自己的sqrt。
  • 检查发现的不精确double sqrt()周围的所有数字是否为真实sqrt。 这意味着还要同时处理数字及其错误。 (太恐怖了!)
  • 计算所有10 ^ 10以下的斐波那契数,并与之进行比较。

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

在我看来,for循环没有任何意义吗?

当您为我删除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");
            }


    }

}

您只需要测试给定的候选人,是吗? for循环完成什么工作? 循环的结果会使您的测试程序失败吗?

另外,代码中缺少}。 如果未在末尾添加另一个},它将无法按发布的方式运行,此后,对于以下输入,它可以正常运行:

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

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

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作为正在考虑的斐波那契数列中的元素数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM