我是初学者。我最近看到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)
答案 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
列表。
我希望这会有所帮助。