ここでは、NCRを見つけるためにC++でプログラムを作成しようとしています。しかし、結果には問題があります。不正解です。プログラムの間違いを見つけるのを手伝ってくれませんか?
#include <iostream>
using namespace std;
int fact(int n){
if(n==0) return 1;
if (n>0) return n*fact(n-1);
};
int NCR(int n,int r){
if(n==r) return 1;
if (r==0&&n!=0) return 1;
else return (n*fact(n-1))/fact(n-1)*fact(n-r);
};
int main(){
int n; //cout<<"Enter A Digit for n";
cin>>n;
int r;
//cout<<"Enter A Digit for r";
cin>>r;
int result=NCR(n,r);
cout<<result;
return 0;
}
あなたの式は完全に間違っています、それはfact(n)/fact(r)/fact(n-r)
であるはずですが、それは今度はそれを計算する非常に非効率的な方法です。
複数カテゴリの組み合わせの高速計算 を参照してください。特に、その質問に対する私のコメントを参照してください。 (ああ、またその質問をもう一度開いてください。正しく答えられるように)
シングルスプリットケースは実際には非常に簡単に処理できます。
unsigned nChoosek( unsigned n, unsigned k )
{
if (k > n) return 0;
if (k * 2 > n) k = n-k;
if (k == 0) return 1;
int result = n;
for( int i = 2; i <= k; ++i ) {
result *= (n-i+1);
result /= i;
}
return result;
}
結果が適合しない場合は、対数の合計を計算して、組み合わせの数を正確にdoubleとして取得できます。または、任意精度の整数ライブラリを使用します。
Ideone.comが最近コードスニペットを失っており、他の質問はまだ新しい答えに向けられていないため、ここで他の密接に関連する質問に私のソリューションを配置します。
#include <utility>
#include <vector>
std::vector< std::pair<int, int> > factor_table;
void fill_sieve( int n )
{
factor_table.resize(n+1);
for( int i = 1; i <= n; ++i )
factor_table[i] = std::pair<int, int>(i, 1);
for( int j = 2, j2 = 4; j2 <= n; (j2 += j), (j2 += ++j) ) {
if (factor_table[j].second == 1) {
int i = j;
int ij = j2;
while (ij <= n) {
factor_table[ij] = std::pair<int, int>(j, i);
++i;
ij += j;
}
}
}
}
std::vector<unsigned> powers;
template<int dir>
void factor( int num )
{
while (num != 1) {
powers[factor_table[num].first] += dir;
num = factor_table[num].second;
}
}
template<unsigned N>
void calc_combinations(unsigned (&bin_sizes)[N])
{
using std::swap;
powers.resize(0);
if (N < 2) return;
unsigned& largest = bin_sizes[0];
size_t sum = largest;
for( int bin = 1; bin < N; ++bin ) {
unsigned& this_bin = bin_sizes[bin];
sum += this_bin;
if (this_bin > largest) swap(this_bin, largest);
}
fill_sieve(sum);
powers.resize(sum+1);
for( unsigned i = largest + 1; i <= sum; ++i ) factor<+1>(i);
for( unsigned bin = 1; bin < N; ++bin )
for( unsigned j = 2; j <= bin_sizes[bin]; ++j ) factor<-1>(j);
}
#include <iostream>
#include <cmath>
int main(void)
{
unsigned bin_sizes[] = { 8, 1, 18, 19, 10, 10, 7, 18, 7, 2, 16, 8, 5, 8, 2, 3, 19, 19, 12, 1, 5, 7, 16, 0, 1, 3, 13, 15, 13, 9, 11, 6, 15, 4, 14, 4, 7, 13, 16, 2, 19, 16, 10, 9, 9, 6, 10, 10, 16, 16 };
calc_combinations(bin_sizes);
char* sep = "";
for( unsigned i = 0; i < powers.size(); ++i ) {
if (powers[i]) {
std::cout << sep << i;
sep = " * ";
if (powers[i] > 1)
std::cout << "**" << powers[i];
}
}
std::cout << "\n\n";
}
N Choose Rの定義は、2つの積を計算し、一方を他方で除算することです。
(N * N-1 * N-2 * ... * N-R + 1)/(1 * 2 * 3 * ... * R)
ただし、乗算が非常に速くなりすぎて、既存のデータ型をオーバーフローする可能性があります。実装の秘訣は、乗算と除算を次のように並べ替えることです。
(N)/ 1 *(N-1)/ 2 *(N-2)/ 3 * ... *(N-R + 1)/ R
各ステップで結果が割り切れることが保証されます(連続するn個の数値の場合、それらの1つはnで割り切れる必要があるため、これらの数の積です)。
たとえば、Nは3を選択し、N、N-1、N-2の少なくとも1つは3の倍数となり、Nは4を選択し、N、N-1、N-2、Nの少なくとも1つを選択します。 -3は4の倍数になります。
以下に示すC++コード。
int NCR(int n, int r)
{
if (r == 0) return 1;
/*
Extra computation saving for large R,
using property:
N choose R = N choose (N-R)
*/
if (r > n / 2) return NCR(n, n - r);
long res = 1;
for (int k = 1; k <= r; ++k)
{
res *= n - k + 1;
res /= k;
}
return res;
}
N-choose-kを実装する良い方法は、階乗に基づくのではなく、階乗に密接に関連する「立ち上がり積」関数に基づくことです。
Rising_product(m、n)は、m *(m + 1)*(m + 2)* ... * nを乗算します。n> = mやn <= 1などのさまざまなコーナーケースを処理するためのルールが適用されます。
Cで記述されたインタープリター型プログラミング言語の組み込み関数としてのnCkおよびnPkの実装については、 ここ を参照してください。
static val rising_product(val m, val n)
{
val acc;
if (lt(n, one))
return one;
if (ge(m, n))
return one;
if (lt(m, one))
m = one;
acc = m;
m = plus(m, one);
while (le(m, n)) {
acc = mul(acc, m);
m = plus(m, one);
}
return acc;
}
val n_choose_k(val n, val k)
{
val top = rising_product(plus(minus(n, k), one), n);
val bottom = rising_product(one, k);
return trunc(top, bottom);
}
val n_perm_k(val n, val k)
{
return rising_product(plus(minus(n, k), one), n);
}
このコードは、+
や<
などの演算子を使用していません。これは、ジェネリック型であるためです(型val
は、「bignum」を含むさまざまな種類の数値など、あらゆる種類の値を表します)整数)、Cで書かれているため(オーバーロードなし)、それが中置構文を持たないLISPのような言語の基礎であるためです。
それにもかかわらず、このn-choose-k実装は、従うのが簡単な単純な構造を持っています。
凡例:le
:以下。 ge
:以上。 trunc
:除算を切り捨てます。 plus
:加算、mul
:乗算、one
:数値1のval
型付き定数。
double
の代わりにint
を使用してください。
更新:
あなたの式も間違っています。 fact(n)/fact(r)/fact(n-r)
を使用する必要があります
この線
else return (n*fact(n-1))/fact(n-1)*fact(n-r);
する必要があります
else return (n*fact(n-1))/(fact(r)*fact(n-r));
あるいは
else return fact(n)/(fact(r)*fact(n-r));
ここでは再帰関数が正しく使用されていません。 fact()
関数は次のように変更する必要があります。
_int fact(int n){
if(n==0||n==1) //factorial of both 0 and 1 is 1. Base case.
{
return 1;
}else
return (n*fact(n-1));//recursive call.
_
};
else
部分で再帰呼び出しを行う必要があります。
NCR()
関数は次のように変更する必要があります。
_int NCR(int n,int r){
if(n==r) {
return 1;
} else if (r==0&&n!=0) {
return 1;
} else if(r==1)
{
return n;
}
else
{
return fact(n)/(fact(r)*fact(n-r));
}
};
_
これは、競合プログラミングでnCrを解決する際に時間制限を超えないようにするための参照です。私はこれを投稿します。これは、あなたの質問の回答がすでに得られているため、uに役立つためです。二項係数の素因数分解を取得することは、おそらく最も特に乗算が高価な場合、それを計算する効率的な方法。これは、階乗の計算に関連する問題にも当てはまります(たとえば、 ここをクリック を参照してください)。
以下は、素因数分解を計算するエラトステネスのふるいに基づく単純なアルゴリズムです。基本的には、ふるいを使って素数を見つけるときに素数を調べるだけでなく、[1、k]と[n-k + 1、n]の範囲にある倍数の数を計算するという考え方です。 Sieveは基本的にO(n\log\log n)アルゴリズムですが、乗算は行われません。素因数分解が見つかると、実際に必要な乗算の数は最低でもO\left(\ frac {n\log\log n} {\ log n}\right)であり、おそらくそれよりも速い方法があります。
prime_factors = []
n = 20
k = 10
composite = [True] * 2 + [False] * n
for p in xrange(n + 1):
if composite[p]:
continue
q = p
m = 1
total_prime_power = 0
prime_power = [0] * (n + 1)
while True:
prime_power[q] = prime_power[m] + 1
r = q
if q <= k:
total_prime_power -= prime_power[q]
if q > n - k:
total_prime_power += prime_power[q]
m += 1
q += p
if q > n:
break
composite[q] = True
prime_factors.append([p, total_prime_power])
print prime_factors