Комбинаторный 'N выбирают R' в java математике? - PullRequest
56 голосов
/ 04 февраля 2010

Есть ли в java-библиотеке встроенный метод, который может вычислить 'N Choose R' для любого N, R?

Ответы [ 17 ]

0 голосов
/ 12 ноября 2018

Уже представлено много решений.

  1. Некоторое решение не учитывало целочисленное переполнение.

  2. Некоторое решение рассчитало все возможные значения nCr при заданных значениях n и r. Результат требует больше времени и места.

В большинстве случаев нам нужно рассчитать nCr напрямую. Я собираюсь поделиться еще одним решением.

static long gcd(long a, long b) {
    if (a == 0) return b;
    return gcd(b%a, a);
}

// Compute (a^n) % m
static long bigMod(long a, long n, long m) {
    if (n == 0) return 1;
    if (n == 1) return a % m;
    long ret = bigMod(a, n/2, m);
    ret = (ret * ret) % m;
    if (n % 2 == 1) return (ret * a) % m;
    return ret;
}

// Function to find (1/a mod m).
// This function can find mod inverse if m are prime
static long modInverseFarmetsTheorem(long a, long m) {
    if (gcd(a, m) != 1) return -1;

    return bigMod(a, m-2, m);
}

// This function finds ncr using modular multiplicative inverse
static long ncr(long n, long r, long m) {
    if (n == r) return 1;
    if (r == 1) return n;

    long start = n - Math.max(r, n - r) + 1;

    long ret = 1;
    for (long i = start; i <= n; i++) ret = (ret * i) % m;

    long until = Math.min(r, n - r), denom = 1;
    for (long i = 1; i <= until; i++) denom = (denom * i)  % m;

    ret = (ret * modInverseFarmetsTheorem(denom, m)) % m;

    return ret;
}
0 голосов
/ 10 мая 2019

Вместо реализации n выберите k рекурсивно (что может привести к замедлению при больших числах), мы можем также использовать тот факт, что:

                n(n-1)(n-2)...(n-k+1)
  n choose k =  --------------------
                        k!

Нам все еще нужно вычислить k !, но этобыть сделано намного быстрее, чем рекурсивный метод.

private static long choose(long n, long k) {
    long numerator = 1;
    long denominator = 1;

    for (long i = n; i >= (n - k + 1); i--) {
        numerator *= i;
    }

    for (long i = k; i >= 1; i--) {
        denominator *= i;
    }

    return (numerator / denominator);
}

Имейте в виду, что метод выбора выше предполагает, что ни n, ни k не являются отрицательными.Кроме того, длинный тип данных может переполниться для достаточно больших значений.Версия BigInteger должна использоваться, если результат соотв.числитель и / или знаменатель, как ожидается, превысят 64 бита.

0 голосов
/ 28 марта 2018

По формуле: n! / ((N-k)! * K!) Если мы просто вычисляем числитель и знаменатель, многие вычисления будут потрачены впустую, и, возможно, диапазон «int», «float» или даже «BigInteger» может заполниться. Таким образом, чтобы преодолеть этот сценарий, мы можем отменить все до того, как умножим значения.

предположим, что n = 6, k = 3

что означает => 6 * 5 * 4 * 3 * 2 * 1 / ((3 * 2) * (3 * 2))

предположим, что если мы умножим числитель, диапазон может заполниться. Лучший вариант - отменить его, прежде чем даже умножить значения.

В этом случае -> если мы отменим все, что у нас осталось: (2 * 5 * 2)

умножение этих значений намного проще и потребует меньше вычислений.

=============================================== =======

Приведенный ниже код будет работать "эффективно" для чисел, где:

  1. n == k
  2. k
  3. k == 0
  4. разница между n и k слишком велика, например. n = 1000, k = 2
  5. k = n / 2 (САМОЕ ПРОЧНОЕ)
  6. Значение k близко к половине значение n

Возможно, код еще можно улучшить.

