用Python模拟衰变链

用Python模拟衰变链,python,physics,Python,Physics,我正在尝试制作一个计算机程序,可以模拟100万个氡-222原子(变成钋-218变成铅-214变成铋-214变成铅-210)的衰变。每个原子在一个单位时间后衰变为钋-218的几率为0.000126。我有一个1000000 m的列表分配给变量Rn,以表示Radon-222。然后,为了确定某个元素是否衰变,我选择一个介于1和1000000之间的随机整数:如果这个数字小于126,Rn[j]的值从0变为1(衰变),我们在列表Po中添加一个0表示钋,等等。所以在1个单位时间之后,我预计在另一个单位时间之后,

我正在尝试制作一个计算机程序,可以模拟100万个氡-222原子(变成钋-218变成铅-214变成铋-214变成铅-210)的衰变。每个原子在一个单位时间后衰变为钋-218的几率为0.000126。我有一个1000000 m的列表分配给变量Rn,以表示Radon-222。然后,为了确定某个元素是否衰变,我选择一个介于1和1000000之间的随机整数:如果这个数字小于126,Rn[j]的值从0变为1(衰变),我们在列表Po中添加一个0表示钋,等等。所以在1个单位时间之后,我预计在另一个单位时间之后,大约有126个钋原子,我们应该有大约126个额外的钋原子,但其中一些可能已经衰变为铅214

在这种建模中,我们将时间视为一个离散实体

下面是一个代码,它可以工作,但速度相当慢

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)])
随机导入
def 1(s):
计数=0
对于范围内的i(len(s)):
如果s[i]==1:
计数+=1
返回计数
Rn=[1]*1000000
采购订单=[0]*1000000
Pb214=[0]*1000000
Bi=[0]*1000000
Pb210=[0]*1000000
打印([01000000,0,0,0,0])
对于范围(1100)内的i:
对于范围内的j(len(Rn)):
rndecay=random.random()

如果你能以不同的方式解决这个问题,我想你会更走运。不要有1000000个元素列表,你能跟踪每个物种的原子数吗

衰变概率为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
编辑以添加示例。
编辑以添加注释中的二项式解释。

在这种特殊情况下,@o_o提出的二项式定律确实相关

如果您想保持代码的精神,这里有几种优化方法:

  • 尽量使用python的内置函数。

    one(s)
    是无用的,因为它可以完成工作

  • 限制循环的数量。

    对于j in…
  • 有5个循环,但一个就足够了

  • 输入您的函数。

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

  • 使用
    elif
    而不是
    if

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

  • 此代码比您的代码快约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)
    
    随机导入
    def doit(步骤,at):
    全球注册护士、采购订单、Pb214、Bi、Pb210
    打印([01000000,0,0,0,0])
    对于第i步:
    计算(at、Rn、Po、Pb214、Bi、Pb210)
    打印([i,注册计数(1),采购计数(1),Pb214计数(1),Bi计数(1),Pb210计数(1)])
    def计算(at、Rn、Po、Pb214、Bi、Pb210):
    对于j in at:
    
    如果随机。随机()谢谢大家的帮助!我已经能够想出这个代码,它使用了一个简单的计数,并且运行得非常好:

    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])
    
    随机导入
    氡=1000000
    钋=0
    铅214=0
    铋=0
    铅210=0
    对于范围(1100)内的t:
    对于范围内的i(铋):
    pdecay=random.random()
    
    如果pdecayDon在模拟期间未打印。打印速度很慢,对
    one
    的五次调用也很慢,因为它们每次迭代超过一百万个元素。为什么有数百万个相同的元素而不是计数?@Basic我想模拟现实生活中的随机性,每个“原子”都有一个随机概率(不是完全随机的,而是在给定概率附近)腐烂的迹象。我不知道计数是怎么做到的?@jlammy它遵循二项分布,请看我的回答:)@jlammy好吧,你应该执行相同的检查,除了不是迭代一百万个元素的列表,而是从一百万迭代到一百万,然后计算衰减的数。然后更新(旧状态)计数以减去该数字并将其添加到(新状态)计数。您正在执行相同的检查(字面意思),但您只是节省了一整块内存。当然,你也可以对整个集合中的衰变次数进行近似,得到不可区分的结果,但这显然不太“现实”,我加了一个例子;希望这会有所帮助。