谷歌面试 扔鸡蛋_如何解决Google招聘人员关于从建筑物中扔鸡蛋的难题

谷歌面试 扔鸡蛋

by Marcin Moskala

通过Marcin Moskala

如何解决Google招聘人员关于从建筑物中扔鸡蛋的难题 (How to solve the Google recruiters’ puzzle about throwing eggs from a building)

There are a lot of great puzzles for programming job interviews. My favorite one is also known as one of the favorites among Google recruiters:

编程求职面试有很多难题。 我最喜欢的人也被称为Google招聘人员中的最爱之一:

You work in a 100 floor building and you get 2 identical eggs. You need to figure out the highest floor an egg can be dropped without breaking. Find an algorithm that is minimizing number of throws in the worst-case scenario.
您在100层高的建筑中工作,会得到2个相同的鸡蛋。 您需要找出可以不摔破鸡蛋的最高层。 找到在最坏情况下最小化抛出次数的算法。

We can make a few assumptions:

我们可以做一些假设:

  • If an egg doesn’t break when dropped from some floor, then it will not break when dropped from any lower floors.

    如果鸡蛋从某个地板掉落时没有破裂,那么从任何较低的地板掉落时也不会破裂。
  • An egg that survives a fall can be used again.

    跌落下来的鸡蛋可以再次使用。
  • A broken egg must be discarded.

    破损的鸡蛋必须丢弃。
  • The effect of a fall is the same for all eggs.

    跌倒的影响对于所有鸡蛋都是相同的。
  • If an egg breaks when dropped, then it would break if dropped from a higher floor.

    如果鸡蛋掉落时破裂,那么从较高的地板掉落时鸡蛋也会破裂。

Most people writes some algorithms to solve this puzzle (and we will do it too), but there is actually an easy solution.

大多数人都写了一些算法来解决这个难题(我们也会做到),但是实际上有一个简单的解决方案。

最简单的答案 (Simplest answer)

The simplest way to obtain the minimal floor is to throw an egg from the first floor, then from the second and so on. This way when the egg is finally broken then we will know that this is the floor. This is a reliable algorithm, but in the worst-case scenario it would take 100 throws.

获得最小楼层的最简单方法是从第一层扔鸡蛋,然后从第二层扔鸡蛋,依此类推。 这样,当鸡蛋最终破碎时,我们将知道这是地板。 这是一种可靠的算法,但是在最坏的情况下,它将需要100次抛出。

The important thing to notice is that it is the only reliable algorithm when you have only one egg. So you need to start using this algorithm when you break the first egg.

需要注意的重要一点是,只有一个鸡蛋时,它是唯一可靠的算法。 因此,当您破掉第一个蛋时,您需要开始使用此算法。

直观的答案 (Intuitive answer)

This way, our first egg should be used to split the 100 floors range into smaller ranges as efficiently as possible. Thus, an intuitive and popular answer is to throw the first egg from 1/n-th of the floors to check. For instance 1/3. Then the algorithm will look like the following:

这样,我们的第一个鸡蛋应被用来尽可能有效地将100个楼层范围分成较小的范围。 因此,一个直观且受欢迎的答案是从第1 / n楼的地板上扔出第一个鸡蛋进行检查。 例如1/3。 然后该算法将如下所示:

  • Throw the egg from 33rd floor. If it breaks, then we check the first 32 floors using the second egg.

    从33楼扔鸡蛋。 如果破裂,我们使用第二个鸡蛋检查前32层。
  • Otherwise, we throw the egg from 33 + (67 * 1/3) = 55th floor. If it breaks, then we check floors 34 to 55 using the second egg.

    否则,我们从33 +(67 * 1/3)= 55楼扔鸡蛋。 如果破裂,我们使用第二个鸡蛋检查34至55楼。

Worst case scenario for 1/3 is max(33, 24, …) = 33. This way we might find a perfect n that optimizes the number of throws using some dynamic programming. This is a valuable solution that presents programming thinking, but it is not an optimal solution.

