[英]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可以四舍五入。 所以:
雙精度數有14位數字,長整數有18位數字,因此您不能使用正方形,需要20位數字。
BigInteger和BigDecimal沒有sqrt()函數。
因此,您有以下三種方式:
最后一種變體是迄今為止最簡單的一種。
在我看來,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.