PI π 计算加载系统
PI π Calculation loading system
感谢您花时间阅读并(希望)回答我的问题!我最近对 Pi 产生了兴趣(π 不是可食用的类型,我已经很喜欢它了!)并且对这样一个数字的计算很感兴趣,我可以流利地输入中等高级 python 并且是高级 Linux 用户,所以我设置了 "cluster" 台旧电脑。经过一番挖掘,我发现了一个计算 pi 的 python 程序,我对其进行了编辑并将其输出到一个文件中,我 运行 它在其中一台计算机上运行并且效果惊人,目前大约在 2百万位 pi(与 22.7 万亿的世界纪录相比微乎其微!)并且使用了 100% 的两个内核和 94% 的 ram,我唯一的问题是我不能取消这个过程或者我必须重新开始,我是试图理解算法,以便我可以在加载函数中编写代码。加载函数应打开文件并从那里继续计算 pi。我可以稍微理解该算法,并发现它使用已经计算出的 pi 数字来计算数字(这解释了速度衰减),因此可以加载预先计算的数据。代码如下:
import sys
def calcPi():
q, r, t, k, n, l = 1, 0, 1, 1, 3, 3
while True:
if 4*q+r-t < n*t:
yield n
nr = 10*(r-n*t)
n = ((10*(3*q+r))//t)-10*n
q *= 10
r = nr
else:
nr = (2*q+r)*l
nn = (q*(7*k)+2+(r*l))//(t*l)
q *= k
t *= l
l += 2
k += 1
n = nn
r = nr
pi_digits = calcPi()
i = 0
for d in pi_digits:
sys.stdout =open("piDigits.txt", "a")
sys.stdout.write(str(d))
i += 1
if i == 50:
print("")
i = 0
如果有人能帮我找到一种方法来加载预先计算的 pi 数字并从那里继续计算或向我解释 algorithm/code 我将非常感激!
提前致谢。
-里奥·科尼利厄斯
您使用的代码中有一个生成器。这是一个带有 'yield' 语句的函数。它们所做的是在被调用时产生一个值,然后等到它们被再次调用,通常是在循环中,然后计算下一个值然后产生那个值。因为你在无限循环中计算无限数,所以程序会 运行 直到你杀死它,然后你就会失去状态。所以你需要一种方法来持久化状态。
我建议您实现一个迭代器来替换生成器。迭代器就像一个生成器,但它不是一个函数,而是一个对象。所以它有状态。即,您可以将所有这些变量(nr、nn、q 等)的当前值存储为 'instance' 个变量。然后,当你想终止时,你可以使用 'pickle' 库保持 class 的当前状态。然后,为了从中断处继续执行脚本,您加载 pickle 文件以完全按照程序终止前的样子重建对象。
您需要转储并恢复 calcPi
生成器的整个状态。幸运的是,所有状态都在第一行中明确指定。这是向您展示想法的原型:
import sys
import os.path
def calcPi(state=None):
if state is None:
q, r, t, k, n, l = 1, 0, 1, 1, 3, 3
skip_first = False
else:
q, r, t, k, n, l = state
skip_first = True
while True:
if 4 * q + r - t < n * t:
# have to skip the first yield in the "restore state" scenario because
# this is the same we returned the last time from this state
if not skip_first:
state = q, r, t, k, n, l
yield n, state
else:
skip_first = False
nr = 10 * (r - n * t)
n = ((10 * (3 * q + r)) // t) - 10 * n
q *= 10
r = nr
else:
nr = (2 * q + r) * l
nn = (q * (7 * k) + 2 + (r * l)) // (t * l)
q *= k
t *= l
l += 2
k += 1
n = nn
r = nr
initial_state = None
total_digit_cnt = 0
buf_digit_cnt = 0
state_file_name = "piState.txt"
if os.path.isfile(state_file_name):
with open(state_file_name, "r+") as stateF:
lines = stateF.readlines()
if len(lines) > 0:
last_line = lines[-1]
# truncate the old state and save only the few last last lines
stateF.seek(0)
stateF.truncate(0)
stateF.write(lines[-3])
stateF.write(lines[-2])
stateF.write(last_line)
initial_state = map(long, last_line.replace('(', '').replace(')', '').split(','))
total_digit_cnt = initial_state[-1]
initial_state = initial_state[:-1]
if initial_state is not None:
print str((total_digit_cnt, initial_state))
pi_digits = calcPi(initial_state)
buf = ""
state_cnt = 0
with open("piDigits.txt", "a") as outF:
with open(state_file_name, "a+") as stateF:
for digit, state in pi_digits:
buf += str(digit)
buf_digit_cnt += 1
total_digit_cnt += 1
if buf_digit_cnt == 500:
print "Start dumping state %d" % state_cnt
buf_digit_cnt = 0
outF.write(buf)
buf = ""
outF.write("\n")
outF.flush()
# as states take much more space, clear old states
state_cnt += 1
if state_cnt % 50 == 0:
stateF.seek(0)
stateF.truncate(0)
stateF.write(str((state, total_digit_cnt)) + "\n")
stateF.flush()
print "End dumping state %d" % state_cnt
想法是 return 不仅要 return 来自生成器的下一个数字,还要包括整个状态,并定期将其转储到另一个文件中。这只是一个原型。在处理数百万位数字的实际代码中,您可能希望根据自上次转储以来经过的时间转储状态,而不是通过计数来转储状态,因为每个下一个数字的计算可能会变得越来越慢。然而,这会使恢复代码复杂化以跟踪更多细节(例如,最后一行实际写入了多少位数字?)所以我没有将其放入 PoC。
感谢您花时间阅读并(希望)回答我的问题!我最近对 Pi 产生了兴趣(π 不是可食用的类型,我已经很喜欢它了!)并且对这样一个数字的计算很感兴趣,我可以流利地输入中等高级 python 并且是高级 Linux 用户,所以我设置了 "cluster" 台旧电脑。经过一番挖掘,我发现了一个计算 pi 的 python 程序,我对其进行了编辑并将其输出到一个文件中,我 运行 它在其中一台计算机上运行并且效果惊人,目前大约在 2百万位 pi(与 22.7 万亿的世界纪录相比微乎其微!)并且使用了 100% 的两个内核和 94% 的 ram,我唯一的问题是我不能取消这个过程或者我必须重新开始,我是试图理解算法,以便我可以在加载函数中编写代码。加载函数应打开文件并从那里继续计算 pi。我可以稍微理解该算法,并发现它使用已经计算出的 pi 数字来计算数字(这解释了速度衰减),因此可以加载预先计算的数据。代码如下:
import sys
def calcPi():
q, r, t, k, n, l = 1, 0, 1, 1, 3, 3
while True:
if 4*q+r-t < n*t:
yield n
nr = 10*(r-n*t)
n = ((10*(3*q+r))//t)-10*n
q *= 10
r = nr
else:
nr = (2*q+r)*l
nn = (q*(7*k)+2+(r*l))//(t*l)
q *= k
t *= l
l += 2
k += 1
n = nn
r = nr
pi_digits = calcPi()
i = 0
for d in pi_digits:
sys.stdout =open("piDigits.txt", "a")
sys.stdout.write(str(d))
i += 1
if i == 50:
print("")
i = 0
如果有人能帮我找到一种方法来加载预先计算的 pi 数字并从那里继续计算或向我解释 algorithm/code 我将非常感激! 提前致谢。 -里奥·科尼利厄斯
您使用的代码中有一个生成器。这是一个带有 'yield' 语句的函数。它们所做的是在被调用时产生一个值,然后等到它们被再次调用,通常是在循环中,然后计算下一个值然后产生那个值。因为你在无限循环中计算无限数,所以程序会 运行 直到你杀死它,然后你就会失去状态。所以你需要一种方法来持久化状态。
我建议您实现一个迭代器来替换生成器。迭代器就像一个生成器,但它不是一个函数,而是一个对象。所以它有状态。即,您可以将所有这些变量(nr、nn、q 等)的当前值存储为 'instance' 个变量。然后,当你想终止时,你可以使用 'pickle' 库保持 class 的当前状态。然后,为了从中断处继续执行脚本,您加载 pickle 文件以完全按照程序终止前的样子重建对象。
您需要转储并恢复 calcPi
生成器的整个状态。幸运的是,所有状态都在第一行中明确指定。这是向您展示想法的原型:
import sys
import os.path
def calcPi(state=None):
if state is None:
q, r, t, k, n, l = 1, 0, 1, 1, 3, 3
skip_first = False
else:
q, r, t, k, n, l = state
skip_first = True
while True:
if 4 * q + r - t < n * t:
# have to skip the first yield in the "restore state" scenario because
# this is the same we returned the last time from this state
if not skip_first:
state = q, r, t, k, n, l
yield n, state
else:
skip_first = False
nr = 10 * (r - n * t)
n = ((10 * (3 * q + r)) // t) - 10 * n
q *= 10
r = nr
else:
nr = (2 * q + r) * l
nn = (q * (7 * k) + 2 + (r * l)) // (t * l)
q *= k
t *= l
l += 2
k += 1
n = nn
r = nr
initial_state = None
total_digit_cnt = 0
buf_digit_cnt = 0
state_file_name = "piState.txt"
if os.path.isfile(state_file_name):
with open(state_file_name, "r+") as stateF:
lines = stateF.readlines()
if len(lines) > 0:
last_line = lines[-1]
# truncate the old state and save only the few last last lines
stateF.seek(0)
stateF.truncate(0)
stateF.write(lines[-3])
stateF.write(lines[-2])
stateF.write(last_line)
initial_state = map(long, last_line.replace('(', '').replace(')', '').split(','))
total_digit_cnt = initial_state[-1]
initial_state = initial_state[:-1]
if initial_state is not None:
print str((total_digit_cnt, initial_state))
pi_digits = calcPi(initial_state)
buf = ""
state_cnt = 0
with open("piDigits.txt", "a") as outF:
with open(state_file_name, "a+") as stateF:
for digit, state in pi_digits:
buf += str(digit)
buf_digit_cnt += 1
total_digit_cnt += 1
if buf_digit_cnt == 500:
print "Start dumping state %d" % state_cnt
buf_digit_cnt = 0
outF.write(buf)
buf = ""
outF.write("\n")
outF.flush()
# as states take much more space, clear old states
state_cnt += 1
if state_cnt % 50 == 0:
stateF.seek(0)
stateF.truncate(0)
stateF.write(str((state, total_digit_cnt)) + "\n")
stateF.flush()
print "End dumping state %d" % state_cnt
想法是 return 不仅要 return 来自生成器的下一个数字,还要包括整个状态,并定期将其转储到另一个文件中。这只是一个原型。在处理数百万位数字的实际代码中,您可能希望根据自上次转储以来经过的时间转储状态,而不是通过计数来转储状态,因为每个下一个数字的计算可能会变得越来越慢。然而,这会使恢复代码复杂化以跟踪更多细节(例如,最后一行实际写入了多少位数字?)所以我没有将其放入 PoC。