0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Python】組み合わせ計算(nCr)に辞書を使った方が高速な場合について

Last updated at Posted at 2019-08-10

#はじめに

Qiita初投稿です。よろしくお願い致します。

いきなり本題だが、
二項分布を用いたモデリングでの尤度計算にて、
組み合わせの計算 $nCr$ がたくさん登場するらしい。

ところで、0から8までしか値を取らないデータが1000件あって、
それぞれで
$n=8$ の組み合わせ計算 $nCr$ を行う
のは、
無駄なのでは?

同じ計算を100回以上行わせてしまうのでは?
と考えた。

そこで、Pythonの辞書を扱い、
「あらかじめ全体の数$N$までの
全ての整数における組み合わせを計算しておくのはどうだろう?」

と思い、何パターンか時間計測をしてみた。

また初めてのQiitaへの投稿の練習もさせていただきたい。

#組み合わせ計算はscipy.specialを使用

@derodero24 様がまとめてくださった、
【Python】組み合わせ(nCr) 計算の高速化
(https://qiita.com/derodero24/items/91b6468e66923a87f39f)

を参考に、
scipy.special.comb()を使用する。

sample.py
from scipy.special import comb

a=comb(n,r)

また、二項分布の乱数発生は
scipy.stats.binom()を使用。

発生した乱数 $r_i$ それぞれについて、
$nCr_i$ を求め、
辞書未使用と使用でどれだけ計算時間がかかるか
比較していく。

#未使用パターン

(マイブームなので)今回は、numpyのfrompyfuncを使用し、
scipy.special.comb()をグローバル関数化する。

comb_frompyfunc(N,d_size,iter_count)
import numpy as np
from scipy.stats import binom
from scipy.special import comb

def comb_frompyfunc(N,d_size,iter_count):
    array_list=[]
    comb_np=np.frompyfunc(comb,2,1)
    
    for _ in range(iter_count):
        d=binom.rvs(N,p,size=d_size)
        comb_array=comb_np(N,d)
        array_list.append(comb_array)
        
    return array_list

(numpy.vectorize(comb)(N,d)などでも計算できるが、今回は使用しない。また、事前に比較したところ、frompyfuncを使ったほうが、どのパターンでも早かった)

#辞書使用パターン

二項分布による乱数を発生させる前に、
辞書comb_dictに、0からNまでの組み合わせの数を代入し、
np.vectorize(comb_dict.get)(d)
で乱数それぞれの組み合わせの数に変換する。

comb_fromdict(N,d_size,iter_count)
import numpy as np
from scipy.stats import binom
from scipy.special import comb

def comb_fromdict(N,d_size,iter_count):
    array_list=[]
    comb_dict={}

    for i in range(0,N+1):
        comb_dict[i]=comb(N,i)
        
    for _ in range(iter_count):
        d=binom.rvs(N,p,size=d_size)
        comb_array=np.vectorize(comb_dict.get)(d)
        array_list.append(comb_array)
        
    return array_list

辞書の作成方法について、forループを使わずに行う方法を知っていればよかったが、上手く行かなかったり、逆に遅くなったりしたので、今回は妥協。

#計測パターン1 組み合わせ数 < データサイズ

組み合わせ   $N=10$
データサイズ  $d\_size=100$

pattern1.py
N=10
d_size=100
iter_count=1

%timeit comb_frompyfunc(N,d_size,iter_count) #未使用

%timeit comb_fromdict(N,d_size,iter_count) #辞書使用


"""
出力

1000 loops, best of 3: 751 µs per loop
1000 loops, best of 3: 196 µs per loop

"""

辞書使用のほうが3.8倍早くなった。

#計測パターン2 組み合わせ数 = データサイズ

組み合わせ   $N=10$
データサイズ  $d\_size=10$

pattern2_1.py
N=10
d_size=10
iter_count=1

%timeit comb_frompyfunc(N,d_size,iter_count) #未使用

%timeit comb_fromdict(N,d_size,iter_count) #辞書使用


"""
出力

10000 loops, best of 3: 123 µs per loop
10000 loops, best of 3: 158 µs per loop

"""

おおかた予想通りだが、
組み合わせ数とデータサイズが等しい場合は、
辞書を使用したほうが21%ほど遅い。

次は組み合わせ数もデータサイズも1000に変更してみた

組み合わせ   $N=1000$
データサイズ  $d\_size=1000$

pattern2_2.py
N=1000
d_size=1000
iter_count=1

%timeit comb_frompyfunc(N,d_size,iter_count) #未使用

%timeit comb_fromdict(N,d_size,iter_count) #辞書使用


"""
出力

100 loops, best of 3: 6.26 ms per loop
100 loops, best of 3: 6.52 ms per loop

"""

今回も辞書型のほうが遅かったが、
約4%の差に減少している。

#計測パターン3 組み合わせ数 <<< データサイズ

組み合わせ   $N=10$
データサイズ  $d\_size=10000$

pattern3.py
N=10
d_size=10000
iter_count=1

%timeit comb_frompyfunc(N,d_size,iter_count) #未使用

%timeit comb_fromdict(N,d_size,iter_count) #辞書使用


"""
出力

10 loops, best of 3: 59 ms per loop
1000 loops, best of 3: 1.72 ms per loop

"""

辞書使用のほうが34.3倍早くなった。

計測パターン1と比較して、
データサイズが1000倍にはなったが、
時間計測の差は10倍程度しか広がらず。

#まとめ

組み合わせ数含めた計算は、データの量が大きくなればなるほど
pythonの辞書を使用したほうが、早くなることがわかった。

辞書の作成にforループを使う以外で適した方法があれば更に早くなるような気はする。

#numpyによる配列の参照-2019/08/15追記
投稿後、@konandoiruasa様より、
コメントにより、numpy.arrayによる方法で、断然早くなることを
ご教授いただきました。

def comb_fromtable(N,d_size,iter_count):
    comb_table = np.array([comb(N, i) for i in range(N+1)])
    d = binom.rvs(N, p, size=d_size*iter_count)
    return comb_table[d]

ありがとうございます!

numpy.arrayで組み合わせ配列を作ってしまい、
comb_table[d]と指定してしまえば、
私のやりたかったことが一気に実現してしまいました。

また、size = d_size * iter_count と指定してしまえば、
forループやappendをする必要もありませんでした。

これでまた2つ賢くなりました。

Qiitaに記事を投稿してみてよかった瞬間でございました。

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?