Одним из способов является кэширование, которое идет с большой ценой использования памяти.
public static int combinations(int group, int members) {
if (members > group - members) {
members = group - members; // 21 choose 17 is same as 21 choose 4
}
final int[][] cache = new int[group][members];
return combinations(group, members, cache);
}
private static int combinations(int group, int members, int[][] cache) {
if (cache[group - 1][members - 1] > 0) {
return cache[group - 1][members - 1];
}
else if (members == 1) {
cache[group - 1][members - 1] = group;
return group;
}
else if (members == group) {
cache[group - 1][members - 1] = 1;
return 1;
}
else {
return (combinations(group - 1, members - 1, cache) + combinations(group - 1, members, cache));
}
}
Я провел небольшой тест (непрофессиональные тесты) и обнаружил, что оригинальный метод занимает половину времени метода кэширования. Похоже, что все эти операции чтения / записи в кэш массива резко замедляют процесс.
Другой способ - изменить всю формулу.
public static int combinations(int group, int members) {
if (members > group - members) {
members = group - members;
}
int answer = 1;
for (int i = group; i > group - members; i--) {
answer *= i;
}
for (int i = 1; i <= members; i++) {
answer /= i;
}
return answer;
}
Опять же, я протестировал новый метод с оригинальным (который я заставил их использовать BigInteger
для теста), и новый метод невероятно быстрее (26 секунд для оригинального метода против 0,00 секунд для последнего для 35 выберите 15 ).
Чтобы добавить немного, я думаю, что сложность времени для использования рекурсивного вызова составляет O((group)(log members))
, тогда как использование новой формулы просто O(members)
.