BigInteger calculateCombination(int num, int k) {

    if (num == k || k == 0)
        return BigInteger.ONE ;

    int numMinusK = num - k;
    int stopAt; // if n=100, k=2 , can stop the multiplication process at 100*99
    int denominator;

    // if n=100, k=98 OR n=100, k=2 --> output remains same.
    // thus choosing the smaller number to multiply with
    if (numMinusK > k) {
        stopAt = numMinusK;
        denominator = k;
    } else {
        stopAt = k;
        denominator = numMinusK;
    }

    // adding all the denominator nums into list
    List<Integer> denoFactList = new ArrayList<Integer>();
    for (int i = 2; i <= denominator; i++) {
        denoFactList.add(i);
    }

    // creating multiples list, because 42 / 27 is not possible
    // but 42 / 3 and followed by 42 / 2 is also possible
    // leaving us only with "7"
    List<Integer> multiplesList = breakInMultiples(denoFactList);
    Collections.sort(multiplesList, Collections.reverseOrder());

    Iterator<Integer> itr;
    BigInteger total = BigInteger.ONE;
    while (num > 0 && num > stopAt) {

        long numToMultiplyWith = num;
        if (!multiplesList.isEmpty()) {
            itr = multiplesList.iterator();
            while (itr.hasNext()) {
                int val = itr.next();
                if (numToMultiplyWith % val == 0) {
                    numToMultiplyWith = numToMultiplyWith / val;
                    itr.remove();
                }
            }
        }

        total = total.multiply(BigInteger.valueOf(numToMultiplyWith));
        num--;
    }
    return total;

}

ArrayList<Integer> breakInMultiples(List<Integer> denoFactList) {
    ArrayList<Integer> multiplesList = new ArrayList<>();
    for (int i : denoFactList)
        updateListWithMultiplesOf(multiplesList, i);
    return multiplesList;
}

void updateListWithMultiplesOf(ArrayList<Integer> list, int i) {
    int count = 2;
    while (i > 1) {
        while (i % count == 0) {
            list.add(count);
            i = i / count;
        }
        count++;
    }
}
0 голосов
/ 04 декабря 2016
public static void combinationNcK(List<String> inputList, String prefix, int chooseCount, List<String> resultList) {
    if (chooseCount == 0)
        resultList.add(prefix);
    else {
        for (int i = 0; i < inputList.size(); i++)
            combinationNcK(inputList.subList(i + 1, inputList.size()), prefix + "," + inputList.get(i), chooseCount - 1, resultList);

        // Finally print once all combinations are done
        if(prefix.equalsIgnoreCase("")){
            resultList.stream().map(str->str.substring(1)).forEach(System.out::println);
        }
    }
}

public static void main(String[] args) {
    List<String> positions = Arrays.asList(new String[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" });
    List<String> resultList = new ArrayList<String>();
    combinationNcK(positions, "", 3, resultList);
}
0 голосов
/ 29 октября 2016

Использование хеш-карты для улучшения решения @ dimo414:

private static Map<Integer, Map<Integer, Integer>> map = new HashMap<>();
private static int choose(int total, int choose){
    if(total < choose)
        return 0;
    if(choose == 0 || choose == total)
        return 1;

    if (! (map.containsKey(total) && map.get(total).containsKey(choose))){
        map.put(total, new HashMap<>());
        map.get(total).put(choose, choose(total-1,choose-1)+choose(total-1,choose));
    }
    return map.get(total).get(choose);
}
0 голосов
/ 28 августа 2015

Как и в версии с гуавой, существует класс BigIntegerMath здесь от Ричарда Дж. Матара, называемого org.nevec.rjm, который является пакетом классов.

Их реализация предоставляет две сигнатуры для биномиального метода: int, int и BigInteger, BigInteger.

0 голосов
/ 30 июля 2015
public static long nCr(int n, int r) {
    long a = n;
    long b = r;
    long c = (n - r);

    for (int o = (int)a - 1; o > 0; o--) { a = a * o; }
    for (int o = (int)b - 1; o > 0; o--) { b = b * o; }
    for (int o = (int)c - 1; o > 0; o--) { c = c * o; }

    return (a / (b * c)); // n! / r! * (n - r)!
}

Отредактировано из ответа, который я сделал несколько лет назад, когда a, b и c были целыми числами, а целочисленное переполнение сделало метод критически непригодным.На самом деле этот вариант не намного лучше с точки зрения надежности, но он ленивый.

Это также будет зависеть, если значение выходит за пределы длинного лимита ... Не очень выполнимо, если вы не пытаетесь найти какой-то быстрыйрешение для школьного проекта или что-то.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...