生成大型列表

时间:2015-08-17 08:07:49

标签: python memory mandelbrot

我是初学者。我最近看到Mandelbrot设置很棒,所以我决定用python绘制这个集合。 但是有一个问题,我得到了记忆错误'当我运行此代码时。

此声明num_set = gen_num_set(10000)将生成一个大型列表,大约20000 * 20000 * 4 = 1600000000.当我使用' 1000'而不是' 10000',我可以成功运行代码。

我的电脑内存为4GB,操作系统为window7 32bit。我想知道这个问题是否是我的计算机的限制,或者有一些方法来优化我的代码。

感谢。

#!/usr/bin/env python3.4

import matplotlib.pyplot as plt
import numpy as np
import random,time
from multiprocessing import *

def first_quadrant(n):
    start_point = 1 / n
    n = 2*n
    return gen_complex_num(start_point,n,1)        

def second_quadrant(n):
    start_point = 1 / n
    n = 2*n
    return gen_complex_num(start_point,n,2)

def third_quadrant(n):
    start_point = 1 / n
    n = 2*n
    return gen_complex_num(start_point,n,3)

def four_quadrant(n):
    start_point = 1 / n
    n = 2*n
    return gen_complex_num(start_point,n,4)

def gen_complex_num(start_point,n,quadrant):
    complex_num = []
    if quadrant == 1:        
        for i in range(n):
            real = i*start_point
            for j in range(n):
                imag = j*start_point
                complex_num.append(complex(real,imag))
        return complex_num
    elif quadrant == 2:
        for i in range(n):
            real = i*start_point*(-1)
            for j in range(n):
                imag = j*start_point
                complex_num.append(complex(real,imag))
        return complex_num
    elif quadrant == 3:
        for i in range(n):
            real = i*start_point*(-1)
            for j in range(n):
                imag = j*start_point*(-1)
                complex_num.append(complex(real,imag))
        return complex_num
    elif quadrant == 4:
        for i in range(n):
            real = i*start_point
            for j in range(n):
                imag = j*start_point*(-1)
                complex_num.append(complex(real,imag))
        return complex_num            

def gen_num_set(n):
    return [first_quadrant(n), second_quadrant(n), third_quadrant(n), four_quadrant(n)]

def if_man_set(num_set):
    iteration_n = 10000
    man_set = []
    z = complex(0,0)
    for c in num_set:
        if_man = 1
        for i in range(iteration_n):
            if abs(z) > 2:
                if_man = 0
                z = complex(0,0)
                break
            z = z*z + c
        if if_man:          
            man_set.append(c)        
    return man_set


def plot_scatter(x,y):
    #plt.plot(x,y)

    color = ran_color()
    plt.scatter(x,y,c=color)
    plt.show()

def ran_num():
    return random.random()

def ran_color():
    return [ran_num() for i in range(3)]

def plot_man_set(man_set):
    z_real = []
    z_imag = []
    for z in man_set:
        z_real.append(z.real)
        z_imag.append(z.imag)
    plot_scatter(z_real,z_imag)


if __name__ == "__main__":
    start_time = time.time()
    num_set = gen_num_set(10000)    
    with Pool(processes=4) as pool:
        #use multiprocess
        set_part = pool.map(if_man_set, num_set)
    man_set = []
    for i in set_part:
        man_set += i
    plot_man_set(man_set)
    end_time = time.time()
    use_time = end_time - start_time
    print(use_time)

2 个答案:

答案 0 :(得分:3)

你说你正在创建一个包含16亿个元素的列表。每个都是一个复数,包含2个浮点数。一个Python复数需要24个字节(至少在我的系统上:sys.getsizeof(complex(1.0,1.0))给出24),所以你只需要超过38GB就可以存储这些值,那就是在你开始查看列表之前本身。

你的16亿个元素的列表根本不适合32位系统(带有4个字节指针的6.4GB),所以你需要转到带有8个字节指针的64位系统,并且需要12.8 GB只是指针。

所以,除非你升级到可能有64GB RAM的64位操作系统,否则你不会这样做(尽管它可能需要更多)。

答案 1 :(得分:0)

处理这样的大数据时,你应该更喜欢使用numpy数组而不是python列表。有一篇很好的文章解释了为什么(Why NumPy instead of Python lists?),但我会尝试总结一下。

在Python中,列表中的每个复数都是一个对象(带有方法和属性),并占用了一些开销空间。这就是为什么他们占用24个字节(如Duncan所指出的)而不是2 * 32bit每个复数的两个浮点数。

Numpy数组构建在c风格的数组上(基本上所有值在内存中彼此相邻地写为原始数字,而不是对象)。它们不提供python列表的一些很好的功能(如追加),并且仅限于一种数据类型。它们可以节省大量空间,因为您不需要保存对象'高架。这将每个复数所需的空间从24个字节减少到8个字节(两个浮点数,每个32位)。

虽然Duncan是对的,你尝试过的大型实例即使使用numpy也无法运行,但它可能会帮助你处理更大的实例。

由于您已经导入了numpy,因此您可以更改代码以使用numpy数组。请注意,我对numpy并不太熟练,而且肯定有更好的方法可以做到这一点,但这只是一个例子,对原始代码只做了很少的改动:

def gen_complex_num_np(start_point, n, quadrant):
    # create a nxn array of complex numbers
    complex_num = np.ndarray(shape=(n,n), dtype=np.complex64) 
    if quadrant == 1:        
        for i in range(n):
            real = i*start_point
            for j in range(n):
                imag = j*start_point
                # fill ony entry in the array
                complex_num[i,j] = complex(real,imag) 
        # concatenate the array rows to 
        # get a list-like return value again
        return complex_num.flatten() 
    ...

这里你的Python列表被替换为数据类型复杂的2d-numpy数组。数组填充后,它将被展平(所有行向量连接)以模仿您的返回格式。

请注意,您必须相应地更改程序所有其他部分中的man_set列表。

我希望这会有所帮助。