关于三星面试题中的一点很有趣的现象,谁给证明一下

suiyili 2009-02-26 08:16:53
原题如下:已知:
a[0] = 0;
a[1] = 1;
...
...
a[2*i] = a[i];
a[2*i+1] = a[i] + a[i+1];

问题:
给定任意一个i(0 <i <2000000)
求a[0]到a[i]之间的最大值,以及其下标。

我的解法:
遍历 a[p]到a[i]找最大值即可
其中 p为2^n (其中,2^(n+1)为小于i的最接近2的幂的数)
例如: p=69;则n=5,遍历a[32] 到 a[69]即可!


观察一下解的状况

2:1
3:2
4:1
5:3
6:2
7:3
8:1
9:4
10:3
11:5
12:2
13:5
14:3
15:4
16:1
17:5
18:4
19:7
20:3
21:8
22:5
23:7
24:2
25:7
26:5
27:8
28:3
。。。

让我想起了帕斯卡三角形,但是不完全一样,构建如下,每2^n 为一层,第一层为0-2,第二层为2-4,第三层4-8,8-16, 16-32...

------------------1 2 1
----------------1 3 2 3 1
---------------1 4 3 5 2 5 3 4 1
-。。。

应用帕斯卡三角形中的原则,每项等于头顶上两项相加。不同的是,每层元素都落到下一层。也可以先把某层落到下一层,然后相邻元素相加插在落下的元素之间。

好了,怎样确定每一层中最大的元素的下标呢?例如,8-16(图中第三层)边界是 8,16 old = 8,new = 16,然后找到中点 (8+16)/2 = 12 令 new = 12 (old = 8 or old = 16,因为每层元素是对称的) ,然后, (8 + 12)/2 = 10 (new = 10, old = 12), 再(12 + 10)/2 = 11 (new = 11,old =10),但是 new(11) 和 old(10) 之间没有数字,所以,下标为11的元素为最大值。

更有趣的现象是,已知下标,如果我们按照题目给定的公式去求值,可能很慢,观察每行中最大值的规律。
第0行算是1,第一行是(0-2) 最大值2,第二行(2-4) 3,第三行(4-8)最大值就是5,第四行(8-16) 8,第五行(16-32) 13...
哈,老朋友,费波那契。

现在我要问的是,那位朋友能够给出比较完整的数学推理,因为我只是观察而已。还有一些细节的工作,例如,给定的i 不是2的幂,怎么办,那要看最大的2的幂与i之间是否包含拥有最大值的那个下标,如果不包含怎么办?我这里抛砖引玉,谁给个比较完整的方案。

...全文
851 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhoujiamurong 2009-06-26
  • 打赏
  • 举报
回复
思想不错
adfas 2009-04-24
  • 打赏
  • 举报
回复
mark
knowcraft 2009-04-23
  • 打赏
  • 举报
回复
break应该在j--后面,改了
C/C++ code
#include <iostream>
using namespace std;
int main()
{
int a[100]={2},b[100]={1,2,3},j,k;
cout < <"Enter the number:" < <endl;
cin >>k;
for(int i=1;i <=100;i++)
{
if(i%2==1)
{a[i]=2*a[i-1]+1;
}
else{a[i]=2*a[i-1]-1;}
}
for(j=0;;j++)
{
if(a[j]>k)
{
j--;
break;
}
}
for(int m=3;m <=j;m++)
{
if(b[m-2]>b[m-3])b[m]=b[m-1]+b[m-2];
else b[m]=b[m-1]+b[m-3];
}
cout < <"The first biggest number is No." < <a[j] < <endl;
cout < <"The first biggest number is" < <b[j] < <endl;
return 0;
}
knowcraft 2009-04-23
  • 打赏
  • 举报
回复
我刚在另外一个帖子里说了一下自己的想法,不妨转来
#include<iostream>
using namespace std;
int main()
{
int a[100]={2},b[100]={1,2,3},j,k;
cout <<"Enter the number:"<<endl;
cin >>k;
for(int i=1;i<=100;i++)
{
if(i%2==1)
{a[i]=2*a[i-1]+1;
}
else{a[i]=2*a[i-1]-1;}
}
for(j=0;;j++)
{
if(a[j]>k)
{break;
j--;
}
}
for(int m=3;m<=j;m++)
{
if(b[m-2]>b[m-3])b[m]=b[m-1]+b[m-2];
else b[m]=b[m-1]+b[m-3];
}
cout <<"The first biggest number is No." <<a[j] <<endl;
cout <<"The first biggest number is" <<b[j] <<endl;
return 0;
}

