在Python中使用多个线程计算阶乘

时间:2017-04-07 08:38:32

标签: python multithreading algorithm

我使用Python 2.7,我有一个任务来编写一个使用多个线程计算阶乘的函数。我尝试使用传统的递归方法来做到这一点,比如

def factorial(n):
    if n < 1:
        return 1
    else:
        return n * factorial(n - 1)

但似乎这种方式并不适用于多线程。有没有办法使用多个线程计算阶乘?

3 个答案:

答案 0 :(得分:4)

在多线程应用程序中,最好尽量减少不同线程之间存在的数据依赖关系。

在你提到的阶乘的递归解决方案中,很难找到不依赖于其他计算结果的计算。

一种独特的方法是将阶乘分为多个部分。 例如,对于两个线程,可以执行以下操作:

n! = [1 * 2 * 3 * .. * (n/2)] * [(n/2 + 1) * ... * n]

第一个线程将计算值:

v1 = 1 * 2 * 3 * .. * (n/2)

第二个线程将计算:

v2 = (n/2 + 1) * ... * n

然后,当两个线程都完成后,主线程将计算n! = v1 * v2

通过将输入因子分成k个不同的部分而不是仅仅两个,可以推广使用k个线程,如上例所示。

答案 1 :(得分:1)

我非常喜欢the idea presented in the other answer

当阶乘为数字[1,n]的computed as the product时:

numbers = range(1,n+1)

您可以使用切片生成工人要处理的数字。例如:

slices = [numbers[i::nworkers] for i in range(nworkers)]
# using n = 10 and nworkers = 3, this produces:
# [[1, 4, 7, 10], [2, 5, 8], [3, 6, 9]]

Map这是process pool,然后减少这些产品的结果以获得最终解决方案。

请勿使用threading模块来实现此功能。这是一个CPU绑定任务,将被Global Interpreter Lock阻止。 multiprocessing模块使用进程而不是线程来支持这一步。

答案 2 :(得分:0)

阶乘只是一系列数字的乘法。由于乘法为associative,因此您可以按任意顺序乘以数字。更具体地说,您可以按照自己喜欢的方式将序列分成任意数量的部分,将部分相乘,然后合并结果。

由于CPython的GIL,你可能最终一次只运行一个线程,但任务的本质可能只是简单地练习线程同步。

threading.Threadqueue.Queue看起来像是工作的正确工具。我将举两个可能的实现示例:

  • 创建一个Queue和一些Thread对象,每个对象都可以
    • 乘以预定义的数字范围,然后
    • 将结果推入队列。
  • 启动主题并等待它们完成
  • 在主线程中,将队列中的所有数字相乘

另一个更有趣的实现是:

  • 将所有数字相乘乘以Queue
  • 创建一些线程,每个线程都

    • 从内部状态1开始
    • 如果可以,请从Queue中取一个数字(即不会阻止)
    • 将状态乘以
  • 这里有趣的部分是队列为空后如何继续:

    • 线程可以简单地将结果推送到其他Queue并退出,留给主线程在所有线程退出后进行最后的乘法
    • 相反,线程可能会将结果推回队列并退出。但是,你需要以某种方式确保在获得最终结果之前并没有所有线程都退出(并推入队列中)。

      • 执行此操作的一种方法是在互锁计数器中跟踪已将多少部分结果推送到队列,并防止线程在检测到要处理最后一个线程时退出:

        #at initialization
        partial_results_lock = threading.Lock()
        partial_results_left=<number of threads>
        
        # in the thread, if the queue is empty
        with partial_results_lock:
            queue.push(state)
            state=1
            partial_results_left-=1
            if partial_result_left!=0: return # after we start over and push
                                              #the final result, the counter will become -1
        <start over>