web-dev-qa-db-ja.com

窓から猫を投げる

あなたが猫のいる高層ビルにいると想像してください。猫は低層階の窓から転落しても生き残ることができますが、高床から投げられると死にます。最小の試行回数で、猫が生き残ることができる最長の低下をどのように把握できますか?

猫が1匹しかない場合、明らかに直線的にしか検索できません。最初に1階から猫を投げます。生き残ったら、2番目から投げます。最終的に、床fから投げられた後、猫は死にます。これで、フロアf-1が最大の安全フロアであったことがわかります。

しかし、猫が複数いる場合はどうでしょうか?これで、ある種の対数検索を試すことができます。ビルドに100フロアがあり、2匹の同一の猫があるとします。 50階から最初の猫を捨てて死んだ場合、50階を直線的に検索するだけで済みます。最初の試行で下の階を選択すると、さらに良い結果が得られます。一度に20フロアの問題に取り組むことを選択し、最初の致命的なフロアは#50であるとしましょう。その場合、最初の猫は20階と40階からのフライトを生き延びてから60階で死亡します。41階から49階を個別に確認するだけです。これは合計12回の試行であり、バイナリ除去を使用しようとした場合に必要な50回よりもはるかに優れています。

一般的に、2匹の猫がいるn階建ての建物の最善の戦略と最悪の場合の複雑さは何ですか?n階とm猫の場合はどうですか?

すべての猫は同等であると仮定します:それらはすべて、特定の窓からの落下で生き残るか死ぬでしょう。また、すべての試みは独立しています:猫が転倒を生き延びた場合、それは完全に無害です。

これは宿題ではありませんが、学校の課題で一度解決したかもしれません。それは今日私の頭に浮かんだ気まぐれな問題であり、解決策を覚えていません。誰かがこの問題または解決アルゴリズムの名前を知っている場合のボーナスポイント。

150
AndrewF

N個のフロアとm個の猫の一般的な場合に、簡単に小さなDP(動的プログラミング)を書くことができます。

主な式a[n][m] = min(max(a[k - 1][m - 1], a[n - k][m]) + 1) : for each k in 1..nは、一目瞭然です:

  • 最初の猫がk階から投げ出されて死亡した場合、チェックする_k - 1_階(すべてkの下)と_m - 1_猫(_a[k - 1][m - 1]_)があります。
  • Catが生き残った場合、_n - k_フロアが残り(kより上のすべてのフロア)、まだm猫が残っています。
  • 2つの最悪の場合を選択する必要があるため、maxです。
  • _+ 1_は、(catが生き残ったかどうかに関係なく)1回だけ試行したという事実に由来します。
  • 可能な限りすべてのフロアを試して最良の結果を見つけるため、min(f(k)) : for k in 1..nです。

Gaurav Saxenaの(100、2)のリンクからのGoogleの結果に同意します。

_int n = 100; // number of floors
int m = 20; // number of cats
int INFINITY = 1000000;

int[][] a = new int[n + 1][m + 1];
for (int i = 1; i <= n; ++i) {
    // no cats - no game
    a[i][0] = INFINITY;
}

for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
        // i floors, j cats
        a[i][j] = INFINITY;

        for (int k = 1; k <= i; ++k) {
            // try throw first cat from k-th floor
            int result = Math.max(a[k - 1][j - 1], a[i - k][j]) + 1;
            a[i][j] = Math.min(a[i][j], result);
        }
    }
}

System.out.println(a[n][m]);
_

最適なkを別の配列に保存すると、戦略(最初の猫を投げる方法)を簡単に見つけることができます。

O(n ^ 3)計算を含まない、より高速なソリューションもありますが、私はもう少し眠いです。

編集
ああ、そうです この問題を以前見た場所を覚えています

70
Nikita Rybak

Radiolabの最近のエピソード(「落下」について) によると、猫は9階までに終端速度に達します。その後、それは緩和し、傷つく可能性が低くなります。 30日を過ぎて落下した後、完全に負傷した猫がいます。最もリスクの高いフロアは5〜9です。

92
Thilo

あなたが猫のいる高層ビルにいると想像してください。猫は低層階の窓から転落しても生き残ることができますが、高床から投げられると死にます。最小の試行回数で、猫が生き残ることができる最長の低下をどのように把握できますか?

この問題を解決するための最良の戦略は、物理法則を使用して、そもそも仮定が真実である確率を調査することです。

もしそうすれば、猫の生存の可能性は、地上までの距離が長くなるほど実際に増加することに気付くでしょう。もちろん、ペトロナスタワーなどのこれまで以上に高い建物から投球し、エベレスト山などのこれまで以上に高い山から投球しないと仮定します。