1/3的最坏情况是max(33,24,…)=33。这样一来,我们可能会找到一个完美的n,该n使用一些动态编程来优化抛出次数。 这是呈现编程思想的有价值的解决方案,但不是最佳解决方案。

完美的解决方案 (Perfect solution)

To understand the perfect solution, we need to understand the equilibrium that is used to calculate the number of throws in the worst case scenario:

要了解完美的解决方案,我们需要了解用于计算最坏情况下的投掷次数的平衡:

Where F(n) is the next floor from which we throw the first egg

F(n)是我们从第一个鸡蛋扔出的下一层

If we introduce following variable:

如果我们引入以下变量:

then equilibrium is following:

那么均衡如下:

The optimal solution is when all arguments of this max function are equal. How do we achieve it? Looking from the end, the last D(n) is going to be 1, because we will finally get to the point where there is only the single floor for the first egg. Therefore D(n-1) should be equal to 2 because it has one less throw of the first egg.

最佳解决方案是当此max函数的所有参数都相等时。 我们如何实现它? 从最后看,最后一个D(n)将为1,因为我们最终将到达第一个鸡蛋只有一个楼层的地步。 因此D(n-1)应该等于2,因为它的第一个鸡蛋丢了一个小球。

We see then that the first egg should be thrown finally from the 99th floor, previously from 99–2=97, previously from 97–3=94, 90, 85, 79, 72, 64, 55, 45, 34, 22 and the 9th floor. This is an optimal solution! This way, we need 14 throws in the worst case scenario (the smallest difference is 13, but we had to make one extra throw on the 9th floor).

然后,我们看到应该将第一个鸡蛋最后从第99层投掷,先前是从99–2 = 97,先前是从97–3 = 94,90、85、79、72、64、55、45、34、22和9楼。 这是一个最佳解决方案! 这样,在最坏的情况下,我们需要掷14次球(最小的差是13次,但我们必须在9楼多掷1次)。

Simple equation to find the answer is following:

查找答案的简单公式如下:

Where f is number of floors. This can be simplified to:

其中f是楼层数。 可以简化为:

That is equal to:

等于:

检查一下 (Check)

OK, so we have a solution and we can calculate it without any help. It is time to check if it is correct. We will write a simple Kotlin program for that. First, let’s express how to count the number of throws for some decision. When there are 2 or fewer floors, then we need as many throws as there are floors left. Otherwise we should use the already presented equilibrium:

好的,所以我们有一个解决方案,我们可以在没有任何帮助的情况下进行计算。 现在该检查是否正确。 我们将为此编写一个简单的Kotlin程序。 首先,让我们表达一下如何计算某些决定的抛出次数。 当楼层数为2或更少时,我们需要的投掷数与剩余楼层数一样多。 否则,我们应该使用已经提出的均衡:

fun maxThrows(floorsLeft: Int, nextFloor: Int): Int =  if (floorsLeft <= 2) floorsLeft  else maxOf(nextFloor, bestMaxThrows(floorsLeft - nextFloor) + 1)

We’ve used here the bestMaxThrows function. It is a hypothetical function that returns a number of throws supposing that the next decisions are perfect. This is how we can define it:

我们在这里使用了bestMaxThrows函数。 它是一个假设函数,它假设下一个决策是完美的,因此会返回许多引发事件。 这是我们如何定义它:

fun bestMaxThrows(floorsLeft: Int): Int =  maxThrows(floorsLeft, bestNextStep(floorsLeft))

Again, we’ve just delegated the responsibility of next floor optimization to bestNextStep function. This function gives us the best next step. We can define it simply — when 2 or fewer floors are left, then we will throw an egg from the first floor. Otherwise we need to check all options and find the optimal one. Here is the implementation:

同样,我们将下层优化的职责委托给bestNextStep 功能。 此功能为我们提供了最佳的下一步。 我们可以简单地定义它-当剩下两层或更少的楼层时,我们将从第一层扔一个鸡蛋。 否则,我们需要检查所有选项并找到最佳选项。 这是实现:

