【问题标题】:Nested loops-keep the number of iterations constant嵌套循环——保持迭代次数不变
【发布时间】:2019-02-21 22:40:36
【问题描述】:

应@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 元素,我从第一个元素开始并到文件末尾,然后从第二个元素开始并到文件末尾等,直到最后我从 @ 开始987654324@ 并转到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 标准,这个解释就足够了,我希望能解决问题。

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

【问题讨论】:

  • 您能否尝试重新表述您面临的问题?不太清楚你在问什么。
  • 用您的说明编辑您的问题。请勿将其发布在 cmets 中。

标签: python while-loop indices


【解决方案1】:

我不知道每项工作的性质是否适合不同类型的拆分,但如果可以,您可以尝试使用 Gauss 用来提出 ∑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  

您将在 4 个作业中进行 8 次运行。每个作业将处理 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 个

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

【讨论】:

  • 感谢您的帮助。我的意思是串行运行我的工作,而不是并行运行。因此,对于每项工作,我将有 i=M, N, J=i+1, T 我希望每个工作具有相似数量的计算,例如1e7。例如,第一个作业将是这样的:i=1,8, J=i+1, 1.6e6 将给出大约 1e7 的计算。第二份工作应该从 9 开始,即 i=9,X, J=i+1, 1.6e6 等等,直到 M
猜你喜欢
  • 2017-07-03
  • 2014-10-22
  • 2013-05-09
  • 1970-01-01
  • 2018-05-23
  • 1970-01-01
  • 2016-05-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多