Я смотрю на Задача тридцать одна в Project Euler, которая спрашивает, сколько существует способов заработать £ 2, используя любое количество монет 1p, 2p, 5p, 10p, 20p, 50 пенсов, 1 фунт (100 пенсов) и 2 фунта (200 пенсов).
Есть рекурсивные решения, такие как это в Scala (кредит Павлу Фатину)
def f(ms: List[Int], n: Int): Int = ms match {
case h :: t =>
if (h > n) 0 else if (n == h) 1 else f(ms, n - h) + f(t, n)
case _ => 0
}
val r = f(List(1, 2, 5, 10, 20, 50, 100, 200), 200)
и хотя он работает достаточно быстро, он относительно неэффективен, вызывая функцию f
около 5,6 миллионов раз.
Я видел чье-то решение на Java, которое программировалось динамически (кредит wizeman из Португалии)
final static int TOTAL = 200;
public static void main(String[] args) {
int[] coins = {1, 2, 5, 10, 20, 50, 100, 200};
int[] ways = new int[TOTAL + 1];
ways[0] = 1;
for (int coin : coins) {
for (int j = coin; j <= TOTAL; j++) {
ways[j] += ways[j - coin];
}
}
System.out.println("Result: " + ways[TOTAL]);
}
Это намного эффективнее и проходит внутренний цикл только 1220 раз.
Хотя я мог бы, очевидно, перевести этот более или менее дословно в Scala, используя Array
объекты, существует ли идиоматический функциональный способ сделать это, используя неизменные структуры данных, предпочтительно с аналогичной краткостью и производительностью?
Я пытался застрять, пытаясь рекурсивно обновить List
, прежде чем решить, что, вероятно, просто подхожу к нему неправильно.