Я работаю над программным обеспечением для машины, которая будет автоматически обрезать ногти на ногах, чтобы пользователи могли просто положить в нее ноги и запустить ее, вместо того, чтобы делать это вручную, кусая их или используя кусачки для ногтей.
Значительный процент нашей потенциальной базы пользователей, скорее всего, будут евреями, и, очевидно, существует традиция не стричь ногти на ногах ( или ногтях ) в последовательном порядке.
Похоже, существует особое мнение о точном применении этой традиции, но мы считаем, что следующих правил достаточно, чтобы приспособить людей, чьи религиозные обычаи запрещают стрижку ногтей на ногах, по порядку:
- Нельзя обрезать два соседних ногтя подряд
- Последовательность резания на левой ступне не должна совпадать с последовательностью на правой ступне.
- Последовательность резки в двух последовательных проходах не должна быть одинаковой. Последовательности не должны быть легко предсказуемыми, поэтому жесткое кодирование чередующейся последовательности не работает.
Вот как мы решили пронумеровать пальцы ног:
5 4 3 2 1 1 2 3 4 5
Left foot Right foot
Я написал код для решения проблемы, но используемый алгоритм не оптимален: на самом деле, производительность в худшем случае составляет O (∞) . Принцип его работы сравним с богосортом . Вот упрощенный псевдокод фактически используемого кода:
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)
По сути, он генерирует случайные последовательности и проверяет, соответствуют ли они критериям. Если он не соответствует критериям, он начинается заново. Это не занимает до смешного много времени, но очень непредсказуемо.
Я понимаю, что то, что я делаю сейчас, довольно ужасно, но у меня проблемы с поиском лучшего способа. Может ли кто-нибудь из вас предложить более элегантный и производительный алгоритм?
источник
Ответы:
Вы можете сгенерировать все возможные последовательности стрижки ногтей на ногах без ограничений, а затем отфильтровать все последовательности, которые нарушают еврейское правило. К счастью, у людей всего пять пальцев на ноге *, поэтому их всего пять! = 120 неограниченных последовательностей.
Пример Python:
Чтобы обеспечить соблюдение правила «Не повторять одну и ту же последовательность», вы можете просто выбрать четыре из указанных выше последовательностей и использовать их поочередно. Единственная загвоздка здесь в том, что если вы считаете два больших пальца ноги «последовательными», то вы не сможете выбрать две последовательности, которые заканчиваются и начинаются с 1 соответственно.
* Возможно, вы захотите создать переменную numberOfToesPerFoot, чтобы вы могли легко изменить ее позже, если у кого-то из ваших клиентов окажется меньше пальцев на ногах, чем вы ожидаете, или больше.
источник
Существует конечное количество последовательностей, удовлетворяющих вашим требованиям.
РЕДАКТИРОВАТЬ: если речь идет не о пальцах ног, а о какой-то случайной проблеме, когда набор может быть намного больше, чем 5, пространство последовательности становится очень большим, и вероятность повторения той же последовательности на второй ноге становится очень маленькой. Так что случайное создание последовательностей и их отклонение, если они совпадают, - хорошая идея. Генерация случайных последовательностей в соответствии с каким-то правилом, например «прыгать по два или по три, затем заполнять пробелы», вероятно, будет быстрее, чем генерация случайных перестановок и тестирования, и вероятность перекрытия все равно будет небольшой, если количество «пальцев» велико. ,
источник
На самом деле мне больше всего нравится ваш оригинальный алгоритм.
Поскольку 14 из 120 перестановок работают, 106 из 120 - нет. Таким образом, вероятность провала каждой проверки составляет 106/120.
Это означает, что ожидаемое количество отказов составляет:
Нетрудно суммировать этот бесконечный ряд:
Умножить на x:
Вычесть:
Снова умножаем на x и снова вычитаем:
Поскольку x = 106/120, S = 64,9.
Итак, в среднем вашему циклу требуется всего 65 итераций, чтобы найти решение.
Какова вероятность, что потребуется, скажем, тысяча итераций?
Что ж, вероятность неудачи любой отдельной итерации составляет 104/120, поэтому вероятность неудачной 1000 итераций составляет (104/120) ^ 1000, что примерно равно 10 ^ (- 63). То есть вы никогда не увидите, чтобы это произошло при вашей жизни, и, вероятно, не при жизни Вселенной.
Никаких предварительно вычисленных таблиц, легкая адаптация к разному количеству пальцев рук / ног / рук / ног, легкая адаптация к разным наборам правил ... Что не нравится? Экспоненциальный спад - замечательная вещь.
[Обновить]
Ой, я ошибся в исходной формуле ... Поскольку мои вероятности не равны 1. :-)
Правильное выражение ожидаемого числа отказов:
(Например, чтобы получить ровно два сбоя, вам нужны два сбоя, за которыми следует успех . Два сбоя имеют вероятность (106/120) ^ 2; один успех имеет вероятность (14/120); умножьте их вместе, чтобы получить вес для "2" срок.)
Таким образом, мой S выключен с коэффициентом (1-x) (т.е. 14/120). Фактическое ожидаемое количество отказов составляет всего x / (1-x) = 106/14 = 7,57. Таким образом, в среднем для поиска решения требуется всего 8-9 итераций (7,5 неудач плюс один успех).
Я думаю, что моя математика для случая «1000 отказов» все еще верна.
источник
Очевидное: найдите один, который работает, и жестко запрограммируйте его. Но я не думаю, что ты хочешь этого делать.
Вы можете генерировать перестановки намного лучше, чем вы это делаете. Вам не нужно делать выборку отбраковки. Используйте тасование Фишера Йетса для изначально отсортированной перестановки (1, 2, .. 5), и вы получите случайную перестановку. http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Но в целом методы генерации и тестирования мне кажутся вполне приемлемыми, если вероятность создания успешной записи достаточно высока. Я уверен, что существует множество допустимых последовательностей в соответствии с вашими критериями, и как только вы переключитесь на случайную перестановку, я сомневаюсь, что вам придется делать много итераций отклонения.
источник
Здесь нет ничего нового, такое же решение @Kevin уже было опубликовано, но я думаю, интересно посмотреть, как оно переводится на функциональный язык. В этом случае Mathematica :
Некоторые пояснения:
Конечный результат:
редактировать
Или, что труднее объяснить, но короче:
источник
На самом деле нет причин вводить случайность в эту проблему. Есть только 14 последовательностей, которые удовлетворяют этой проблеме, и, безусловно, некоторый порядок этих последовательностей лучше всего удовлетворит эстетический смысл, который вы пытаетесь приспособить. Таким образом, вам следует просто свести эту проблему к алгоритму выбора последовательности из этих 14, вероятно, в заранее установленном порядке.
Реализация Javascript алгоритма поиска 14:
РЕДАКТИРОВАТЬ: новое требование о том, что последовательности должны генерироваться случайным образом, не может быть легко выполнено. Лучшее, что вы, вероятно, можете сделать, - это сгенерировать их псевдослучайно, что столь же детерминировано, как и жесткое кодирование их заранее, и поэтому не должно удовлетворять чьи-либо суеверия.
источник