編集:
実際には、未完成のラクダの配布が表示されます。
まず、猫が死ぬ確率は低く(非常に低い高度)、次に高くなり(低い高度)、次に再び低くなり(高い高度)、次に再び高くなります(非常に高い高度)。

猫が地上の高度の関数として死亡する確率のグラフは次のようになります。
(未完成のラクダ分布のため、3で終了)

alt text

更新:
猫の終末速度は100 km/h(60mph)[= 27.7m/s = 25.4ヤード/秒]です。
人間の終末速度は210 km/h(130mph)です。[= 75m/s = 68.58ヤード/秒]

ターミナル速度ソース:
http://en.wikipedia.org/wiki/Cat_righting_reflex

クレジット:
Goooooogle

後で確認する必要があります。
http://en.wikipedia.org/wiki/Terminal_velocity
http://www.grc.nasa.gov/WWW/K-12/airplane/termv.html


10
Stefan Steiger

私はこの問題を最初にSteven SkienaのAlgorithm Design Manual(演習8.15)で読みました。動的プログラミングの章に従いましたが、戦略の正確な限界を証明するために動的プログラミングを知る必要はありません。最初に問題の説明、次に以下の解決策。

卵は十分な高さから落とすと壊れます。 n階建ての建物を考えると、床fから落ちた卵が壊れるが、床f-1から落ちた卵は生き残るような床fがなければなりません。 (卵が任意のフロアから壊れた場合、f = 1と言います。卵が任意のフロアから生き残った場合、f = n + 1と言います)。

クリティカルフロアを見つけようとしますf。実行できる唯一の操作は、卵を床から落とし、何が起こるかを確認することです。 k個の卵から始めて、できるだけ数回卵を落とそうとします。壊れた卵は再利用できません(無傷の卵はできます)。 E(k、n)を常に十分な卵の糞の最小数とします。

  1. E(1、n)= nであることを示します。
  2. E(k,n) = Θ(n**(1/k))であることを示します。
  3. E(k、n)の繰り返しを見つけます。 E(k、n)を見つけるための動的プログラムの実行時間は?

卵1個のみ

最初のフロアから開始して各フロアから卵をドロップすると、(最悪の)n回の操作で重要なフロアが見つかります。

より高速なアルゴリズムはありません。任意のアルゴリズムでいつでも、卵が壊れていないことが確認された最上階をgにします。アルゴリズムは、高階h> g + 1の前に階g + 1をテストする必要があります。そうでない場合、卵が階hから分割される場合、f = g + 1とf = hを区別できません。

卵2個

まず、n = r ** 2が完全な正方形である場合、k = 2の卵の場合を考えてみましょう。これは、O(sqrt(n))時間です。最初のエッグをrフロア単位でドロップすることから始めます。最初のエッグが壊れたとき、フロアarで、クリティカルフロアfは_(a-1)r < f <= ar_でなければならないことを知っています。その後、各フロアから_(a-1)r_で始まる2番目のエッグをドロップします。 r時間なので、このアルゴリズムは最悪の2r操作、つまりΘ(sqrt(n))を取ります。

Nが完全な正方形でない場合、r = ceil(sqrt(n)) ∈ Θ(sqrt(n))を使用します。アルゴリズムはΘ(sqrt(n))のままです。

アルゴリズムが少なくともsqrt(n)時間かかることの証明。より高速なアルゴリズムがあるとします。 (壊れない限り)最初のエッグをドロップするフロアのシーケンスを検討します。ドロップするのはsqrt(n)より少ないため、少なくともn/sqrt(n)の間隔が必要です。これはsqrt(n)です。 fがこの間隔にある場合、アルゴリズムは2番目のEggでfを調査する必要があり、1-Eggの場合を思い出して、フロアごとに実行する必要があります。矛盾。

k個の卵

2個の卵に対して提示されたアルゴリズムは、k個の卵に簡単に拡張できます。一定の間隔で各Eggをドロップします。これは、nのk番目のルートの累乗と見なされます。たとえば、n = 1000およびk = 3の場合、最初のEggで100フロア、2番目のEggで10フロア、最後のEggで1フロアの間隔を検索します。

同様に、k = 2の証明から誘導することにより、アルゴリズムがより高速であることを証明できますΘ(n**(1/k))

正確なソリューション

小さいパラメーターの最適なソリューションがわかっていると仮定して、最初のEgg(フロアg)をドロップする場所を最適化することにより、再発を推測します。卵が壊れた場合、k-1個の卵を探索するために、g-1階が下にあります。卵が生き残った場合、k個の卵を探索するために上の階がn-gあります。悪魔は私たちにとって最悪のものを選びます。したがって、k> 1の場合、繰り返し

