简体   繁体   English

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

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

It was asked to find a way to check whether a number is in the Fibonacci Sequence or not. 有人要求找到一种检查数字是否在斐波那契数列中的方法。 The constraints are 约束是
1≤T≤10^5 1≤T≤10^ 5

1≤N≤10^10 1≤N≤10^ 10

where the T is the number of test cases, T是测试用例的数量,

and N is the given number, the Fibonacci candidate to be tested. N是给定的数字,即要测试的斐波那契候选人。

I wrote it the following using the fact a number is Fibonacci if and only if one or both of (5*n2 + 4) or (5*n2 – 4) is a perfect square :- 我使用以下事实写了它:(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");
        }
       }
     }
  }

But its not clearing all the test cases? 但是它不能清除所有测试用例吗? What bug fixes I can do ? 我可以做哪些错误修复?

A much simpler solution is based on the fact that there are only 49 Fibonacci numbers below 10^10 . 一个更简单的解决方案基于以下事实: 在10 ^ 10以下只有49个斐波那契数

Precompute them and store them in an array or hash table for existency checks. 预先计算它们,并将它们存储在数组或哈希表中以进行存在性检查。

The runtime complexity will be O(log N + T): 运行时复杂度为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

If you want to go down the perfect square route, you might want to use arbitrary-precision arithmetics: 如果您想走完美的平方路线,则可能要使用任意精度算法:

// 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);
}

Your tests for perfect squares involve floating point calculations. 您对完美正方形的测试涉及浮点计算。 That is liable to give you incorrect answers because floating point calculations typically give you inaccurate results. 这很可能给您错误的答案,因为浮点计算通常会给您不准确的结果。 (Floating point is at best an approximate to Real numbers.) (浮点数最多近似于实数。)

In this case sqrt(n*n) might give you n - epsilon for some small epsilon and (int) sqrt(n*n) would then be n - 1 instead of the expected n . 在这种情况下, sqrt(n*n)可能会为您提供一些小epsilon的n - epsilon ,然后(int) sqrt(n*n)将为n - 1而不是预期的n

Restructure your code so that the tests are performed using integer arithmetic. 重组代码,以便使用整数算术执行测试。 But note that N < 10 10 means that N 2 < 10 20 . 但是请注意,N <10 10表示N 2 <10 20 That is bigger than a long ... so you will need to use ... 那比long还大...所以您将需要使用...

UPDATE 更新

There is more to it than this. 不仅如此。 First, Math.sqrt(double) is guaranteed to give you a double result that is rounded to the closest double value to the true square root. 首先, Math.sqrt(double)确保为您提供一个double Math.sqrt(double)结果,该结果将四舍五入为最接近真平方根的double值。 So you might think we are in the clear (as it were). 因此,您可能会认为我们很清楚。

But the problem is that N multiplied by N has up to 20 significant digits ... which is more than can be represented when you widen the number to a double in order to make the sqrt call. 但是问题在于,N乘以N最多有20个有效数字……这比将数字扩展为double数以进行sqrt调用时所能表示的数字还要多。 (A double has 15.95 decimal digits of precision, according to Wikipedia.) (根据Wikipedia的说法, double精度数的精度为15.95个十进制数字。)

On top of that, the code as written does this: 最重要的是,编写的代码可以做到这一点:

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

For large values of cand , that is liable to overflow. 对于较大的cand值,容易溢出。 And it will even overflow if you use long instead of int ... given that the cand values may be up to 10^10. 如果您使用long而不是int甚至可能溢出,因为cand值可能高达10 ^ 10。 (A long can represent numbers up to +9,223,372,036,854,775,807 ... which is less than 10 20 .) And then we have to multiply N 2 by 5. (一个long整数可以表示最多+9,223,372,036,854,775,807 ...的数字,该数字小于10 20。 )然后我们必须将N 2乘以5。

In summary, while the code should work for small candidates, for really large ones it could either break when you attempt to read the candidate (as an int ) or it could give the wrong answer due to integer overflow (as a long ). 总而言之,虽然代码应适用于较小的候选对象,但对于较大的候选对象而言,它可能会在尝试读取候选对象时中断(作为int ),或者由于整数溢出(如long )而可能给出错误的答案。

