なんだこれは

はてなダイアリーから移転しました。

Combinationを巡る冒険

組合せの計算はすぐに桁あふれするという。
それは、愚直に factorial(n) = n! を実装するからだと思う。
組合せcombination(n,r) = n ! (n -r +1)!/ r!にはfactorialの積がでてくるしね。

でも、ちょっとまった。組み合わせの計算をするときは、そんなふうに計算したかな?こうかな?

combination(5,2) = (5*4)/(2*1)


この式をいろいろと見ていると、

combination(5,2) = (((1*5)/1)*4)/2

と計算できることがわかる。

combination(n,r) = 1 * (n) / (1) * (n-1) / (2) ... * (n-r+1) / (r)

この式を左結合としてみてほしい。

そう、必要な計算だけにしぼってみたら、うまく桁あがりが抑えられる。交互に積、除を繰り返すのがポイントだ。しかもこの順番ですると割り算はかならず、割り切れる。

あとは、そう、combination(n,r) = combination(n,n-r)を使えば、無駄な計算が抑えられるはずだ。

#include <stdio.h>
#include <stdlib.h>
#define COMBUNDEF 0

unsigned long long combination( int n, int r);

unsigned long long combination( int n, int r){
  int i;
  unsigned long long work = 1;

  if( (n < r) || (n < 0) || (r < 0)){
    return COMBUNDEF;
  }
  
  for( i = 0; i < r; i++){
    work = work * (unsigned long long) (n - i);
    work = work / (unsigned long long) (i + 1);
  }

  return work;
}

/* driver for combination */
int main(void){
  int i, j;
  unsigned long long res;
  for(i=1;i<20;i++){
    for(j=0;j<=i;j++){
      res = combination( i, j);
      printf("%d,%d %llu\n",i, j, res); 
    }
  }

  exit(0);
}