_E(k,n) = min(max(E(k,n-g), E(k-1,g))) minimised over g in 1..n
_
8
Colonel Panic

これは、「The Same Cat」を使用していると仮定していませんか?

数学的にアプローチすることはできますが、それは数学の素晴らしいところです...正しい仮定では、0は1に等しくなります(0の大きな値の場合)。

実用的な観点からは、「類似猫」は入手できますが、「同じ猫」は入手できません。

あなたは経験的に答えを決定しようとすることができますが、答えが統計的に意味のない十分な統計的差異があると思います。

「同じ猫」を使用することもできますが、最初のドロップ後は同じ猫ではなくなるため、うまくいきません。 (同様に、同じ川に二度と足を踏み入れることはできません)

または、非常に近い間隔でサンプリングして猫の健康状態を集計し、猫が「ほとんど生きている」(「The Princess Bride」の「mostly dead」とは反対)の高さを見つけることができます。猫は平均して(最後の間隔まで)生き残ります。

元の意図から外れたと思いますが、経験的なルートに進む場合は、統計的に生き残るまで、可能な限り高く開始し、身長が低くなるにつれて猫を落とし続けることに投票します。そして、確実に生き残った猫で再テストします。

2
Marc

私はソリューションを作成するためにわずかに異なる方法を取りました。

x catsとyの推測を使用して、次の方法でカバーできる最大床を計算することから始めました。

1フロアから始めて、推測されたフロアを追跡しながら推測数を増やし続けます。これにより、チェックされた推測と、各フロアに残っている猫の数が推測されます。
これをy回まで繰り返します。

このvery与えられた答えを計算するのに非効率的なコードですが、それでも少数の猫/床には有用です。

Pythonコード:

def next_step(x, guess):
  next_x = []
  for y in x:
    if y[0] == guess:
      if y[1] != 1:
        next_x.append((guess+1, y[1] - 1))
    next_x.append(y)
    if y[0] == guess:
      next_x.append((guess+1, y[1]))
  return next_x

x = [(1, TOTAL_NUM_CATS)]
current_floor = 1
while len(x) <= TOTAL_NUM_FLOORS:
  x = next_step(x, current_floor)
  current_floor += 1
  print len(x)

2匹の猫の場合、x回の推測で識別できる最大床数は次のとおりです。
1、3、6、10、15、21、28 ...

3匹の猫の場合:
1、3、7、14、25、41、63 ...

4匹の猫の場合:
1、3、7、15、30、56、98 ...

広範な調査の後(ほとんどの場合、 [〜#〜] oeis [〜#〜] に数字のシーケンスを入力する必要があります)xの最大フロアは 組み合わせ 区分的パターン。

2匹の猫の場合:
n <2:2 ^ n-1
n> = 2:C(n、1)+ C(n、2)

3匹の猫の場合:
n <3:2 ^ n-1
n> = 3:C(n、1)+ C(n、2)+ C(n、3)

4匹の猫の場合:
n <4:2 ^ n-1
n> = 4:C(n、1)+ C(n、2)+ C(n、3)+ C(n、4)

ここから、必要なフロア数を渡すまで、単純にnをインクリメントするという簡単なアプローチを取りました。

Pythonコード:

def find_smallest(floors, eggs):
  maximum_floors = 0
  n = 0
  while maximum_floors < floors:
    maximum_floors = 0
    n += 1
    if n < eggs:
      maximum_floors = 2**n - 1
    else:
      count = 0
      for x in xrange(1, eggs+1):
        maximum_floors += combination(n, x)
  print n

これにより、(100、2)= 14の正しい解が得られます。
ささいなことをチェックしたい人には、(1 000 000、5)= 43を返します。

これはO(n)で実行されます。ここで、nは問題に対する答えです(猫が多ければ多いほど良い)。
しかし、数学のレベルが高い人なら、区分式を単純化してO(1)で計算できると確信しています。

0
threenplusone
O(m*(n^(1/m))) algorithm.

Let 'x' be the maximum number of attempts needed.  

m = 1 => linear => x=n

m = 2:  
Let the floors be split into 'k' partitions. The first cat is thrown at the end of each partition (max 'k' times). 
When it dies, the second cat is used to go up from the beginning of this partition.   
x = k + n/k.   
Minimize x by diff wrt k and setting = 0, to get k = n^(1/2) and x = 2 * n^(1/2).

m = 3:  
x = k + 2*(y^(1/2)), where y = n/k  
diff wrt x and set = 0, to get k = n^(1/3) and x = 3 * n^(1/3)

for general m:  
x = m * n^(1/m). 
0
tldr