Thuật toán cắt móng chân người Do Thái tối ưu là gì?


118

Tôi đang nghiên cứu phần mềm cho một chiếc máy tự động cắt móng chân, để người dùng có thể chỉ cần đặt chân vào đó và chạy nó thay vì phải làm thủ công bằng cách cắn hoặc cắt móng tay.

Một tỷ lệ phần trăm đáng kể trong cơ sở người dùng tiềm năng của chúng tôi có thể sẽ là người Do Thái và hiển nhiên, có một truyền thống về việc không cắt tỉa móng chân ( hoặc móng tay ) theo thứ tự tuần tự

Có vẻ như có ý kiến ​​bất đồng về việc áp dụng chính xác truyền thống này, nhưng chúng tôi nghĩ rằng các quy tắc sau đây là đủ để áp dụng những người có thực hành tôn giáo cấm cắt móng chân theo thứ tự:

  • Không nên cắt hai móng chân liền nhau
  • Trình tự cắt ở chân trái không được khớp với trình tự ở chân phải
  • Trình tự cắt trên hai lần chạy liên tiếp không được giống nhau. Các trình tự không được dự đoán dễ dàng, vì vậy việc mã hóa cứng một trình tự xen kẽ không hoạt động.

Đây là cách chúng tôi quyết định đánh số ngón chân:

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

Tôi đã viết mã để giải quyết vấn đề, nhưng thuật toán được sử dụng là tối ưu phụ: trên thực tế, hiệu suất trong trường hợp xấu nhất là O (∞) . Cách nó hoạt động có thể so sánh với bogosort . Đây là một đơn giản hóa mã giả của mã thực được sử dụng:

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)

Về cơ bản, nó tạo ra các chuỗi ngẫu nhiên và kiểm tra xem chúng có đáp ứng các tiêu chí hay không. Nếu nó không đáp ứng các tiêu chí, nó sẽ bắt đầu lại. Nó không mất nhiều thời gian một cách lố bịch, nhưng nó rất khó lường.

Tôi nhận ra rằng cách tôi đang làm hiện tại khá tệ, nhưng tôi đang gặp khó khăn khi tìm ra cách tốt hơn. Có ai trong số bạn có thể đề xuất một thuật toán hiệu quả và thanh lịch hơn không?


28
Điều này giống như một vấn đề bài tập về nhà. Nếu không, tại sao không chỉ mã hóa chuỗi?
Michael Brown

24
Tôi đã nghe nói về việc cắn móng tay, nhưng móng chân?
bay

63
Ý nghĩ về một chiếc máy cắt móng chân khá đáng sợ. Tôi hy vọng đây thực sự là bài tập về nhà và không phải là một thảm kịch đau đớn đang chờ đợi xảy ra.
Peter Recore

43
Thử thách lập trình ở đây là điều khiển chiếc máy cắt móng chân để nó không làm bị thương người. Nếu có một lập trình viên có khả năng giải quyết vấn đề đó, thì chắc chắn người đó có thể giải quyết vấn đề này trong hai phút.
bay

41
Tôi thích thực tế là một câu hỏi về truyền thống Do Thái được gắn thẻ như (ngôn ngữ) thuyết bất khả tri ... :-)
Steve Melnikoff

Câu trả lời:


87

Bạn có thể tạo tất cả các trình tự cắt móng chân có thể có mà không có hạn chế, sau đó lọc ra tất cả các trình tự vi phạm quy tắc jewish. May mắn thay, con người chỉ có năm ngón chân trên mỗi bàn chân *, vì vậy chỉ có 5! = 120 trình tự không bị giới hạn.

Ví dụ 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)

Để thực thi quy tắc "không lặp lại cùng một trình tự", bạn chỉ có thể chọn bốn trong số các trình tự trên và sử dụng chúng luân phiên. Điểm duy nhất ở đây là nếu bạn đếm hai ngón chân cái là "liên tiếp", thì bạn không thể chọn hai chuỗi kết thúc và bắt đầu bằng 1, tương ứng.

