我正在努力解决这个任务:
设x[0]=0;x[1]=1;x[i]=x[i-2]+x[i-1]
查找单词x[n]的第k个字符,查看它是“0”还是“1”,边界为1<=k<n<=93
例如,对于序列01101101,我们有
x[0]=0
x[1]=1
x[2]=01
x[3]=101
x[4]=01101
x[5]=10101101
当我使用n=44或更高的值进行测试时,ide抛出一个outofmemoryerror java堆空间。我知道我这样做会存储序列中的第n个单词、第n-1个单词和第n-2个单词,这会占用大量内存,但我想不出更好的方法。
在对论文进行了一些草稿工作之后,我还看到,要找到n=3之后的第n个单词,while循环只需要运行n-2次,但不需要执行
我还尝试将每个单词存储在字符串arraylist中,并使用recursive进行存储,但效率更低
任何小费都很感激
这是我的密码
import java.util.Scanner;
public class BinarySequence {
public static void main(String[] args) {
Scanner read = new Scanner(System.in);
int t = read.nextInt(); //number of test to run
while (t>0){
String s0 = "0";
String s1 = "1";
int n = read.nextInt(); //nth fibonacci word
int k = read.nextInt(); // kth char of the word
System.out.println(fib(s0,s1,n-1).charAt(k-1));
t--;
}
}
private static String fib(String s0,String s1, int n) {
String ans ="";
if(n==0)
return s0;
else if(n==1)
return s1;
else {
while(n>=0){
ans = s0+s1;
s0=s1;
s1=ans;
n--;
}
return ans;
}
}
}
2条答案
按热度按时间beq87vna1#
输入
k
仅限于介于1
及92
,因此,要计算序列字符串,只需要前92个字符。但是,字符串的开头会因每个不同的原因而改变x[i]
价值前十一名x[i]
值字符串取决于的完整值x[i-1]
及x[i-2]
,但在11号之后x[i]
值的第一个字符串x[i-2]
已足够长,因此x[i-1]
不再重要了,因为它在结果的末尾被连接起来。价值x[i-1]
及x[i-2]
对于较大的指数,可如下所示:假设
111...111
/222...222
部分(当然不是实际的字符)有92个字符长,那么你就不需要剩下的东西了xx...
及yyyyy...
在那之后,你再也无法用有限的时间接触到他们了k
不管怎样,都要珍惜。所以对于你的问题,这个序列与
足够高
i
.现在剩下的问题是计算/选择
111..111
或222...222
应该在计算以下内容时使用x[24]
甚至x[80]
. 最有可能是一个奇数/偶数的检查,在这里你会写下:“什么时候?”n
是平的,用x[10]
,否则使用x[11]
.".⑨)检查是否存在任何“一个关闭”错误,92个字符的阈值可能不在索引中
11
.mmvthczy2#
有一种解决方案适用于任何情况
k
那是一个int
并且不包含昂贵的串联操作,具有O(1)
记忆与O(log(k))
时间1。前缀和奇偶校验
此算法使用@progman在其答案中的观察结果,即如果
a < b
及a
及b
那么,你有相同的平价吗x[a]
是的前缀x[b]
(这是从以下事实归纳得出的结论:x[n-2]
是的前缀x[n]
). 这意味着我们不需要计算n
序列中的项目,我们只需要找到j
,我将其定义为x[j]
大于k
,及j
具有与相同的奇偶性n
.例如,如果
n = 12345
及k = 1
,那么我们只需要计算x[3] = 101
因为我们知道x[3]
是的前缀x[12345]
作为3
及12345
他们俩都很奇怪。所以答案是0
.进入o(1)内存
用于避免存储长的0和1序列的方法如下:
不需要计算x
首先注意单词的长度
x[n]
等于fib[n]
哪里fib
是斐波那契序列。因此,与其计算中的字符串x
和索引到x[n]
看看是否返回1
或0
,该方法使用以下事实:x[n] = x[n-2] + x[n-1]
. 你可以算出x[n][k]
是x[n-2]
或x[n-1]
通过比较k
长到x[n-2]
(其中x[n-2]
是fib[n-2]
). 经过比较,我们知道x[n][k]
等于x[n-2][k]
或x[n-1][k-fib[n-2]]
. 然后,我们重复这个过程n
着手n-1
或n-2
视情况而定,以及k
保持不变或设置为k-fib[n-2]
视情况而定。重复此操作直到n == 0
或n == 1
,在哪一点上k
将0
所以x[n][k]
要么等于x[0][0] = 0
或x[1][0] = 1
,顾名思义。不需要储存谎言
x
仅在计算中不需要fib
,这避免了存储长的数字序列,但是我们肯定需要存储所有的斐波那契数,直到fib[j]
为了执行上一段中定义的步骤?不,我们没有!这是因为我们首先发现j
,只保留fib[i-1]
及fib[i]
在记忆中。然后我们重新排列方程,找到fib[n-2] = fib[n] - fib[n-1]
,并使用它回溯斐波那契序列以查找x[n][k]
.实施
我已经解释了算法,下面是一个java实现:
首先,我们定义一个
Fib
类封装斐波那契序列,保持代码整洁(如果需要保留一个文件,可以将其移动到内部类。)然后,实际算法:
回家还是回家
这个
O(log(k))
时间复杂性意味着它运行得非常快,即使是非常大的时间k
价值观如果你想要k
大于Integer.MAX_VALUE
(相当于n
大于45
)你可以改变k
到long
但在计算斐波那契数时,这可能会导致溢出错误,因此需要将一些变量更改为BigInteger
s、 虽然这会稍微增加时间和空间的复杂性。1斐波那契序列具有指数下界,因此
x[n]
大于(3/2)**n
,意思是O(log(k))
需要计算斐波那契数才能找到一个大于k
. 然后,第二阶段执行相同数量的“回溯”操作以返回到目标x[0]
或x[1]
,这是额外的费用O(log(k))
时间,导致O(log(k))
总计