嵌套循环-保持迭代次数恒定

时间:2019-02-21 22:40:37

标签: python while-loop indices

通过@mypetition的请求,我正在编辑我的问题,尽管我认为此处的天文学细节并不重要。 我有以下格式的文件:

   a          e        q          Q          i        lasc       aper      M          H      dist   comment     i_f      i_h      i_free
45.23710   0.1394   38.93105   51.54315    5.0300    19.9336   286.2554   164.9683   8.41   51.3773   warm     0.000    62.000     4.796
46.78620   0.1404   40.21742   53.35498    3.1061   148.9657   192.3009   337.5967   7.37   40.8789   cold     0.000    42.000     2.473
45.79450   0.1230   40.16178   51.42722    8.0695   104.6470   348.5004    32.9457   8.45   41.3089   warm     0.000    47.000     6.451
42.95280   0.0145   42.32998   43.57562    2.9273   126.3988   262.8777   163.4198   7.36   43.5518   cold     0.000   161.000     2.186

共有1.6e6行。这些是轨道元素。我需要计算每对轨道之间的最小轨道相交距离(MOID),例如,第1行与第2行,第1行与第3行,依此类推,直到到达文件末尾。然后,我从第二行开始,然后转到文件的末尾。然后从第三行开始,再到文件末尾,等等。由于我有1.6e6个轨道,所以将是~1e12个轨道对。

我不想在1个CPU上加载所有这些1e12计算并永远等待,因此我打算使用群集并启动多个串行作业。

我需要遍历1.6e6元素,从第一个元素开始到文件的末尾,然后从第二个元素开始到文件的末尾,依此类推,直到最后开始T-1,然后转到T。这些将导致10^12迭代,我正计划将它们拆分为多个作业,每个作业都进行C=10^7计算,因此我可以在计算机集群上运行它们。
我想出了以下嵌套循环:

for i in range( M, N)
  for j in range( i+1, T)

其中M=1并根据我将要拥有的工作数量而变化。 T=1.6e6是常数(要迭代的行数)。我想找到索引N,因此操作总数为C=10^7。这是我解决问题的方法: [T-(N+1) + T-(M+1)]*(M-N+1)/2=C-因为运算的数目只是上述算术序列的总和。因此,我求解了二次方程式,并且得到了根。这是该代码的python代码:

import numpy as np
import math

C=1.0e7  # How many calculations per job do you want?
T=1.6e6  # How many orbits do you have?
M=1      # what is the starting index of outer loop?
#    N = end index of outer loop (this is to be calculated!)
P=1
l=0
with open('indx.txt','w') as f:

   while P<T:
      l=l+1
      K=np.roots([-1,2*T,M**2-2*T*(M-1)-2*C])
      N=int(round(K[1]))

      f.write("%s %s\n" % (P,P+N))
      M=K[1]+1
      P=P+N+1

但是,保留上述解决方案,更新M = M + N,我注意到不满足条件C = 10 ^ 7。这是前几个索引的列表。

M N

1 7
8 21
22 41
42 67
68 99
100 138
139 183
184 234
235 291
 ....
 ....
1583930 1588385
1588386 1592847
1592848 1597316
1597317 1601791

但是,如果您查看最后一对之前的对,则i=1592848 - 1597316j=i+1, T上的循环将比C=10^7产生更多的计算,即大约(2685 + 7153)* 4468/2〜 2.2e7。

关于如何解决此问题的任何想法,保持C = 1e7不变,这将提供我需要运行的作业数量(具有相似的运行时间),以便迭代1.6e6行。

希望,按照@mypetition标准,此解释已足够,并希望解决该问题。

我们将非常感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

我不知道每个工作的性质是否可以使其适用于其他类型的拆分,但是如果这样做,您可以尝试使用与高斯提出的∑1..n = n相同的技巧。 (n + 1)/ 2

诀窍是用相反的顺序排列序列:

1  2  3  4  5  6  7  8  
8  7  6  5  4  3  2  1 
-- -- -- -- -- -- -- --
9  9  9  9  9  9  9  9  = 8 * 9 is twice the sum so (8*9)/2 = ∑1..8 = 36

基于此,如果您将中间的序列对分开,则会得到4对可处理相同数量元素的运行:

1  2  3  4    
8  7  6  5  
-- -- -- -- 
9  9  9  9  

因此,您将有8次奔跑,分4个工作。每个作业将处理n + 1(9)个元素,并计算两个具有互补数量元素的运行

Job 1 would do run 8..8 and run 1..8 (length 1 and 8)
Job 2 would do run 7..8 and run 2..8 
Job 3 would do run 6..8 and run 3..8
Job 4 would do run 7..8 and run 4..8

更笼统地说:

(N + 1)/ 2个作业i确实运行(N-i + 1).. N和i..N

如果单个运行不能进一步并行化,这应该为您提供最佳的分布(实际上是总处理时间的平方根)

在Python中(伪代码):

size = len(array) 
for index in range((size+1)//2):
    launchJob(array, run1Start=index, run2Start=size-index-1)

注意:如果您不使用基于零的索引,则可能需要调整起点。

note2:如果您不单独处理最后一个元素(即,不包括N..N),则您的工作中将有N个元素要处理,而不是N + 1,因此您必须对该人例外

添加更多作业不会显着改善总处理时间,但是如果您想要更少的并行作业,则仍然可以通过对配对将它们保持相等。

例如2个工作:每个工作[1,8,2,7]和[3,6,4,5] = 18

理想情况下,您的工作数量应该是成对数量的除数。如果不是这样,您仍然可以通过在其他作业上平均分配额外的对(或运行)来获得相对平衡的处理时间。如果您选择分散运行,请在对列表的中间选择它们(因为它们的处理时间彼此接近)。