最適なユダヤ人の足の爪の切断アルゴリズムは何ですか?


118

私は、足の爪を自動的にトリミングするマシンのソフトウェアに取り組んでいます。そのため、ユーザーは自分の足をその中に入れて実行するだけで、手動でかむことや爪切りを使用する必要はありません。

潜在的なユーザーベースのかなりの割合がユダヤ人である可能性が高く、明らかに、指のまたは指の爪)を順番どおりにトリミングしないという伝統があります。

この伝統の正確な適用については反対意見があるようですが、次のルールは、宗教的な慣習により爪を順番に切ることを禁止している人々に対応するのに十分だと思います。

  • 隣接する2つの足指の爪を連続して切断しないでください
  • 左足の切断順序は右足の順序と一致してはなりません
  • 2つの連続したランの切断順序は同じであってはなりません。シーケンスは簡単には予測できないため、交互のシーケンスをハードコーディングしても機能しません。

これは、つま先に番号を付けることを決定した方法です。

5 4 3 2 1  1 2 3 4 5
Left foot  Right foot

問題を解決するためのコードを作成しましたが、使用されているアルゴリズムは最適ではありません。実際、最悪の場合のパフォーマンスはO(∞)です。動作方法はbogosortに匹敵します。以下は、使用される実際のコードの疑似コードの簡略化です。

function GenerateRandomSequence
   sequence = Array[5]
   foreach (item in sequence)
       item = RandomNumberBetween(1,5)
   return sequence

function GetToenailCuttingOrder
   while (true)
      sequence = GenerateRandomSequence()
      if (!AllItemsAreUnique(sequence))
         continue
      if (NoTwoAdjacentItemsHaveConsecutiveNumbers(sequence))
         return sequence

do
    leftFootSequence = GetToenailCuttingOrder()
    rightFootSequence = GetToenailCuttingOrder()
until (leftFootSequence != rightFootSequence &&
       leftFootSequence != leftFootSequenceFromLastRun &&
       rightFootSequence != rightFootSequenceFromLastRun)

基本的に、ランダムなシーケンスを生成し、それらが基準を満たしているかどうかをチェックします。基準を満たしていない場合は、最初からやり直します。途方もなく長い時間はかかりませんが、非常に予測不可能です。

現在のやり方はかなりひどいことに気づきましたが、もっと良い方法を思い付くのに苦労しています。よりエレガントで高性能なアルゴリズムを提案できる人はいますか?


28
これは宿題のようなにおいがします。そうでなければ、なぜシーケンスをハードコーディングしないのですか?
マイケルブラウン

24
爪を噛むと聞いたことがありますが、足指の爪?
2011年

63
足の爪の切断機の考えはかなり怖いです。これが本当に宿題であり、起こるのを待っている悲痛な悲劇ではないことを願っています。
Peter Recore、2011年

43
ここでのプログラミングの課題は、爪を切る機械を制御して、人を傷つけないようにすることです。その問題を解決できるプログラマーがいれば、その人はきっとこの問題を2分で解決できるでしょう。
ハエ

41
ユダヤ人の伝統についての質問が(言語)不可知論者としてタグ付けされているという事実が好きです... :-)
Steve Melnikoff

回答:


87

制限なしですべての可能な足の爪の切断シーケンスを生成し、ユダヤ人の規則に違反するすべてのシーケンスをフィルターで取り除くことができます。幸い、人間の足の指は5本しかありません*。= 120個の無制限のシーケンス。

Pythonの例:

#seq is only valid when consecutive elements in the list differ by at least two.
def isValid(seq):
    for i in range(len(seq)-1):
        a = seq[i]
        b = seq[i+1]
        if abs(a-b) == 1:
            return False
    return True


from itertools import ifilter, permutations
validseqs = ifilter(isValid, permutations([1,2,3,4,5]))
for i in validseqs:
    print i

(1, 3, 5, 2, 4)
(1, 4, 2, 5, 3)
(2, 4, 1, 3, 5)
(2, 4, 1, 5, 3)
(2, 5, 3, 1, 4)
(3, 1, 4, 2, 5)
(3, 1, 5, 2, 4)
(3, 5, 1, 4, 2)
(3, 5, 2, 4, 1)
(4, 1, 3, 5, 2)
(4, 2, 5, 1, 3)
(4, 2, 5, 3, 1)
(5, 2, 4, 1, 3)
(5, 3, 1, 4, 2)