没怎么看前面帖子,感觉哥几个想复杂了。
说明一下,把0到i这个区间分成0,2的n次方到2的n+1次方的各区间,n为0,1,2,3...
那么显然每一段都有各自的最大值,而各段最大值递增。因为任意一个奇数位值等于相邻两位偶数位值之和,相邻两位偶数位下标除以2,就落在前一个段中,也就是说本段最大值即为前段任意相邻两数和最大值,接下来就好办了。假使我们知道某段最大值,肯定下标为奇数,可认为是相邻下标偶数位数之和,比较此两数,找到较大者,那么显然下段最大值下标为本段最大值下标乘以2,加1或减1,偶数位两位数如果较大者下标大,那么+1,否则-1。
每段最大值不唯一,我找的是每段中首次出现,比如2-8段最大下标为5,8-16段最大下标为9,16-32段19,32-64段37,64-128段75,正好5*2-1,9*2+1,19*2-1,37*2+1。
当然也有可能我的方法有缺陷,就是比如说,某一段最大5,3+2=5,下一段为5+3=8,但是否有可能1+4=5,5+4=9呢?没去确切证明,初步想想,觉得不大可能,呵呵。如果错了,谢谢指正的朋友。
sefule 2009-04-23
  • 打赏
  • 举报
回复
估计这题也就是讨论一下解法,实际应用只要求出一定范围内的解,做成表供查询,那就是顶级效率啊.

1 3 5 9 11 19 21 35 37 43 69 73 75 83 85 139 147 149 165 171 277 293 299 331 339 341 555 587 595 597 661 683 1109 1173 1189 1195 1323 1355 1363 1365 2219 2347 2379 2387 2389 2645 2709 2731 4437 4691 4693 4757 4779 5291 5419 5451 5459 5461 8875 9387 9515 9547 9555 9557 10581 10837 10923 17749 18771 18773 19029 19093 19115 21163 21675 21803 21835 21843 21845 35499 37547 38059 38187 38219 38227 38229 42325 43349 43605 43691 70997 75091 75093 76117 76373 76459 84651 86699 87211 87339 87371 87379 87381 141995 150187 152235 152747 152875 152907 152915 152917 169301 173397 174421 174763 283989 300371 300373 304469 305493 305749 305835 338603 346795 348843 349355 349483 349515 349523 349525 567979 600747 608939 610987 611499 611627 611659 611667 611669 677205 693589 697685 698709 699051
绿色夹克衫 2009-02-27
  • 打赏
  • 举报
回复
大于32应该都是1000开头后面跟若干个10,如果是奇数最后就是101,如果是偶数,最后就是11
以若干个10(1010...)作为结尾,如果是偶数+1.


[Quote=引用 10 楼 suiyili 的回复:]
另一个很有趣的现象,以类帕斯卡三角中的第3层为例: 1 4 3 5 2 5 3 4 1 (元素8-16),当你按照我上面这段文字找最大值索引的过程,