val bestNextStep(floorsLeft: Int): Int =   if (floorsLeft <= 2) 1  else (1..floorsLeft)        .toList()        .minBy { maxThrows(floorsLeft, it) }!!

Note that this function uses the maxThrows function, so we deal with recurrence. It is not a problem, because when bestNextStep calls maxThrows, it always calls it with a smaller value then floorsLeft (because nextFloor is always bigger than 0). Before we use it we will add buffering to speed up the calculations:

请注意,此函数使用maxThrows函数,因此我们处理递归。 没问题,因为当bestNextStep调用maxThrows ,它总是使用一个小于floorsLeft值来调用它(因为nextFloor总是大于0)。 在使用它之前,我们将添加缓冲以加快计算速度:

val bestNextStep: (Int) -> Int = memorise { floorsLeft ->  if (floorsLeft <= 2) 1  else (1..floorsLeft)        .toList()        .minBy { maxThrows(floorsLeft, it) }!!}fun maxThrows(floorsLeft: Int, nextFloor: Int): Int =  if (floorsLeft <= 2) floorsLeft  else maxOf(nextFloor, bestMaxThrows(floorsLeft - nextFloor) + 1)val bestMaxThrows: (Int) -> Int = memorise { floorsLeft ->  maxThrows(floorsLeft, bestNextStep(floorsLeft))}fun <V, T> memorise(f: (V) -&gt; T): (V) -> T {    val map = mutableMapOf<V, T&gt;()    return { map.getOrPut(it) { f(it) } }}

First, we can check if it returns the same result as the one we have calculated:

首先,我们可以检查它是否返回与我们计算出的结果相同的结果:

fun main(args: Array<String>) {    print(bestMaxThrows(100)) // Prints: 14}

The answer is good :) Let’s check out our next steps:

答案是好的:)让我们看看我们的下一步:

fun main(args: Array<String>) {    var floor = 0    while (floor < 100) {        val floorsLeft = 100 - floor        val nextStep = bestNextStep(floorsLeft)        floor += nextStep        print("$floor, ")    }}

Result:

结果:

9, 22, 34, 45, 55, 64, 72, 79, 85, 90, 94, 97, 99, 100,

9,22,34,45,55,64,72,79,85,90,94,97,99,100,

Just how we calculated! Nice :D

我们就是这样计算的! 不错:D

图片更大 (Bigger picture)

Now we have a nice algorithm that we can use for a lot of similar problems. For example, we can change it a little to calculate the number of throws for the most probabilistic scenario. We can also check how this minimal number of throws will differ depending on the height of the building. Here is a graph answering that:

现在,我们有了一个不错的算法,可以将其用于许多类似的问题。 例如,我们可以对其进行一些更改以计算最可能出现的情况的抛出次数。 我们还可以检查最小投掷次数如何根据建筑物的高度而有所不同。 这是一个图形回答:

结论 (Conclusion)

You are now better prepared for your Google interview, but it is more important that you are now better prepared for general algorithmic thinking. This algorithm presented a nice, functional approach. A similar approach can be used for lot’s of different problems in our daily jobs.

现在,您已经为Google面试做好了更好的准备,但更重要的是,现在您已经为一般的算法思维做好了准备。 该算法提出了一种很好的功能方法。 类似的方法可以用于我们日常工作中的许多不同问题。

I hope that you liked it. Clap to say thank you and to help others find this article. More interesting materials on my Twitter. Reference me using @marcinmoskala. If you are interested in Kotlin, check out Kotlin Academy and Kotlin Academy portal for Kotlin puzzlers and advanced materials.

我希望你喜欢它。 鼓掌表示感谢,并帮助其他人找到本文。 我的Twitter上有更多有趣的资料。 使用@marcinmoskala引用我。 如果您对Kotlin感兴趣,请访问Kotlin AcademyKotlin Academy门户网站,以了解Kotlin拼图和高级材料。

翻译自: https://www.freecodecamp.org/news/how-to-solve-the-google-recruiters-puzzle-about-throwing-eggs-from-a-building-de6e7ef1755d/

谷歌面试 扔鸡蛋

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值