* Bạn có thể muốn tạo một biến numberOfToesPerFoot, vì vậy bạn có thể dễ dàng thay đổi nó sau này nếu bất kỳ khách hàng nào của bạn có ít ngón chân hơn bạn mong đợi hoặc nhiều hơn.


22
Bạn đúng! Tôi thậm chí chưa bao giờ coi những người có polydactly . Sẽ là sai lầm nếu loại trừ chúng.
Peter Olson

1
trường hợp ít ngón chân hơn được thuật toán ban đầu bao gồm (trình tự có thể chấp nhận cho 5 ngón chân được chấp nhận cho 4 ngón). Đó là những ngón chân thừa điên rồ gây ra vấn đề;)
bay vào

4
Giải pháp rất hay! Tôi sẽ tiếp cận "không lặp lại cùng một trình tự" mặc dù hơi khác một chút. Chỉ cần làm cho máy ghi nhớ trình tự nó đã sử dụng cuối cùng và sử dụng trình tự ngẫu nhiên (nhưng không giống nhau) tiếp theo. Điều đó hoạt động cho chân thứ hai cũng như cho khách hàng mới và nó ngẫu nhiên hơn là gắn bó với 4 trình tự.
Jakob

3
Người ta cũng cần xem xét các ngón chân bị thiếu do cắt cụt, chẳng hạn như ngón chân thứ 3 bị thiếu. Điều này gây ra các vấn đề nếu, ví dụ, việc cắt bỏ ngón chân thứ 3 lúc này khiến các ngón chân 2 và 4 được coi là tuần tự.
cdeszaq

2
Còn những người chỉ có 2 ngón chân trong một bàn chân thì sao? Họ có được phép cắt móng chân không?
matiasg

26

Có một số trình tự hữu hạn đáp ứng yêu cầu của bạn.

  1. Tạo tất cả các hoán vị của {1,2,3,4,5}. Chỉ có 120.
  2. Từ chối những cái không đáp ứng yêu cầu và lưu trữ bộ còn lại (vĩnh viễn).
  3. Chọn ngẫu nhiên hai dãy khác nhau. Hãy nhớ những cái mà bạn đã sử dụng lần trước.

CHỈNH SỬA: Nếu đây không thực sự là về ngón chân, mà là về một số vấn đề ngẫu nhiên trong đó tập hợp có thể lớn hơn 5 rất nhiều, không gian trình tự trở nên rất lớn và cơ hội lặp lại cùng một trình tự ở chân thứ hai trở nên rất nhỏ. Vì vậy, tạo ngẫu nhiên các trình tự và từ chối chúng nếu chúng khớp là một ý kiến ​​hay. Tạo chuỗi ngẫu nhiên theo một số quy tắc như "nhảy theo hai hoặc ba, sau đó điền vào chỗ trống" có thể sẽ nhanh hơn tạo hoán vị và thử nghiệm ngẫu nhiên và cơ hội trùng lặp vẫn sẽ nhỏ nếu số lượng "ngón chân" lớn .


20

Trên thực tế, tôi thích thuật toán ban đầu của bạn nhất.

Vì 14 trong số 120 hoán vị hoạt động, 106 trong số 120 thì không. Vì vậy, mỗi séc có 106/120 cơ hội bị trượt.

Điều đó có nghĩa là số lỗi dự kiến ​​là:

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

Không quá khó để tổng hợp chuỗi vô hạn này:

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

Nhân với x:

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

Trừ đi:

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

Nhân với x một lần nữa và trừ một lần nữa:

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

Vì x = 106/120 nên S = 64,9.

Vì vậy, trung bình vòng lặp của bạn chỉ cần 65 lần lặp để tìm ra giải pháp.

Xác suất xảy ra, giả sử, một nghìn lần lặp là bao nhiêu?

Chà, xác suất không thành công bất kỳ lần lặp nào là 104/120, vì vậy xác suất thất bại 1000 lần lặp là (104/120) ^ 1000, giống như 10 ^ (- 63). Có nghĩa là, bạn sẽ không bao giờ thấy nó xảy ra trong cuộc đời của bạn, và có lẽ không phải trong cuộc đời của vũ trụ.