‘怎样确定每一层中最大的元素的下标呢?例如,8-16(图中第三层)边界是 8,16 old = 8,new = 16,然后找到中点 (8+16)/2 = 12 令 new = 12 (old = 8 or old = 16,因为每层元素是对称的) ,然后, (8 + 12)/2 = 10 (new = 10, old = 12), 再(12 + 10)/2 = 11 (new = 11,old =10),但是 new(…
[/Quote]
suiyili 2009-02-27
  • 打赏
  • 举报
回复
另一个很有趣的现象,以类帕斯卡三角中的第3层为例: 1 4 3 5 2 5 3 4 1 (元素8-16),当你按照我上面这段文字找最大值索引的过程,

‘怎样确定每一层中最大的元素的下标呢?例如,8-16(图中第三层)边界是 8,16 old = 8,new = 16,然后找到中点 (8+16)/2 = 12 令 new = 12 (old = 8 or old = 16,因为每层元素是对称的) ,然后, (8 + 12)/2 = 10 (new = 10, old = 12), 再(12 + 10)/2 = 11 (new = 11,old =10),但是 new(11) 和 old(10) 之间没有数字,所以,下标为11的元素为最大值。’

你会发现 a[12] = a[8] + a[16]=1+1=2, a[10] = a[8] + a[12]=1+2=3 , a[11] = a[10] + a[12]= 3+2 =5, 再向下一层 16-32 那一组也是一样,整个就是一个费波那契数列。

利用这种方法,我们或许可以求得当给定i (例如,i = 25, 距离i 最近的2的幂,比25小的是16,比25大的那个是32)这种情况下我们根据主帖中的方法很容易确定0-16之间的最大值和他的索引(其实是8-16)设为a[k]。那么我们要做的就是确定是否在 16-25 之间存在一个值a[m],以至于a[m]>a[k]。
其实也不难,我们扩展一下上面说的中点法,任何中点的值都是两边值相加,那么我们就可以确定任何一个16-25 之间的值了,找到最大的a[m],然后和那个a[k]比较一下,。。。
iwantnon 2009-02-27
  • 打赏
  • 举报
回复
将a[0],a[1],a[2],...排成下面三角形:

第0行--------------------------------------0
第1行--------------------------------------1
第2行--------------------------------------1
第3行--------------------------------------2 1
第4行------------------------------------3 2 3 1
第5行--------------------------------4 3 5 2 5 3 4 1
第6行------------------------5 4 7 3 8 5 7 2 7 5 8 3 7 4 5 1
第7行---6 5 9 4 11 7 10 3 11 8 13 5 12 7 9 2 9 7 12 5 13 8 11 3 10 7 11 4 9 5 6 1

则第i行(i>=3)中最大数为a[(2^i+(-1)^(i+1))/3]。
iwantnon 2009-02-27
  • 打赏
  • 举报
回复
恩,不对。
只能写出每层最大元素的数学表达式。
iwantnon 2009-02-27
  • 打赏
  • 举报
回复
[Quote=引用楼主 suiyili 的帖子:]
原题如下:已知:
a[0] = 0;
a[1] = 1;
...
...
a[2*i] = a[i];
a[2*i+1] = a[i] + a[i+1];

问题:
给定任意一个i(0 <i <2000000)
求a[0]到a[i]之间的最大值,以及其下标。
[/Quote]
刚才上课的时大概看了一下,设a[0]~a[i]间最大值下标为b[i],那么完全可以求出b[i]的数学表达式吧。
xyzlcyk 2009-02-27
  • 打赏
  • 举报
回复
看了很长时间懂了 很好 哈哈
绿色夹克衫 2009-02-27
  • 打赏
  • 举报
回复
产生菲波那切的原因也很简单,设M[1]....M[n]分别对应2^n - (2^(n+1) - 1)之间的最大值

那么A[M[n]] = A[M[n]/2] + A[M[n]/2 + 1](因为n为奇数)

A[M[n]/2]与A[M[n]/2 + 1]中有一个偶数,应该是A[M[n]/2],所以A[M[n]/2] = A[M[n]/4]

实际上M[n-1]就是M[n]/2 + 1,而M[n-2]就是M[n]/4,因此就形成了菲波那切数列。

[Quote=引用 8 楼 suiyili 的回复:]
引用 7 楼 sxbwelcome 的回复:
这个问题和斐波那契数列没有关系
你只是通过看行的最大数,但实际问题不是问正行的,而是一个随机的

由于a[2*i+1]= a[2*i]+a[2*i+2]
所以可以断定下标为偶数的肯定不是最大数,可以排除,这样搜索范围可以减少一半;
而a[2*i+1] = a[i] + a[i+1];后两个数中必然有一个下标为奇数,所以又可以排除下标为奇数的前半段


你可以试着再观察几项结果,每2^n层就是以费氏数列为最大项的。…
[/Quote]
iwantnon 2009-02-27
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 iwantnon 的回复:]
用max(U)表示集合U的最大
[/Quote]
此句应为:“用max(U)表示集合U的最大元素”。
iwantnon 2009-02-27
  • 打赏
  • 举报
