在Python中模拟的衰变链

时间:2016-01-19 17:40:02

标签: python physics

我正在尝试制作一个计算机程序,可以模拟氡-222的1,000,000个原子的衰变(进入Pol -ium-218进入Lead-214进入铋-214进入铅-210)。每个原子在一个单位时间后有0.000126的机会腐烂成Pol -218。我有一个1,000,000 0 m的列表分配给变量Rn来表示Radon-222。然后,为了确定特定元素是否衰减,我选择1到1,000,000之间的随机整数:如果此数字小于126,则Rn [j]的值从0切换为1(衰减),并且我们将0添加到在单位时间之后,我预计会有大约126个Pol原子,在另一个单位时间之后,我们应该有大约126个额外的Pol原子,但其中一些可能会腐烂到Lead-214

我们将时间视为此建模中的离散实体。 See here for a list of ideal values after 10 unit times.

这是一个有效的代码,但速度很慢。

import random

def one(s):
    count=0
    for i in range(len(s)):
        if s[i]==1:
            count+=1
    return count

Rn=[1]*1000000
Po=[0]*1000000
Pb214=[0]*1000000
Bi=[0]*1000000
Pb210=[0]*1000000

print([0,1000000,0,0,0,0])

for i in range(1,100):
    for j in range(len(Rn)):
        rndecay=random.random()
        if rndecay<=0.000126:
            Rn[j]=2        

    for j in range(len(Po)):
        if Po[j]==1:
            podecay=random.random()
            if podecay<=0.203:
                Po[j]=2

        if Rn[j]==2 and Po[j]==0:
            Po[j]=1

    for j in range(len(Pb214)):            
        if Pb214[j]==1:
            decay214=random.random()
            if decay214<=0.0255:
                Pb214[j]=2
        if Po[j]==2 and Pb214[j]==0:
            Pb214[j]=1

    for j in range(len(Bi)):
        if Bi[j]==1:
            bidecay=random.random()
            if bidecay<=0.0346:
                Bi[j]=2
        if Pb214[j]==2 and Bi[j]==0:
            Bi[j]=1

    for j in range(len(Pb210)):
        if Bi[j]==2 and Pb210[j]==0:
            Pb210[j]=1

    print([i,one(Rn),one(Po),one(Pb214),one(Bi),one(Pb210)])

如何优化此代码?

3 个答案:

答案 0 :(得分:6)

我认为你可能会以更好的方式解决问题。您可以只跟踪每个物种的原子数,而不是拥有1,000,000个元素列表吗?

具有衰减概率p的n群体中的衰变数遵循具有这些参数的二项分布。许多基本统计数据包将允许您生成遵循分布的随机数(而不是random.random使用的统一分布)。因此,您只需为每个可能的转换提取一个随机值,然后使用结果更新计数。这种方法完全等同于原始帖子中采用的方法。

以下是使用我编制的转换概率计数的示例:

from numpy.random import binomial

atoms = {'Rn222': 1000000,
         'Po218': 0,
         'Pb214': 0,
         'Bi214': 0,
         'Pb210': 0}

for _ in range(100):
    Rn222_Po218 = binomial(atoms['Rn222'], 0.000126)
    Po218_Pb214 = binomial(atoms['Po218'], 0.001240)
    Pb214_Bi214 = binomial(atoms['Pb214'], 0.003450)
    Bi214_Pb210 = binomial(atoms['Bi214'], 0.012046)

    atoms['Rn222'] -= Rn222_Po218
    atoms['Po218'] += Rn222_Po218

    atoms['Po218'] -= Po218_Pb214
    atoms['Pb214'] += Po218_Pb214

    atoms['Pb214'] -= Pb214_Bi214
    atoms['Bi214'] += Pb214_Bi214

    atoms['Bi214'] -= Bi214_Pb210
    atoms['Pb210'] += Bi214_Pb210

print atoms

编辑添加示例。 编辑从评论中添加二项式解释。

答案 1 :(得分:0)

在这种特殊情况下,@ o_o提出的二项式法则非常重要。

如果您想保持代码的精神,可以通过以下几种方式对其进行优化:

  1. 尽可能使用python的内置函数。

    one(s)没用,因为list.count(value)可以胜任。

  2. 限制循环次数。

    你有5个循环for j in ...,但只有一个循环。

  3. data agregation放入函数中。

    在函数中定义循环将加速执行。

  4. 使用elif而不是if

    您可以使用elif避免一些无用的检查。

  5. 此代码比您的代码快2.5倍:

    import random
    
    def doit(step,at):
      global Rn,Po,Pb214,Bi,Pb210
      print([0,1000000,0,0,0,0])
      for i in step:
        compute(at,Rn,Po,Pb214,Bi,Pb210)
        print([i,Rn.count(1),Po.count(1),Pb214.count(1),Bi.count(1),Pb210.count(1)])
    
    def compute(at,Rn,Po,Pb214,Bi,Pb210):
      for j in at:
        if random.random()<=0.000126:
          Rn[j]=2
    
        if Po[j]==1 and random.random()<=0.203:
          Po[j]=2
    
        elif Rn[j]==2 and Po[j]==0:
          Po[j]=1
    
        if Pb214[j]==1 and random.random()<=0.0255:
          Pb214[j]=2
    
        elif Po[j]==2 and Pb214[j]==0:
          Pb214[j]=1
    
        if Bi[j]==1 and random.random()<=0.0346:
          Bi[j]=2
    
        elif Pb214[j]==2 and Bi[j]==0:
          Bi[j]=1
    
        if Bi[j]==2 and Pb210[j]==0:
          Pb210[j]=1
    
    N = 1000000
    Rn   =[1]*N
    Po   =[0]*N
    Pb214=[0]*N
    Bi   =[0]*N
    Pb210=[0]*N
    
    step = range(1,100)
    at = range(N)
    doit(step,at)
    

    我尝试使用列表推导进行优化失败了,但我认为这是可能的。

答案 2 :(得分:0)

感谢您的帮助!我已经能够提出这个使用简单计数并且运行得非常好的代码:

import random

radon=1000000
polonium=0
lead214=0
bismuth=0
lead210=0

for t in range(1,100):
    for i in range(bismuth): 
        pdecay=random.random()
        if pdecay<0.0346:
            bismuth-=1
            lead210+=1

    for i in range(lead214): 
        pdecay=random.random() 
        if pdecay<0.0255:
            lead214-=1
            bismuth+=1

    for i in range(polonium): 
        pdecay=random.random()  
        if pdecay<0.203:
            polonium-=1
            lead214+=1

    for i in range(radon): 
        pdecay=random.random()
        if pdecay<0.000126:
            radon-=1
            polonium+=1

print([t,radon,polonium,lead214,bismuth,lead210])