[算法] 从一个 Google 面试题想到的
2007-01-05 13:45
302 查看
前一段时间看到一道 Google 的面试题在各大论坛被炒得很火,题目如下:“有一个100层高的大厦,你手中有两个相同的玻璃围棋子。从这个大厦的某一层扔下围棋子就会碎,用你手中的这两个玻璃围棋子,找出一个最优的策略,来得知那个临界层面。”题目虽然看起来简单,但是仔细想想,此题中蕴含的算法道理以及实用价值还是很值得好好研究一下。石头在网上也看到了不少热心朋友的解法(CSDN、ChinaUnix),看过之后感觉还是挺有启发的,于是总结一下,主要的算法有以下几种:
<1> 等分段求最小值:这种算法先假设把大楼分成等高的 x 段,这样在最差的情况下,要确定临界段,我们需要投掷 100/x-1 次,确定了临界段之后要确定临界层,我们需要再投掷 x-1 次。这样,问题就成了求函数 f(x)=(100/x-1)+(x-1) 的最小值问题。由于 f(x) 存在最小值且只有一个驻点,所以当 x=10 时 f(x) 取得最小值,最小值为18。
<2> 假设投掷次数是均匀分布的,那么为了使最坏情况的投掷数最小,我们希望无论临界段在哪里,总的投掷数都不变(也就是说将投掷数均匀分布)。这样我们就可以假设第一次投掷的层数是 f,转化成数学模型,可以得到如下方程式 f+(f-1)+...+2+1>=99,即 f(f+1)/2>=99 的最小整数解,解出结果等于14。程序算法如下:
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
// Equation.cpp
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
#include <stdio.h>
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
#include <iostream>
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
using namespace std;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockStart.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedBlock.gif)
int f (int i) ...{
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
int r;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
int s = 0;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedSubBlock.gif)
while (i >= 1) ...{
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
s = s + i;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
i--;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
if (s >= 99) return 1;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
else return 0;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockEnd.gif)
}
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockStart.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedBlock.gif)
int main () ...{
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
int m;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
int n;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedSubBlock.gif)
for (m = 1; m <= 100; m++) ...{
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedSubBlock.gif)
if (f(m) == 1) ...{
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
n = m; break;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
printf("The result is : %dn", n);
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
system("pause");
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockEnd.gif)
}
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
按结果分析看来,方法一的最小值的确比较小(10)但是问题是最大值无法确定(比如假设临界层在第99层则需要仍19下);而方法二的算法好在能得出一个固定的临界层值,这样便于一些问题的处理。总的来说,石头认为两种方法各有所长,虽然方法二看起来的确更接近出题者的本意,但是如果将棋子本身破碎的概率也考虑进去就不一定了(当然,一般来说层数越高破碎的概率应该越大,但是我们试想一下如果假设棋子破碎的几率是和层数成反比,那么使用方法一是否会有更好的效果呢?)。然而不管出题者的意图是什么,我觉得这个题目所引出的数学模型还是很有实用意义的,特别在一些数据挖掘应用中。我猜想这些算法是不是与 Google 数据库的技术内幕有什么联系呢 ... 前几天和一个业内的前辈谈起下一代互联网的技术趋势,说到了所谓的“算法时代”的话题,看来关注一些有趣的算法也不错呢 ... 不知不觉时间又晚了,还是先休息吧 :)
<1> 等分段求最小值:这种算法先假设把大楼分成等高的 x 段,这样在最差的情况下,要确定临界段,我们需要投掷 100/x-1 次,确定了临界段之后要确定临界层,我们需要再投掷 x-1 次。这样,问题就成了求函数 f(x)=(100/x-1)+(x-1) 的最小值问题。由于 f(x) 存在最小值且只有一个驻点,所以当 x=10 时 f(x) 取得最小值,最小值为18。
<2> 假设投掷次数是均匀分布的,那么为了使最坏情况的投掷数最小,我们希望无论临界段在哪里,总的投掷数都不变(也就是说将投掷数均匀分布)。这样我们就可以假设第一次投掷的层数是 f,转化成数学模型,可以得到如下方程式 f+(f-1)+...+2+1>=99,即 f(f+1)/2>=99 的最小整数解,解出结果等于14。程序算法如下:
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
// Equation.cpp
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
#include <stdio.h>
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
#include <iostream>
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
using namespace std;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockStart.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedBlock.gif)
int f (int i) ...{
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
int r;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
int s = 0;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedSubBlock.gif)
while (i >= 1) ...{
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
s = s + i;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
i--;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
if (s >= 99) return 1;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
else return 0;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockEnd.gif)
}
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockStart.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedBlock.gif)
int main () ...{
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
int m;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
int n;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedSubBlock.gif)
for (m = 1; m <= 100; m++) ...{
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedSubBlock.gif)
if (f(m) == 1) ...{
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
n = m; break;
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
printf("The result is : %dn", n);
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif)
system("pause");
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockEnd.gif)
}
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
![](http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif)
按结果分析看来,方法一的最小值的确比较小(10)但是问题是最大值无法确定(比如假设临界层在第99层则需要仍19下);而方法二的算法好在能得出一个固定的临界层值,这样便于一些问题的处理。总的来说,石头认为两种方法各有所长,虽然方法二看起来的确更接近出题者的本意,但是如果将棋子本身破碎的概率也考虑进去就不一定了(当然,一般来说层数越高破碎的概率应该越大,但是我们试想一下如果假设棋子破碎的几率是和层数成反比,那么使用方法一是否会有更好的效果呢?)。然而不管出题者的意图是什么,我觉得这个题目所引出的数学模型还是很有实用意义的,特别在一些数据挖掘应用中。我猜想这些算法是不是与 Google 数据库的技术内幕有什么联系呢 ... 前几天和一个业内的前辈谈起下一代互联网的技术趋势,说到了所谓的“算法时代”的话题,看来关注一些有趣的算法也不错呢 ... 不知不觉时间又晚了,还是先休息吧 :)
相关文章推荐
- google面试题及我的算法(1)——交叉换位
- Shell在大数据时代的魅力:从一道百度大数据面试题想到的点滴
- 每日一道算法题:Google面试题:判断一个自然数是否是某个数的平方
- 每日一道算法题:Google面试题:给定能随机生成整数1到5的函数,写出能随机生成整数1到7的函数
- 经典面试题(一)附答案 算法+数据结构+代码 微软Microsoft、谷歌Google、百度、腾讯
- google面试题及我的算法(1)——交叉换位(改进)
- 【白话经典算法系列之十一】一道有趣的GOOGLE面试题 --【解法2】
- 经典面试题(四)附答案 算法+数据结构+代码 微软Microsoft、谷歌Google、百度、腾讯
- 一道面试题:从一个字符串中找出第一个不重复字符;算法一;
- 白话经典算法系列之十 一道有趣的GOOGLE面试题
- 经典面试题(一)附答案 算法+数据结构+代码 微软Microsoft、谷歌Google、百度、腾讯
- lintcode&九章算法——Google面试题:原子计数
- google面试题及我的算法(1)——交叉换位(改进)
- Shell在大数据时代的魅力:从一道百度大数据面试题想到的点滴
- 【算法】一道有趣的GOOGLE面试题
- 一道面试题:从一个字符串中找出第一个不重复字符;算法二;
- 经典面试题(二)附答案 算法+数据结构+代码 微软Microsoft、谷歌Google、百度、腾讯
- 经典面试题(四)附答案 算法+数据结构+代码 微软Microsoft、谷歌Google、百度、腾讯
- 白话经典算法系列之十 一道有趣的GOOGLE面试题 .
- 白话经典算法系列之十一 一道有趣的GOOGLE面试题 --【解法2】