C++: комбинированные/мультимножественные функции (факториальное переполнение)

Я должен реализовать задачу, которая вычисляет комбинации и мультимножества из m элементов из набора из n элементов. Формулы для них следующие:

введите здесь описание изображения

введите здесь описание изображения

Проблема в том, что с факториалом легко переполниться, так что в основном какие решения могут быть для этой проблемы?

Поскольку это подзадача проблемы в TopCoder, у меня есть следующие ограничения:

1) Программа должна быть написана на C++.

2) Я не могу загружать внешние библиотеки.


person Kudayar Pirimbaev    schedule 27.03.2013    source источник
comment
Перепишите явный термин для C(n,m). Тот, который у вас есть в вашем вопросе, как правило, наиболее удобен для работы в теории, но когда вам действительно нужно его вычислить, есть тот, который не переполняется так быстро.   -  person us2012    schedule 27.03.2013


Ответы (3)


Вам действительно не нужно вычислять n! напрямую, что может легко привести к переполнению. С

C(n,m) = C(n-1, m-1) + C(n-1,m)
C(n,0) = C(n,n) =1

Вы можете создать таблицу размером (n+1,m+1) и использовать динамическое программирование для построения таблицы снизу вверх.

Алгоритм псевдокода может понравиться следующим:

for i ← 0 to n do  // fill out the table row wise
      for j = 0 to min(i, m) do
          if j==0 or j==i then C[i, j] ← 1 
          else C[i, j] ← C[i-1, j-1] + C[i-1, j] 
return C[n, m]

Если вы объявите c(n,m) длинным двойным, а n не таким большим, этот способ должен работать. В противном случае вам нужно определить свой собственный класс BigInteger, чтобы избежать переполнения, и определить, как оператор + работает с BigInteger, которые обычно представлены в виде массива символов или строки.

person taocp    schedule 27.03.2013
comment
должно быть j во втором качестве условия или я что-то упускаю? - person Kudayar Pirimbaev; 27.03.2013
comment
@KudayarPirimbaev извините, опечатка, вторая переменная цикла должна быть j, а не i. Я обновил код. хороший улов, спасибо! - person taocp; 27.03.2013
comment
хорошо, спасибо, не могу поверить, что я не понял этот другой подход - person Kudayar Pirimbaev; 27.03.2013
comment
Мне нравятся эти причудливые маленькие символы . - person Peter Wood; 28.03.2013

Факториалы — довольно большие числа (они не помещаются в 64-битное слово). Поэтому вам нужно использовать bignums (арифметика произвольной точности), чтобы вычислить их полностью. Рассмотрите возможность использования для этой цели GMPlib (или кода на языке и реализации, например Common Lisp с SBCL, что дает их изначально)

См. также это и это ответы на вопрос, очень похожий на ваш.

person Basile Starynkevitch    schedule 27.03.2013
comment
так что, если я не могу использовать внешние библиотеки и должен писать их на С++? так как я писал это в TopCoder, у меня есть некоторые ограничения - person Kudayar Pirimbaev; 27.03.2013
comment
@KudayarPirimbaev: TopCoder позволяет вам использовать как минимум C++, Java, C# и VB. 3 из 4 из них имеют целые числа произвольной длины, встроенные в их стандартную библиотеку, если вы хотите пойти по этому пути. - person cHao; 27.03.2013
comment
так как я знаю только С++, я не могу просто изменить язык, чтобы решить один вопрос, но я знаю, что они созданы, спасибо, просто за расширение моей области программирования, которую я просил с ограничениями, для изучения новых вещей, вы знаете - person Kudayar Pirimbaev; 28.03.2013

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

person Arslan Ali    schedule 27.03.2013
comment
числа переполняются, а не стекаются - person Kudayar Pirimbaev; 27.03.2013