Không có bảng tính toán trước, dễ dàng thích ứng với số lượng ngón tay / ngón chân / bàn tay / bàn chân khác nhau, dễ dàng thích ứng với các bộ quy tắc khác nhau ... Còn gì không thích? Phân rã theo cấp số nhân là một điều tuyệt vời.

[cập nhật]

Rất tiếc, tôi đã lấy sai công thức ban đầu ... Vì xác suất của tôi không tổng bằng 1. :-)

Biểu thức đúng cho số lần hỏng hóc dự kiến ​​là:

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

(Ví dụ, để có được chính xác hai lần thất bại, bạn cần hai lần thất bại tiếp theo là một lần thành công . Hai lần thất bại có xác suất (106/120) ^ 2; một lần thành công có xác suất (14/120); nhân chúng với nhau để có trọng số cho Thuật ngữ "2".)

Vì vậy, S của tôi bị lệch bởi một hệ số của (1-x) (tức là, 14/120). Số lỗi dự kiến ​​thực tế chỉ là x / (1-x) = 106/14 = 7,57. Vì vậy, trung bình chỉ mất 8-9 lần lặp để tìm ra giải pháp (7,5 lần thất bại cộng với một lần thành công).

Tôi nghĩ rằng phép toán của tôi cho trường hợp "1000 lần thất bại" vẫn đúng.


1
+1 để suy nghĩ thấu đáo và đưa ra cách nhìn khác về vấn đề.
nalply

9

Điều hiển nhiên: Tìm một đơn đặt hàng hoạt động và viết mã đơn hàng đó. Nhưng tôi không nghĩ bạn muốn làm điều đó.

Bạn có thể tạo hoán vị tốt hơn nhiều so với cách bạn đang làm. Bạn không cần phải lấy mẫu từ chối. Sử dụng một phép ngẫu nhiên Fisher Yates trên một hoán vị được sắp xếp ban đầu (1, 2, .. 5), và bạn sẽ có một hoán vị ngẫu nhiên. http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

Nhưng nói chung, phương pháp tạo và kiểm tra có vẻ hoàn toàn ổn đối với tôi, miễn là xác suất tạo ra một mục nhập thành công đủ cao. Tôi chắc chắn có nhiều chuỗi hợp lệ theo tiêu chí của bạn, một khi bạn chuyển sang hoán vị ngẫu nhiên, tôi nghi ngờ bạn sẽ phải thực hiện nhiều lần lặp từ chối.


2

Không có gì thực sự mới ở đây, giải pháp tương tự @Kevin đã được đăng, nhưng tôi nghĩ thú vị khi xem cách nó dịch sang một ngôn ngữ chức năng. Trong trường hợp này, Mathematica :

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

Một số giải thích:

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

Kết quả cuối cùng là:

{{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}}

Biên tập

Hoặc, khó giải thích hơn, nhưng ngắn gọn hơn:

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

0

Thực sự không có lý do gì để đưa sự ngẫu nhiên vào vấn đề này. Chỉ có 14 chuỗi đáp ứng vấn đề này, và chắc chắn một số thứ tự của những chuỗi đó sẽ đáp ứng tốt nhất gu thẩm mỹ mà bạn đang cố gắng đáp ứng. Vì vậy, bạn chỉ nên giảm vấn đề này thành một thuật toán để chọn một chuỗi từ 14 chuỗi đó, có thể là theo một thứ tự đặt trước.

Triển khai thuật toán Javascript để tìm 14:

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)

CHỈNH SỬA: Không thể dễ dàng đáp ứng được yêu cầu mới mà các chuỗi phải được tạo ngẫu nhiên. Điều tốt nhất bạn có thể làm là tạo ra chúng không thường xuyên, điều này cũng có tính xác định giống như việc mã hóa cứng chúng trước thời hạn, và vì vậy không nên thỏa mãn sự mê tín của bất kỳ ai.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.