Вы находитесь на странице: 1из 11

Algorithmic Analysis and

Optimization of the Two Egg Problem


Stuart J. Lumby B.C.Sc
July 16, 2019

Description
A recent interesting problem in computer science involves determining which floor in a high rise building an egg
will break if dropped. The problem states that only two eggs are allowed for the test and that there is a lowest floor
at which the egg will break if dropped. The egg will break for all floors above this floor, but will not break for all
floors below. The solution is to find the lowest floor at which the egg breaks using the least number of attempts.

1. Analysis of Problem
On first site, an optimal algorithm that uses the least number of attempts would appear to be some variation of a
binary search method. With this approach, the first egg would be dropped from the middle floor of the building, and
if it does not break then the egg would be dropped on the floor half-way from the middle to the top (or at about ¾ of
the way up). If it still does not break than we keep halving the distance to the top until it does break, at which point
we would need to use the second egg and make attempts one floor at a time from the last floor that the first egg did
not break to the last one where it did break. The first floor at which the second egg does break would then be the
lowest floor. This approach is pseudo-coded below in Figure 1.0:

function binarySearch(floor[])
bottom := 1
top := floor.number
while bottom < top
middle := lower_bound((top-bottom)/2)+top
if floor[middle] = EGG_BREAKS
for i from bottom to middle
if floor[i] = EGG_BREAKS
return i
else
bottom := middle+1
return not found

Figure 1.0

In the worst case scenario, however, if the lowest floor at which the egg breaks is the 50 th floor of a 100 story
building, this variation on a binary search would take 50 attempts.

A better heuristic would be to use a stepped incremental search approach. With this approach, starting from a given
small incremented number of floors from the first floor, we drop the first egg. If the egg breaks, then using the
second egg we only have a small incremented number of floors to test from the first floor, one floor at a time, to
determine the lowest floor where the egg breaks. If the egg does not break, then we continually go up the same
incremented number of floors until the first egg does break. At this point, we use the second egg to test from the
previously tested floor that is at most an incremented number of floors below the current floor, again one floor at a
time, to determine the lowest floor where the egg breaks. This approach is pseudo-coded below in Figure 2.0:

1
function stepSearch(floor[], increment)
test := increment
top := floor.number
while test < top
if floor[test] = EGG_BREAKS
for i from test-(increment-1) to test
if floor[i] = EGG_BREAKS
return i
else
test := test+increment
if test > top
test = top
return not found

Figure 2.0

As an example, using a stepped increment of 10 floors in a 100 story building, the worst case scenario would occur
if the lowest floor at which the egg breaks is from the 99th floor. In this case, we would search the 10th, 20th, 30th, up
to the 100th floor followed by the 91st, 92nd, 93rd, up to the 99th floor at which point a total of 19 attempts would have
been made. Hence, the stepped incremental search shows a significant improvement of taking only 19 attempts over
the varied binary search, which would take 50 attempts in a worst case scenario.

2. Mathematical Analysis and Optimization


We can generalize the two egg problem and optimize its solution by generalizing the total number of floors as 𝒏 and
considering a varied increment 𝒊 to use in the stepped incremental search. This variable increment 𝒊 is what is to be
optimized for in the following function, which determines the total number of attempts in the worst case scenario
based on the increment chosen:
𝒏
𝛉(𝒊) = + ( 𝒊 − 𝟏) (1)
𝒊

To determine the minimum value of the function, we use the Calculus First Derivative Test that first involves taking
the derivative of the function 𝛉 with respect to 𝒊 as follows:

𝛛 𝒏
𝛉(𝒊) = 𝟏 − (2)
𝛛𝒊 𝒊𝟐

The critical points of 𝛉 are determined when the derivative of this function is zero. From (2),

𝒏
𝟎 = 𝟏− (3)
𝒊𝟐

𝒊𝟐 = 𝒏 (4)

𝒊 = ±√𝒏 (5)