「同じシーケンスを繰り返さない」ルールを適用するには、上記のシーケンスを4つ選択し、それらを交互に使用します。ここでの唯一の問題は、2つの親指を「連続」として数えると、それぞれ1で終わる2つのシーケンスを選択できないことです。

* numberOfToesPerFoot変数を作成すると、クライアントのつま先の数が予想より少ない場合、または多い場合に後で簡単に変更できます。


22
あなたが正しい!私は多指症の人すら考えていませんでした。それらを除外するのは間違っているでしょう。
Peter Olson

1
つま先の数が少ない場合は、元のアルゴリズムでカバーされます(5つま先の許容シーケンスは4つま先で許容されます)。問題を引き起こすのはクレイジーなつま先です;)
ハエ

4
とても良い解決策です!私は「同じシーケンスのリピートなし」に少し異なるアプローチをします。最後に使用したシーケンスをマシンに記憶させ、次にランダムなシーケンス(ただし、同じシーケンスではない)を使用するだけです。これは2番目の足だけでなく新しいクライアントでも機能し、4つのシーケンスを使用するよりもランダムです。
Jakob

3
また、第3足指の欠損など、切断による足指の欠損を考慮する必要があります。これにより、たとえば、3番目のつま先を削除するとつま先2と4が連続していると見なされる場合に問題が発生します。
cdeszaq 2012年

2
片足でつま先が2つしかない人はどうですか?彼らは足の爪を切ることを許可されていますか?
matiasg 2014

26

要件を満たすシーケンスの数には限りがあります。

  1. {1,2,3,4,5}のすべての順列を生成します。120しかない。
  2. 要件を満たさないものを拒否し、残りのセットを(永久に)保存します。
  3. 2つの異なるシーケンスをランダムに選択します。前回使用したものを覚えておいてください。

編集:これが本当のつま先ではないが、セットが5をはるかに超える可能性があるランダムな問題については、シーケンススペースが非常に大きくなり、2番目の足で同じシーケンスを繰り返す可能性が非常に小さくなります。したがって、シーケンスをランダムに生成し、それらが一致する場合にそれらを拒否することは良い考えです。「2つか3つずつホップしてから空白を埋める」などのルールに従ってランダムシーケンスを生成すると、ランダムな順列とテストを生成するよりも高速であり、「つま先」の数が多い場合でも重複の可能性は小さいままです。 。


20

実は、元のアルゴリズムが一番好きです。

120の置換のうち14は機能するため、120のうち106は機能しません。したがって、各チェックは106/120の確率で失敗します。

つまり、予想される失敗の数は次のとおりです。

1*(106/120) + 2*(106/120)^2 + 3*(106/120)^3 + ...

この無限のシリーズを要約するのはそれほど難しくありません:

S       = 1*x + 2*x^2 + 3*x^3 + ...

xを掛ける:

x*S     =       1*x^2 + 2*x^3 + ...

減算:

S - x*S = x + x^2 + x^3 + ...

もう一度xを掛けて、もう一度減算します。

x*S - x^2*S = x^2 + x^3 + ...
S - 2*x*S + x^2S = x
S*(1-x)^2 = x
S = x/(1-x)^2

x = 106/120なので、S = 64.9。

したがって、ループは平均して65回の反復で解決策を見つけることができます。

たとえば、1,000回の反復が行われる確率はどれくらいですか?

さて、1回の繰り返しに失敗する確率は104/120なので、1000回の繰り返しに失敗する確率は(104/120)^ 1000で、10 ^(-63)のようなものです。つまり、あなたはそれがあなたの生涯に起こることは決してないでしょう、そしておそらく宇宙の生涯では起こりません。

事前に計算されたテーブルはなく、指/つま先/手/足の数に合わせて簡単に調整でき、さまざまなルールセットに簡単に調整できます...気に入らない点は何ですか?指数関数的減衰は素晴らしいことです。

[更新]

おっと、元の式が間違っていました...私の確率の合計が1にならないため:-)

予想される失敗数の正しい式は次のとおりです。

0*(14/120) + 1*(106/120)*(14/120) + 2*(106/120)^2*(14/120) + ...

(たとえば、正確に2つの失敗を取得するには、2つの失敗とそれに続く成功が必要です。2つの失敗には確率(106/120)^ 2があり、1つの成功には確率(14/120)があります。これらを掛け合わせて、 「2」項。)

