簡體   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