#kotlin #recursion
#kotlin #рекурсия
Вопрос:
Это не домашнее задание, а приложение реального мира, на котором я зацикливаюсь.
Лучший способ, которым я могу объяснить свою проблему, заключается в следующем:
Представьте, что у вас есть 3 свинарника A
, B
, и C
и 10 свиней, которых нужно содержать. Ручки имеют ограничения в том, что в каждой ручке должно быть не менее 1 свиньи и A.pigs <= B.pigs <= C.pigs
. Перечислите все возможные конфигурации пера.
РЕАЛЬНОЕ приложение может содержать от 1 до 7 ручек, а также от numPens
и numPens*30
до свиней, и ни одна ручка не может содержать более 30 свиней. Таким образом, по сути, это означает, что «какие числа от 1 до 30 (допустимые повторы) составляют X, используя числа Y»
Это можно было бы решить просто с помощью вложенных циклов, но это ужасно неэффективное решение, особенно с учетом области реального применения:
var cnt = 0
val target = 10
for (a in 1..30) {
for (b in a..30) {
val c = target - a - b
cnt
if (a <= b amp;amp; b <= c amp;amp; a b c == target) {
println("$a $b $c")
}
}
}
println(cnt)
вывод:
1 1 8
1 2 7
1 3 6
1 4 5
2 2 6
2 3 5
2 4 4
3 3 4
465
Я уверен, что есть рекурсивное решение этой проблемы. Но у меня возникли проблемы даже с поиском отправной точки для этого.
Кажется, легко начать с массива [1, 1, 8]
, где каждый индекс представляет перо. Просто поместите всех свиней в 1 загон и перемещайте их по 1 за раз, соблюдая ограничения.
Вычтите из C, добавьте к B столько, сколько сможете, сохраняя при этом ограничения, [1, 2, 7], [1, 3, 6], [1, 4, 5]
но в этот момент я застрял в коде.
Что у меня есть в настоящее время:
fun main(vararg args: String) {
val list = arrayOf(1, 1, 8)
list.forEach { print("$itt") }
println()
rec(list, list.lastIndex - 1)
}
fun rec(list: Array<Int>, index: Int) {
if (index == list.lastIndex || index < 0) {
return
}
while (list[index] 1 <= list[index 1] - 1) {
list[index]
list[index 1]--
list.forEach { print("$itt") }
println()
}
}
Очевидно, мне нужно вызвать rec
где-то внутри себя и, вероятно, нужны некоторые условные обозначения для правильного вызова rec (возможно, добавив к нему дополнительные параметры). Вопрос в том, где и как? Обычно неплохо с рекурсией, но это ставит меня в тупик.
Решение: я так зациклился на идее, что мне нужна рекурсия. Этот цикл, похоже, работает довольно хорошо.
for (a in 1..30) {
// we know a and c must be at least b, so cap range
var b = a
while(b < min(target - a - b, 30)){
val c = target - a - b
cnt
if (a <= b amp;amp; b <= c amp;amp; a b c == target) {
println("$a $b $c")
}
b
}
}
Комментарии:
1. Лучше на codereview.stackexchange.com ?
2. На каком языке вы это пишете?
3. @ScottHunter это было написано с помощью kotlin
4. Почему использование циклов «ужасно неэффективно»?
5. @ScottHunter показанные вложенные циклы имеют время выполнения O (n ^ pens). хотя я знаю, что можно найти решение для примера за 8 итераций кода. Я просто не могу придумать код, чтобы это осуществить
Ответ №1:
Следует отметить некоторые недостатки:
- Как только вы узнаете
a
иb
, единственным значением дляc
этого может быть решение, будет 10 —a
b
, так что вам вообще не нужен самый внутренний цикл. - Поскольку
a
<=b
, второй цикл должен начинаться сa
, а не с 1 . - Вы также можете ограничить верхнюю часть диапазона второго цикла на основе значения
a