回复
把楼主的三角形:
---------------------1
-------------------1 2 1
-----------------1 3 2 3 1
-------------1 4 3 5 2 5 3 4 1
只看右半边:
1
2 1
2 3 1
2 5 3 4 1
2 7 5 8 3 7 4 5 1
2 9 7 12 5 13 8 11 3 10 7 11 4 9 5 6 1
2 11 9 16 7 19 12 17 5 18 13 21 8 19 11 14 3 13 10 17 7 18 11 15 4 13 9 14 5 11 6 7 1
用max(U)表示集合U的最大
用C(i)表示第i行数字所成集合。
用c(i,j)表示第i行第j个元素。
用A(i)表示第i行数中直接由第i-1行滑落下来者所成集合。
用B(i)表示第i行数中由第i-1行相邻两数之和得到者所成集合。
有下面几条结论:
(1),A(i)=C(i-1)。(由A(i)定义即得)。
(2),C(i)=A(i)∪B(i)。
(3),max(C(i))=max(B(i))。
(4),max(B(i+1))=max(B(i))+max(A(i))。
利用这些就可以直接得到波斐那契数列了:
max(C(i+1))
=max(B(i+1)) (据(3))
=max(B(i))+max(A(i)) (据(4))
=max(C(i))+max(C(i-1)) (据(3),(1))
这就是楼主所看到的波斐那契数列。
绿色夹克衫 2009-02-26
  • 打赏
  • 举报
回复
其实解释起来挺复杂,但是并不太难,LZ可以把最大编号转为2进制看一下,就明白了!

2^17 - 2^18
100010101010101
......
101010101010101

这样的数才会出现最大,LZ递归算一下值就明白了,每一步递归计算中,

A[2*i + 1] = A[i+1](奇数) + A[i/2](奇数)

所以A[2*i + 1]才大的。

因此我才说那道题有log(n)的解法,而不是我给出的log(n)*log(n)

实际上可以用构造法来做!
suiyili 2009-02-26
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 sxbwelcome 的回复:]
这个问题和斐波那契数列没有关系
你只是通过看行的最大数,但实际问题不是问正行的,而是一个随机的

由于a[2*i+1]= a[2*i]+a[2*i+2]
所以可以断定下标为偶数的肯定不是最大数,可以排除,这样搜索范围可以减少一半;
而a[2*i+1] = a[i] + a[i+1];后两个数中必然有一个下标为奇数,所以又可以排除下标为奇数的前半段
[/Quote]

你可以试着再观察几项结果,每2^n层就是以费氏数列为最大项的。而且不存在反例。
设某层A,A层最大元素A(k),A层每个元素都落到下一层(B层),而且B层的最大数也必然在产生在A(k)旁边,再下一层最大值必然是A层最大元素加上B层最大元素,过程一如费氏数列!
而如果按照'所以可以断定下标为偶数的肯定不是最大数,可以排除,这样搜索范围可以减少一半',然后 ‘而a[2*i+1] = a[i] + a[i+1];后两个数中必然有一个下标为奇数,所以又可以排除下标为奇数的前半段’的原则来搜索,得到的是一个范围,而不是一个具体的数组索引,而最终对那些剩下的,下标为奇数的后半段搜索还是要根据公式回朔计算前面的值。

而如果根据类帕斯卡三角方案,就可以一次确定最大值索引,而不是一个范围,在如何知道这个索引所指向的最大值问题上,如果利用费氏数列,那么就不用再使用公式回朔。
所以,如果能证明此方案的严谨可靠,那么此方案应该是最快方案。
sxbwelcome 2009-02-26
  • 打赏
  • 举报
回复
这个问题和斐波那契数列没有关系
你只是通过看行的最大数,但实际问题不是问正行的,而是一个随机的

由于a[2*i+1]= a[2*i]+a[2*i+2]
所以可以断定下标为偶数的肯定不是最大数,可以排除,这样搜索范围可以减少一半;
而a[2*i+1] = a[i] + a[i+1];后两个数中必然有一个下标为奇数,所以又可以排除下标为奇数的前半段


suiyili 2009-02-26
  • 打赏
  • 举报
回复
这是所得到的数组的索引和它的值,a[2]= 1, a[3]=2,a[4]=1,a[5]=3...
iwantnon 2009-02-26
  • 打赏
  • 举报
回复
[Quote=引用楼主 suiyili 的帖子:]
观察一下解的状况

2:1
3:2
4:1
5:3
6:2
7:3
8:1
9:4
10:3
11:5
12:2
13:5
14:3
15:4
16:1
17:5
18:4
19:7
20:3
21:8
22:5
23:7
24:2
25:7
26:5
27:8
28:3
。。。
[/Quote]
没看明白这段是啥意思
suiyili 2009-02-26
  • 打赏
  • 举报
回复
顶楼上,不错。那么,费波那契数列怎么解释?
加载更多回复(3)

33,008

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