[英]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.