PythonでMapReduceの真似事

MapReduceとはGoogleによって考案され,クラウドコンピューティングの要として注目されたアルゴリズムフレームワークです.Googleによる分散処理の実装自体をMapReduceと呼ぶことも少なくありませんが,アルゴリズムにおける分割統治法のように,並列コンピューティングにおける考え方の枠組みこそがMapReduceであると捉えることができます.Hadoop等のソフトウェアフレームワークを使用せずに,PythonのみでMapReduceを実践してみましょう.問題はオライリーから出版の「並行コンピューティング技法」より,friendly数の計算です.

問題

  • 互いにfriendlyな自然数の組を,プログラム起動時に与えられた正の整数までの範囲で,すべて探索せよ.
  • 2つの数のうち,一方の数のすべての約数の和をその数で除した比率が,もう一方の数を同じように処理した比率と等しい場合を,2つの数が互いにfriendlyであると言い,またこの比率をその数のabundancyと言います.
  • (例)30と140はfriendly

    \frac{1+2+3+5+6+10+15+30}{30} = \frac{72}{30} = \frac{12}{5}
    \frac{1+2+4+5+7+10+14+20+28+35+70+140}{140} = \frac{336}{140} = \frac{12}{5}

実装

#!/usr/bin/env python2.6

import multiprocessing
import fractions
import collections

def abundancy(n):
    numerator = n + 1
    for i in range(2, n/2):
        if n%i == 0:
            numerator += i
    return [[fractions.Fraction(numerator, n), n]]

def find_friend(mapped_value):
    abundancy, values = mapped_value
    num_values = len(values)
    result = []
    for i in range(num_values):
        for j in range(i+1, num_values):
            result.append((values[i], values[j]))
    return result

def partition(mapped_values):
    partitioned_data = collections.defaultdict(list)
    for sublist in mapped_values:
        for key, value in sublist:
            partitioned_data[key].append(value)
    return partitioned_data

def mapreduce(inputs):
    pool = multiprocessing.Pool()
    mapped_values = pool.map(abundancy, inputs)
    partitioned_data = partition(mapped_values)
    reduced_values = pool.map(find_friend, partitioned_data.items())
    return reduced_values

if __name__ == '__main__':
    results = mapreduce(range(1,10000))
    for result in results:
        for a,b in result:
            print '{0} and {1} are friendly'.format(a, b)