したがって、私のSは(1-x)の係数(つまり、14/120)だけずれています。実際に予想される失敗の数は、x /(1-x)= 106/14 = 7.57です。したがって、平均して8-9回の反復で解決策を見つけることができます(7.5回の失敗と1回の成功)。

「1000失敗」の場合の私の計算はまだ正しいと思います。


1
+1を考えれば、問題の別の見方ができます。
11

9

明白なこと:機能する注文を1つ見つけ、ハードコーディングします。しかし、私はあなたがそうしたいとは思わない。

あなたはそれをやっている方法よりもはるかに良い順列を生成することができます。拒否のサンプリングを行う必要はありません。最初に並べ替えられた順列(1、2、.. 5)でフィッシャーイェーツのシャッフルを使用すると、ランダムな順列になります。 http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

しかし、一般的に、エントリの生成が成功する確率が十分に高い限り、生成とテストの方法はまったく問題ありません。あなたの基準に従って多くの有効なシーケンスがあると私は確信しています。ランダムな順列に切り替えたら、多くの拒否の反復を行わなければならないことは間違いないでしょう。


2

ここには本当に新しいものはありません。同じソリューション@Kevinはすでに投稿されていますが、それが関数型言語にどのように変換されるかを見るのは興味深いと思います。この場合、Mathematica

Extract[#,Position[Times @@ (Abs@#-1)&/@ Differences/@ #, Except@0, 1][[2 ;;]]]  
         &@ Permutations@Range@5

いくつかの説明:

Permutations@Range@5 Calculates all permutations of {1, 2, 3, 4, 5}

Differences          Calculate the differences between adjacent elements
                     (we wish to discard all lists containing +1 or -1)

Times @@ (Abs@#-1)   Abs turns the -1s into +1s, and then to zeros by subtracting
                     one, then TIMES multiplies all elements, giving zero when 
                     the original result of "Differences" contained a +1 or a -1

Position ... Except@0 Returns the position of the non zero results

Extract              Returns the original elements according to the calculated 
                     positions

最終結果は次のとおりです。

{{1, 3, 5, 2, 4}, {1, 4, 2, 5, 3}, {2, 4, 1, 3, 5}, {2, 4, 1, 5, 3}, 
 {2, 5, 3, 1, 4}, {3, 1, 4, 2, 5}, {3, 1, 5, 2, 4}, {3, 5, 1, 4, 2}, 
 {3, 5, 2, 4, 1}, {4, 1, 3, 5, 2}, {4, 2, 5, 1, 3}, {4, 2, 5, 3, 1}, 
 {5, 2, 4, 1, 3}, {5, 3, 1, 4, 2}}

編集する

または、説明するのはより困難ですが、短くなります。

Reap[ Table[ If[Times @@ (Abs@Differences@i - 1) != 0, Sow@i],
           {i, Permutations@Range@5}]][[2, 1]]

0

この問題にランダム性を導入する理由は本当にありません。この問題を満たすシーケンスは14しかありません。これらのシーケンスの順序付けは、対応しようとしている美的感覚を最もよく満たすはずです。したがって、これらの14からシーケンスを選択するアルゴリズムにこの問題を削減する必要があります。

14を見つけるためのアルゴリズムのJavascript実装:

function swap (a, i, j) {
  var temp = a[i]
  a[i]=a[j]
  a[j]=temp
}

function permute (b, n, a) {
  if (n==4) {
    b.push(a.slice(0)) //copy array
  }
  else {
    for (var i = n; i < 5; i++) {
      swap(a,n,i)
      permute(b, n+1, a)
      swap(a,n,i)
    }
  }
}

var a = [1,2,3,4,5]
var b = []
var c = []

permute(b,0,a)

for (var i = 1; i < b.length-1; i++) {
  var good = true
  for (var j = 0; j < b[i].length; j++) {
    if (Math.abs(b[i][j-1]-b[i][j]) < 2 || Math.abs(b[i][j]-b[i][j+1]) < 2) {
      good = false
    }
  }
  if (good) c.push(b[i].join(''))
}

console.log(c)

編集:シーケンスをランダムに生成する必要があるという新しい要件を簡単に満たすことはできません。おそらくあなたができる最善のことは、それらを疑似ランダムに生成することです。これは、事前にそれらをハードコーディングするのと同じように決定論的であり、だれの迷信も満たさないはずです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.