【问题标题】:More efficient way to formulate if/elif statement based on outcome of np.random.random?根据 np.random.random 的结果制定 if/elif 语句的更有效方法?
【发布时间】:2021-07-20 20:38:21
【问题描述】:

我正在尝试加速我的代码,该代码具有以下形式的长 if/elif 语句:

random = np.random.random()
tot = a + b + c + d

if random < a/tot:
   var1 += 1
elif a/tot < random < (a+b)/tot:
   var1 -= 1
elif (a+b)/tot < random < (a+b+c)/tot:
   var2 += 1
elif (a+b+c)/tot < random < 1:
   var2 -= 1

我试图想办法用字典来做到这一点,但我不知道如何索引它。代码按原样工作,但是我正在尝试加快这部分代码的速度,这会占用大量运行时间。有没有其他方法可以做到这一点?

【问题讨论】:

  • 一个明显的优化:elif a/tot &lt; random &lt; ...elif (a+b)/tot &lt; random &lt; ...elif (a+b+c)/tot &lt; random &lt; ...,你已经在之前的if/elif 正文中验证了这些,你只需要对每个elif进行第二次比较
  • 最后一个比较不应该是&lt; 1(或者干脆不说)?使用 &lt; tot 可能是错误的。如果(a+b)/tot == random 又会怎样?在您的示例中,没有涵盖这种情况。
  • var2 真的与var1 耦合吗?这样写,每个事件 (a,b,c,d) 是完全相互独立的(它们之间没有交集)
  • 是的@a_guest,这是我的错误。
  • 与其用tot除,不如在if之前用tot多随机一次

标签: python python-3.x if-statement random


【解决方案1】:

您可以使用bisection 来确定您的随机数落在哪个“bin”中:

bins = np.cumsum([0., a, b, c, d])
bins /= bins.sum()

updates_var1 = [1, -1, 0, 0]
updates_var2 = [0, 0, 1, -1]

index = bisect.bisect(bins, random) - 1
var1 += updates_var1[index]
var2 += updates_var2[index]

这个解决方案可以通过使用np.searchsorted而不是bisect.bisect来向量化。

【讨论】:

  • 也许用bins = bins / bins.sum()替换原地除法,这样它也可以处理整数?
  • @MariosMitalidis 我将0. 添加为第一个元素,因此数组将隐式创建为float 类型。
【解决方案2】:

我在这里看到的仅有的几件事是:

  1. 你只需要第二个条件(第一个条件已经在前面的if中检查过了,所以你知道它的反面是真的)

  2. 如果if-else链在循环内,并且a,b,c,d固定在循环内,则提前计算总和和除法(在进入循环之前)

  3. 将最后一个 elif 更改为 else

导致

# if a,b,c,d,tot are FIXED inside some external loop
# you should calculate tot,P1,P2,P3 OUTSIDE the loop
tot = a + b + c + d
P1=a/tot
P2=(a+b)/tot
P3=(a+b+c)/tot

# considering a,b,c,d,tot are fixed inside the loop
# the loop should start here
random = np.random.random()
if random < P1:
   var1 += 1
elif random < P2:
   var1 -= 1
elif random < P3:
   var2 += 1
else:
   var2 -= 1

您可以尝试的另一件事是将 if-else 链放入函数中,并使用 numba、pythran 或 cython 为其生成编译代码。

【讨论】:

  • 如果P1-P3 只被使用一次,为什么要预先计算它们?
  • 我假设 if-else 链在循环内,并且 P1、P2 和 P3 在循环内不会改变...如果这些概率在循环内改变,那么计算就没有意义了他们事先
  • rand_tot = random * tot 然后在几个运动中使用 rand_tot 而不是除以 tot
【解决方案3】:

假设每个项目的可能性相同,那么可能值得这样做:

a_b = a+b
a_b_c = a_b+c
random = np.random.random() * (a_b_c+d)

if a_b < random:
   var1 += 1 if a < random else -1
else:
   var2 += 1 if a_b_c < random else -1

这将导致每次迭代执行两个if 语句,而您的版本对于a 只需要一个,而对于d 需要四个

如果在这里花费大量时间,那么您可能值得做其他事情。例如矢量化事物,或依赖一些分析统计结果,因此您不必抽取尽可能多的样本

【讨论】:

    【解决方案4】:

    这可能是最有效的方法:删除分区

    tot = a + b + c + d
    random = np.random.random() * tot
    
    if random < a:
       var1 += 1
    elif random < a+b:
       var1 -= 1
    elif random < a+b+c:
       var2 += 1
    else:
       var2 -= 1
    

    【讨论】:

      猜你喜欢
      • 2021-04-15
      • 1970-01-01
      • 1970-01-01
      • 2017-09-06
      • 2013-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多