Fixing this requires a significant rethink. 解决此问题需要重新考虑。 (Or deeper analysis than I have done to show that the computational hazards don't result in an incorrect answer for any large N in the range of possible inputs .) (或者比我做的更深入的分析表明,对于可能的输入范围内的任何大N,计算危险都不会导致错误的答案。)

According to this link a number is Fibonacci if and only if one or both of (5*n2 + 4) or (5*n2 – 4) is a perfect square so you can basically do this check. 根据链接, 当且仅当(5 * n2 + 4)或(5 * n2 – 4)中的一个或两个为完美平方时数字才是斐波那契数,因此您基本上可以进行此检查。

Hope this helps :) 希望这可以帮助 :)

Use binary search and the Fibonacci Q-matrix for a O((log n)^2) solution per test case if you use exponentiation by squaring . 如果您通过平方运算使用幂运算,请对每个测试用例使用二进制搜索Fibonacci Q-矩阵求解 O((log n)^2)

Your solution does not work because it involves rounding floating point square roots of large numbers (potentially large enough not to even fit in a long ), which sometimes will not be exact. 您的解决方案不起作用,因为它涉及到舍入大量的浮点平方根(可能足够大,甚至无法容纳long ),有时这并不精确。

The binary search will work like this: find Q^m : if the m -th Fibonacci number is larger than yours, set right = m , if it is equal return true , else set left = m + 1 . 二进制搜索将像这样工作:find Q^m :如果第m个斐波那契数大于您的斐波那契数,则设置right = m ,如果相等,则返回true ,否则设置left = m + 1

As it was correctly said, sqrt could be rounded down. 正确地说,sqrt可以四舍五入。 So: 所以:

  • Even if you use long instead of int, it has 18 digits. 即使使用long而不是int,它也有18位数字。
  • even if you use Math.round(), not simply (int) or (long). 即使您使用Math.round(),也不能简单地使用(int)或(long)。 Notice, your function wouldn't work correctly even on small numbers because of that. 请注意,因此,即使是很小的数字,您的函数也无法正常工作。

double have 14 digits, long has 18, so you can't work with squares, you need 20 digits. 双精度数有14位数字,长整数有18位数字,因此您不能使用正方形,需要20位数字。

BigInteger and BigDecimal have no sqrt() function. BigInteger和BigDecimal没有sqrt()函数。

So, you have three ways: 因此,您有以下三种方式:

  • write your own sqrt for BigInteger. 为BigInteger编写自己的sqrt。
  • check all numbers around the found unprecise double sqrt() for being a real sqrt. 检查发现的不精确double sqrt()周围的所有数字是否为真实sqrt。 That means also working with numbers and their errors simultaneously. 这意味着还要同时处理数字及其错误。 (it's horror!) (太恐怖了!)
  • count all Fibonacci numbers under 10^10 and compare against them. 计算所有10 ^ 10以下的斐波那契数,并与之进行比较。

The last variant is by far the simplest one. 最后一种变体是迄今为止最简单的一种。

Looks like to me the for-loop doesn't make any sense ? 在我看来,for循环没有任何意义吗?

When you remove the for-loop for me the program works as advertised: 当您为我删除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");
            }


    }

}

You only need to test for a given candidate, yes? 您只需要测试给定的候选人,是吗? What is the for loop accomplishing? for循环完成什么工作? Could the results of the loop be throwing your testing program off? 循环的结果会使您的测试程序失败吗?

Also, there is a missing } in the code. 另外,代码中缺少}。 It will not run as posted without adding another } at the end, after which it runs fine for the following input: 如果未在末尾添加另一个},它将无法按发布的方式运行,此后,对于以下输入,它可以正常运行:

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

Taking into account all the above suggestions I wrote the following which passed all test cases 考虑到以上所有建议,我编写了以下通过所有测试用例的内容

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");
        }  
     }
   }
 }

I wanted to be on the safer side hence I choose 52 as the number of elements in the Fibonacci sequence under consideration. 我想更加安全,因此我选择52作为正在考虑的斐波那契数列中的元素数。

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

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