Since the number of floors 𝒏 and the increment 𝒊 must be positive and can typically be considered to be greater than
one for all practical purposes, it follows that

𝟏 < √𝒏 < 𝒏 (6)

2
Based on (6) and the derivative of the function 𝜽 in (2):

𝛛
𝜽(𝟏) = 𝟏 − n < 𝟎 (7)
𝛛𝒊

𝛛 𝟏
𝜽(𝒏) = 𝟏 − > 𝟎 (8)
𝛛𝒊 𝒏

Thus, from the First Derivative Test, it follows from (7) that the function 𝜽 is decreasing on the interval [𝟏, √𝐧) and
from (8) that the function 𝜽 is increasing on the interval (√𝒏, ∞) so that for the stepped increment search the
minimum number of attempts will occur when the increment is given by the square root of the number of floors
specified for the problem:

𝒊 = √𝒏 (9)

If (9) is taken as the value of the increment in the stepped increment search, it follows that the number of attempts in
the worst case scenario for the two egg problem is given by the following based on (1) and (9):

𝒏
𝛉(𝒏) = + √𝒏 − 𝟏 (10)
√𝒏

𝛉(𝒏) = 𝟐√𝒏 − 𝟏 (11)

In terms of order in Big “O” notation, the order for the stepped increment search using (9) for the increment then
becomes:

𝜣(√𝒏)
This order has significantly higher performance in comparison to the initially proposed variation of a binary search
to solve the two egg problem. In fact, the variation of binary search has a worst case scenario of 𝒏/𝟐 , and
consequently has an order 𝒏, which has significantly lower performance than the stepped increment search:

𝜣(𝒏)
Figure 3.0 illustrates graphs for various orders (including the orders √𝒏 and 𝒏 for the different algorithms discussed
here) that map the size of the data set 𝒏 against the number of operations 𝑵 performed. This corresponds to the
number of floors against the number of attempts required to solve the two egg problem. The significant difference
between how the graphs of the orders √𝒏 and 𝒏 increase with the data set size clearly shows the difference in
potential performance between the two different algorithms considered to solve the two egg problem.

3
Figure 3.0

3. Experimental Results and Conclusion


Appendix I contains a C program that uses a stepped incremental search algorithm for the two egg problem. It tests
and outputs the average number of attempts for a series of floor numbers from 100 to 500 and a series of various
increments for each floor number. Interestingly, the results of the program found in Appendix II show that if the
total number of floors are 100, 200, 300, 400, and 500, then the minimum number of attempts at solving the two egg
problem occur with stepped increments of √100 = 10, √200 ≈ 14.142, √300 ≈ 17.321, √400 = 20, and √500 ≈
22.360, respectively. These results agree nearly exactly with the analysis. The graphs in Figures 4.0 to 8.0 plot the
results of running the program.

4
Two Egg Problem for 100 Floors
14.00000

12.00000

10.00000
Avergae Attempts

8.00000

6.00000

4.00000

2.00000

0.00000
0 2 4 6 8 10 12 14 16
Step Increment Size

Figure 4.0

Two Egg Problem for 200 Floors


15.30000
15.20000
15.10000
15.00000
Average Attempts

14.90000
14.80000
14.70000
14.60000
14.50000
14.40000
14.30000
14.20000
0 5 10 15 20 25
Step Increment Size

Figure 5.0

5
Two Egg Problem for 300 Floors
18.80000

18.60000

18.40000
Average Attempts

18.20000

18.00000

17.80000

17.60000

17.40000
0 5 10 15 20 25
Step Increment Size

Figure 6.0

Two Egg Problem for 400 Floors


21.20000

21.00000
Average Attempts

20.80000

20.60000

20.40000

20.20000

20.00000
0 5 10 15 20 25 30
Step Increment Size

Figure 7.0

6
Two Egg Problem for 500 Floors
23.10000

23.00000

22.90000
Average Attempts

22.80000

22.70000

22.60000

22.50000

22.40000
0 5 10 15 20 25 30
Step Increment Size

Figure 8.0

7
Appendix I

////////////////////////////////////////////////////////////////////////////////////////////////////
// Test to Determine the Optimal Solution to the Two Egg Problem //
////////////////////////////////////////////////////////////////////////////////////////////////////

//
// Standard C Header Files
//
#include <stdio.h>
#include <windows.h>
#include <time.h>
#include <math.h>

////////////////////////////////////////////////////////////////////////////////////////////////////
// Testing Structure and Function //
////////////////////////////////////////////////////////////////////////////////////////////////////
struct TestType
{
double TimeTaken;
unsigned long Operations;
unsigned long Solution;
bool Successful;
};

struct TestType StepSearch(bool Floor[], unsigned long FloorCount, unsigned long Increment)
{
TestType StepSearchTest;

bool TestSuccessful;
bool TestCompleted;

unsigned long Test;


unsigned long Top;
unsigned long OperationCount;

LARGE_INTEGER ClockTicksPerSecond;
LARGE_INTEGER ClockTicksStart;
LARGE_INTEGER ClockTicksEnd;

QueryPerformanceFrequency(&ClockTicksPerSecond);
QueryPerformanceCounter (&ClockTicksStart);

TestSuccessful = false;
TestCompleted = false;
Test = Increment - 1;
Top = FloorCount - 1;
OperationCount = 0;

//
// Ihe main part of the algorithm starts at this point. It loops until conditions inside
// the loop causes the test completed value to be set to true.
//
while(TestCompleted == false)
{
OperationCount++;

//
// If the egg breaks from the current floor then go down to one floor above the
// previously tested floor, where the egg did not break. This floor would be a whole
// number of floors (going down) based on the increment value plus one. From this
// floor, go up one floor at a time until the second egg breaks, which is the solution
// to the problem.
//
if (Floor[Test] == true)
{
Test = Test - (Increment - 1);

while (TestCompleted == false)


{
OperationCount++;
Test++;
if (Floor[Test] == true)
{
TestCompleted = true;
TestSuccessful = true;
}
}
}

8
//
// Go up a whole incremented number of floors from the current floor otherwise, unless
// the incremented number of floors will exceed the total number of floors. In this
// case go to the top floor.
//
else
{
if (Test + Increment < Top)
{
Test += Increment;
}
else
{
Test = Top;
}
}
}

QueryPerformanceCounter(&ClockTicksEnd);

StepSearchTest.Operations = OperationCount;
StepSearchTest.Solution = Test;
StepSearchTest.Successful = TestSuccessful;
StepSearchTest.TimeTaken = (ClockTicksEnd.QuadPart - ClockTicksStart.QuadPart) * 1000.0 /
(ClockTicksPerSecond.QuadPart);

return StepSearchTest;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Constants for Testing //
////////////////////////////////////////////////////////////////////////////////////////////////////
const int NumberOfTests = 5;
const int MaximumFloors = 500;
const int NumberOfFloors[ ] = { 100, 200, 300, 400, 500 };
const int IncrementStart[ ] = { 5, 10, 12, 15, 18 };
const int IncrementEnd [ ] = { 15, 20, 22, 25, 28 };

////////////////////////////////////////////////////////////////////////////////////////////////////
// Main Function for the Test //
////////////////////////////////////////////////////////////////////////////////////////////////////
int main()
{
TestType StepSearchTest;

bool Floor[MaximumFloors];

int TestNumber = 0;
int TestOperations = 0;
double TestTimeTaken = 0;

printf("\n");
printf("--------------------------------------------------------- \n");
printf(" Testing Solutions to the Two Egg Problem \n");
printf("--------------------------------------------------------- \n");
printf("---- ------ --------- ------------------ ------------ \n");
printf("Test Floors Increment Average Operations Average Time \n");
printf("---- ------ --------- ------------------ ------------ \n");

for(int n = 0; n < NumberOfTests; n++)


{
for(int i = 1; i < MaximumFloors; i++)
{
Floor[i] = false;
}

for(int i = IncrementStart[n]; i <= IncrementEnd[n]; i++)


{
TestNumber += 1;
TestOperations = 0;
TestTimeTaken = 0;

for (int j = 1; j <= NumberOfFloors[n]; j++)


{
Floor[NumberOfFloors[n] - j] = true;

StepSearchTest = StepSearch(Floor, NumberOfFloors[n], i);

TestOperations = TestOperations + StepSearchTest.Operations;


TestTimeTaken = TestTimeTaken + StepSearchTest.TimeTaken;

9
}

printf("%04d %4d %4d %6.3f %6.6f s \n",


TestNumber,
NumberOfFloors[n],
i,
(double)((double)(TestOperations) / (double)(NumberOfFloors[n])),
(double)((double)(TestTimeTaken) / (double)(NumberOfFloors[n])));

for (int i = 1; i < MaximumFloors; i++)


{
Floor[i] = false;
}
}
}

return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// End of File //
////////////////////////////////////////////////////////////////////////////////////////////////////

10
Appendix II

---------------------------------------------------------
Testing Solutions to the Two Egg Problem
---------------------------------------------------------
---- ------ --------- ------------------ ------------
Test Floors Increment Average Operations Average Time
---- ------ --------- ------------------ ------------
0001 100 5 12.700 0.000057 s
0002 100 6 11.540 0.000041 s
0003 100 7 10.840 0.000067 s
0004 100 8 10.460 0.000221 s
0005 100 9 10.210 0.000031 s
0006 100 10 10.100 0.000036 s
0007 100 11 10.190 0.000046 s
0008 100 12 10.420 0.000072 s
0009 100 13 10.610 0.000046 s
0010 100 14 10.770 0.000036 s
0011 100 15 11.160 0.000036 s

0012 200 10 15.100 0.000049 s


0013 200 11 14.730 0.000054 s
0014 200 12 14.500 0.000059 s
0015 200 13 14.375 0.000041 s
0016 200 14 14.320 0.000049 s
0017 200 15 14.365 0.000036 s
0018 200 16 14.480 0.000046 s
0019 200 17 14.575 0.000049 s
0020 200 18 14.695 0.000044 s
0021 200 19 15.050 0.000044 s
0022 200 20 15.250 0.000036 s

0023 300 12 18.583 0.000051 s


0024 300 13 18.137 0.000050 s
0025 300 14 17.870 0.000053 s
0026 300 15 17.567 0.000060 s
0027 300 16 17.520 0.000050 s
0028 300 17 17.497 0.000057 s
0029 300 18 17.513 0.000041 s
0030 300 19 17.550 0.000048 s
0031 300 20 17.550 0.000046 s
0032 300 21 17.847 0.000050 s
0033 300 22 18.057 0.000063 s

0034 400 15 20.965 0.000057 s


0035 400 16 20.563 0.000057 s
0036 400 17 20.418 0.000060 s
0037 400 18 20.240 0.000057 s
0038 400 19 20.102 0.000057 s
0039 400 20 20.050 0.000060 s
0040 400 21 20.098 0.000051 s
0041 400 22 20.230 0.000050 s
0042 400 23 20.402 0.000057 s
0043 400 24 20.540 0.000073 s
0044 400 25 20.540 0.000059 s

0045 500 18 23.002 0.000071 s


0046 500 19 22.792 0.000071 s
0047 500 20 22.550 0.000070 s
0048 500 21 22.522 0.000172 s
0049 500 22 22.508 0.000066 s
0050 500 23 22.518 0.000071 s
0051 500 24 22.540 0.000088 s
0052 500 25 22.540 0.000084 s
0053 500 26 22.778 0.000079 s
0054 500 27 22.984 0.000078 s
0055 500 28 23.062 0.000080 s

11

Вам также может понравиться