在Python中生成随机分支

图片

回忆道金斯,其主要思想可以表达如下:如果长时间将龙卷风放在垃圾桶上,则可以组装一架波音747。一种结构的出现是由于混战者的混乱:对所有无意义和无序的过程中的所有事物进行排序和重新组合,可以看到相当有意义和有序的过程。如果这样的过程是固定的并以某种方式重复,那么这个昨天是布朗运动的系统今天就会开始看起来好像它的行为是由一只看不见的手所建立的,并且它正在采取一些有意义的行动。同时,根本没有手。她准备好了。

为了再次确保这一点,我努力编写某种数字化生活,在没有混乱的情况下,无需人为指示,它就能为自己随机生成逻辑并存在于其自然栖息地-操作系统中。是的,在这种情况下,可能与“人为生活”方向的许多程序有所不同,“人为生活”方向是“生活”在畜栏里,产生“食肉动物”和“草食动物”,并与“食物”和其他食物共存。这些程序均未与系统对象(进程,文件等)进行交互,这意味着该代码并没有真正存在。另外,该代码还是以一种方式仍然执行人需要的某种任务,因此范围非常有限。

为了在操作系统中实现具有很大动作自由度的代码,而这又不仅仅是混乱的可执行指令集,出现了一个由3个模块组成的模型。

  1. 随机生成主要可执行代码的模块
  2. 随机教育模块
  3. OS对象的“计算机视觉”模块

在本文中,我们将讨论第一个模块,到目前为止,这只是随机分支的生成,即像“ if-elif-else”这样的结构。为什么分支?因为总的来说,任何生物的生命都由条件反应组成:我们所做的一切都是对感知信息的反应。如果发生某些情况,细胞会分裂;如果受害者看到强壮的掠食者,则试图逃跑;如果受害者较弱,则可以尝试攻击他;如果灯亮,蟑螂会散开;有人去吃饭,饿了,等等。等等-这排是无尽的。没有独立的,独立的动作,不受任何条件限制。因此,特别是将生物体的行为描述为对以下条件的反应:IF [某物] THEN [某物]。我们正在尝试产生这种行为。

为什么随机?为了离开代码,最大的机会是独立行动并尽可能使人(程序员)远离此过程(理想情况下完全排除在外)。对于程序员来说,后者是最困难的,因为每个人都习惯的标准编程类似于对动物的严格训练,必须严格按照程序员指示的方式执行,就像他在指示时所指示的那样。在这里情况恰好相反:最终生成的代码必须采取行动,以使其生成器的创建者无法预测。

在继续讨论生成器的图和代码之前,有必要详细介绍决策功能,该功能用作导体,允许执行代码的一部分或另一部分。我之前写过关于她的文章在这里。然后被提示我描述了强化学习的思想和约翰·康威的游戏,名为``生命''。我很可能不反对使用已经开发或公开的内容。最后,所有新功能都是已知功能的综合,我本人也承认我采用了Windows中使用的优先处理流程的想法。在这里她很合适。

目前,所提到的功能已进行了一些转换:

def make_solution(p_random, p_deter):                       
    deter_flag = 0
    random_flag = 0
    if p_random >= random.random():
            p_random-=0.01                                  #  
            p_deter+=0.01
            random_flag = 1
    if p_deter >= random.random():
            p_deter-=0.01                                   #  
            p_random+=0.01
            deter_flag = 1
    if random_flag == 1 and deter_flag == 0:
        return(p_random, p_deter, 1)
    elif deter_flag == 1 and random_flag == 0:
        return(p_random, p_deter, -1)
    else:
        return (p_random, p_deter,0)

在输入时,它采用2个概率(默认情况下,它们最初都等于0.5),然后再逐个检查其操作。触发的概率将自身降低1%,同时将其他概率提高1%。因此,每当概率起作用时,它就减小,而其他概率就增加。结果,没有一个概率比另一个具有太大的优势,它们自平​​衡,形成以0.5为中心的正态分布,且工作邻域不超过+ -10%,这使该函数与标准随机数区分开来,在这种情况下,概率它始终等于0.5,并且不依赖于先前的计算。

形象地讲,它是一个振幅很小的概率摆。如果第一个概率起作用而第二个概率不起作用,则返回1,否则返回-1,并且如果两个概率都起作用或不起作用,则返回0。因此,用于2个传入概率make_solution函数将返回3个可能的动作之一,从而获得平衡具有3个可能的继续选项的分叉解决方案。将来,此功能可能会通用,并且可以采用无限数量的概率,因为分叉处的变化可以大于3,但是对于if-elif-else生成器,三个连续选项就足够了。

这里还应该注意的是,在代码中有不同的,可以说是典型的派生。例如,如下所示,在生成器的主要功能中,有一个分叉,其中可以选择一种构造分支的方案,其中只有3种,但是代码中还存在其他情况:插入一个动作块或开始递归,应该生成多少个动作行,应该多么复杂符合条件的put或or和elif否则。

我认为,应该针对每种类型的动作设置我们上面提到的概率摆:然后,仅根据该分叉之前发生的情况来平衡分叉,而不是在代码的其他某些部分中进行平衡。那些。在选择常规分支结构时,我们有一对自己的概率,而在构建其元素时,内部就有另一个概率。

当然,您可以将所有动作用一对平衡,但是每个分叉处的概率将非常困难,并且取决于其他路口的所有先前动作。这样的设计的随机性会更高,但是现在我个人倾向于第一种方案,因为我喜欢其他小的设计在一个大的摆动摆的框架内摆动的设计。较小的余额产生于一个大余额。另外,在第二种方案中,随机性也绰绰有余。

当写分支产生,有必要做不仅可行的代码产生无差错代,而且这样的代码可能生成if-elif-else的最大可能构造,但是没有2或3个这样的可能选项,例如,考虑以下可能的方案。

图片

方案中的图标[..]表示条件或一组随机动作的一组表达式。最基本的方案是1,其中条件简单地通过,然后是动作块。 2a和2b是if与一个elif或另一个的变化。在选项2c中,如果已经与几个elif结合使用,则没有其他选择。最后,在选项2d中,提出了最通用的方案,其中if包含几个elif和else 1。

如果不需要建立无限的分支机构,那么一切都会很简单。在每个if,elif或其他之后,可以调用递归,这又可以进一步递归并在“右边”生成新的elif-else块。让我们看一下可能的选择方案。

图片

实施例2e和2f示出了当在单个小数点之后或在另一个小数点之后调用递归时这种递归分支的简单特殊情况。选项2g描述了这种递归的最复杂和最一般的情况,即在每个省略号之后可以有一个动作块+递归(或立即递归),而在其他情况下也可能发生相同的情况。

当if和if块之后立即进行递归时,仍然存在变化。

图片

这在选项3a和3b中可见。选项3c以最一般的形式显示了这种方案。

这并不是说上述方案涵盖了构建分支的所有可能选项,但是即使以这种形式,最终代码也很容易产生150行的分支,“向右”移动10-15步。无论如何,在必要时使方案复杂化并不困难。

您可以查看一个这样的世代的示例,以确保分支可以非常多样化。

图片

您无需注意条件表达式和操作块的组成-为了直观起见,它们仅由两个变量,3个表达式以及少量算术和逻辑符号的组合生成。真正的重组“肉”的讨论不在本文的讨论范围内(这将在3个模块的讨论中进行讨论)。

在继续直接检查生成器代码之前,有必要记住,生成的块必须水平向右移动(如果是elif,否则,如果是递归或动作块),并且还必须在分支完成后“返回”到左侧。而且,由于Python对于水平缩进非常挑剔,因此希望使步骤相同(在我们的示例中,步骤为3)。

下图说明了位移如何移位。

图片

这里最重要的是,随着分支的加深,位移始终向右移动。但是,例如,如果我们有一个elif-else块,其中有多个elif或单个elif-else对,则需要“返回”浮动在右侧的笔架,以便下一个elif(或其他)以与该块中的上一个偏移量相同。为此,必须保存原始偏移量(wall_offset),并在分支生成结束后(例如,一个elif的完全分支),将其还原。这样可以确保块中的其他元素均匀地“彼此重叠”。此外,每个新块都有其自己的位移。相同的技巧可以在整个if-elif-else构造(包括递归)中实现和谐。

现在让我们继续执行代码。该代码总共约200行,包含8个函数,我们在上面检查了其中之一。由于递归性和传递给函数的大量参数,它在某些地方可读性很差。首先,我将引用用于生成条件表达式和操作块的“肉”。

var_list = ['a','b']
exp_list = ['a+b','b-a', 'b//a']
sign = ['+','-','/','*','//']
sign2 = ['>','<','==','>=','<=','!=']
a = 3
b = 2

如您所见,使用了两个变量:a和b(var_list),它们已初始化,3个算术表达式(exp_list),以及两个带有符号的表(sign,sign2)。如前所述,现在不再需要结果表达式的组成,因此本文不予考虑-主要用于说明代码。还应注意一个特殊之处:在elif-else块的生成中,您需要跟踪else的外观并停止生成,否则else可能会出现在elif之前,这自然会引起错误。fin_else_flag标志用于此目的

我们从主要的生成功能开始考虑。

def if_gen(exp_list, var_list, if_str, offset_koeff, fin_else_flag, prob_list):             
    choice_list = [exp_list, var_list]
    base_offset = ' '
    #   
    prob_list[0],prob_list[1],sol = make_solution(prob_list[0],prob_list[1])       
    # if +   (1   )        
    if sol == 0: 
        #     +3                                                                   
        action_str = action_str_gen(choice_list, offset_koeff+3, prob_list)                 
        return(base_offset*offset_koeff+'if '+ if_sub(exp_list,var_list, sign, prob_list) +':\n' + action_str, offset_koeff, fin_else_flag, prob_list) 
    # if + elif/else (2   )           
    elif sol == -1:                                                                         
        if_str= base_offset*offset_koeff+'if '+ if_sub(exp_list,var_list, sign, prob_list) +':\n' + action_str_gen(choice_list, offset_koeff+3, prob_list) # if [..]:
        #  elif/else
        prob_list[2],prob_list[3],sol2=make_solution(prob_list[2],prob_list[3])             
        if sol2!=0:
            ee_string='elif'
        else:
             ee_string='else'
        #   elif/else
        if_str, offset_koeff, fin_else_flag, prob_list = elif_else_block(ee_string, offset_koeff, exp_list, var_list, sign, if_str, choice_list, fin_else_flag, prob_list)
        return(if_str, offset_koeff, fin_else_flag, prob_list)
    # if + if() (3   )
    else:                                                                                   
            if_str= base_offset*offset_koeff+'if '+ if_sub(exp_list,var_list, sign, prob_list) +':\n' # if [..]:
            #  if/if+ 
            prob_list[4],prob_list[5],sol = make_solution(prob_list[4],prob_list[5])        
            if sol==0:
                #     +3
                if_str+=action_str_gen(choice_list, offset_koeff+3, prob_list)      
            #          
            wall_offset = offset_koeff                                                      
            if_rek, offset_koeff, fin_else_flag, prob_list = if_gen(exp_list, var_list, if_str, offset_koeff+3, fin_else_flag, prob_list) #  if+if
            #    
            if_str+=if_rek   
            #   elif-else/                                                                
            prob_list[4],prob_list[5],sol2=make_solution(prob_list[4],prob_list[5])         
            if sol2!=0:
                prob_list[2],prob_list[3],sol3=make_solution(prob_list[2],prob_list[3])
                if sol3!=0:
                    ee_string='elif'
                else:
                    ee_string='else'
                if_str, offset_koeff, fin_else_flag, prob_list = elif_else_block(ee_string, wall_offset, exp_list, var_list, sign, if_str, choice_list, fin_else_flag, prob_list)  
            else:
                #     +3
                if_str+=action_str_gen(choice_list, offset_koeff+3, prob_list)              
            return(if_str, offset_koeff,fin_else_flag, prob_list)

除了带有“肉”的生成列表(exp_list,var_list)之外,该函数还接受if_str-这是依次收集生成的代码的行。之所以被接受是因为if_gen函数本身可以递归调用,因此建议不要丢失之前生成的代码。offset_koeff

参数是偏移系数,它是具有一个空格的行的系数(base_offset),因此,它负责代码块的水平位移。我们上面讨论fin_else_flag,在这里它只是传递给一个负责生成if + elif / else的函数(请参见下文)。 好吧,还有另一个参数-



prob_list,这是一个包含10个概率(5对概率)的工作表
prob_list = [0.5 for y in range(0,10)] 
并由我们上面讨论make_solution函数使用:与之对应的那对或那对概率与派生类型相对应(例如,主结构派生使用表中的前两个概率:prob_list [0]prob_list [1])。例如,下图中可以看到此工作表中概率变化的结果。

图片

如果在下一代中执行了相应的代码,则此列表中的概率会随着一代而变化。

在函数本身中,嵌套的choice_list会在开始时进行初始化-从“肉”中方便地随机生成表达式,并且在一个空格中将基本偏移量base_offset =”生成该表达式是必需的

之后是主fork,它通过make_solution函数将解决方案放入sol变量中。 Sol采用三个值(0,-1.1)之一并因此根据要构造的方案确定结构。

如果+ [..],则第一个选项实现最简单的选项。答案形成为具有当前偏移量的字符串(不一定等于0!),“ if”字符串,由if_sub函数生成的随机条件(将在后面讨论),回车以及使用action_str函数生成操作块(请参见下文)。 。结果,我们得到如下信息:

if ((a+b)==(b)):
   b=b
   a=b-a
   a=a

第二个选项负责生成此类型:if [..] + elif / else-block(方案中的选项2)。首先,在此处形成一条if + [..]行,然后出现一个elif / else分支,该分支决定将生成ifif -elif还是if-else的elif-else块(e lif_else_block函数-参见下文)。结果可能会有所不同。例如:

if ((a+b)==(a)):
   b=a+b
elif ((b//a)==(a)):
   None
elif ((a+b)<=(a)):
   a=b//a
else:
   if ((b)<=(a)):
      a=b-a
      b=a

if ((a)==(b-a)):
   b=b-a
   b=b
   a=b
   a=b-a
elif ((b)>(b-a))and((a)<(b-a)):
   if ((b//a)<(a)):
      b=b-a
   elif ((a+b)<(b-a))and((b)<(a+b))or((a+b)==(a+b)):
      b=b
      a=b-a
   elif ((a)>(b-a)):
      None

if ((b)<=(b-a))or((a+b)>=(b)):
   a=a
   b=b
elif ((b)<=(b)):
   if ((a)>=(b)):
      a=a+b
      a=b
elif ((b)>=(a)):
   a=b-a
   a=a
   if ((a)>=(b))and((b//a)==(a))and((b//a)!=(b)):
      b=b-a
else:
   a=b//a
   if ((b//a)<(b-a)):
      a=b
      a=b-a
   else:
      if ((a)==(b)):
         a=a
         a=b//a
         b=b
         b=a+b
         b=a
      else:
         None

第三个选项从一开始就实现了递归(方案中的选项3),即 产生以下形式的分支:

if ((a)==(a)):
   if ((a+b)<(b)):

要么
if ((b-a)<=(a)):
   a=a
   if ((b-a)==(b)):
      a=a
      a=a

首先,形成if行(类似),然后出现一个fork,该fork决定是否进一步插入操作块,然后保存偏移量并调用递归。必须保存偏移量,以便在完成递归并返回代码段之后,可以使用if在与原始行相同的偏移量处添加另一个elif-else块。在这里,您可以看到elif和else分支如何与它们的“ native” if处于相同的偏移量。

if ((b-a)==(b)):

   if ((a)>(a+b)):
      if ((b)==(b-a)):
         b=b
         a=a
      elif ((b)>(b)):
         None
      else:
         None
         b=a
         b=b

接下来是elif-else-block / action块中的fork,它决定递归后是添加action块还是elif-else块。如果决定添加一个elif-else块,则类似于上述情况,在方案2中选择elif或else。

在此有必要注意以下事实:以+3的偏移量调用递归以将生成的代码向右移动一步,而elif-else块以wall_offset的偏移量调用,以便该块在递归后不会移到右侧,而是保留为“本机”原始if的偏移量。

结果可能大不相同:从简单到复杂,但是递归的出现立即产生了最华丽的分支。

if ((b-a)>(a+b))and((b)<(a+b)):
   if ((b-a)<=(a+b)):
      b=b//a
   elif ((b)!=(a)):
      a=b-a
else:
   if ((a+b)!=(b-a)):
      a=a

if ((b)<(b-a)):
   if ((a+b)==(b-a))and((b-a)<(a+b))and((b-a)==(a))and((a)>(b//a))or((a+b)>(b//a)):
      if ((b)>=(b-a)):
         a=b
         b=b
         if ((b)>(b)):
            a=a+b
            b=a+b
            a=a
            b=a+b
            b=b//a
            b=a
      else:
         b=a+b
         a=b
         a=b
   elif ((a)<(b-a)):
      a=b//a
      a=b-a

if ((a)>=(b-a))or((a)>=(a))or((b)<=(b)):
   a=a
   a=a
elif ((a)==(a))and((b)>(b-a)):
   a=b//a
   if ((a)<(b)):
      if ((a+b)==(b-a)):
         a=a
         if ((a)!=(b//a)):
            if ((b//a)!=(a))and((b-a)>=(b)):
               a=b
            else:
               None
               a=b//a
      else:
         b=b
         b=a+b
         if ((b-a)<=(b//a)):
            a=b
            a=b
            a=a+b
else:
   a=a+b
   if ((b-a)>=(a)):
      a=b
      if ((b-a)==(a))or((b)!=(b//a)):
         a=b-a
         a=a
         a=a
         a=b//a
         a=a+b
         b=a

现在,让我们看一下elif_else_block函数,该函数负责形成elif-else块,并从主要的if_gen函数调用

def elif_else_block(ee_string, offset_koeff, exp_list, var_list, sign, if_str, choice_list,  fin_else_flag, prob_list):
    if ee_string=='elif':
        sol3 = 9
        #  
        wall_offset = offset_koeff
        #  elif  
        while sol3!=0 and fin_else_flag!=1:
            temp_str, offset_koeff, fin_else_flag, prob_list=elif_else('elif', wall_offset, exp_list, var_list, sign, if_str, choice_list, fin_else_flag, prob_list)
            if_str+=temp_str
            prob_list[6],prob_list[7],sol3 = make_solution(prob_list[6],prob_list[7])
        #  -   else   elif?
        prob_list[2],prob_list[3],sol = make_solution(prob_list[2],prob_list[3])
        if sol!=0:
            #  else,   
            fin_else_flag=1
            temp_str,offset_koeff, fin_else_flag, prob_list=elif_else('else', wall_offset, exp_list, var_list, sign, if_str, choice_list, fin_else_flag, prob_list)
            if_str+=temp_str
        return(if_str,offset_koeff, fin_else_flag, prob_list)
    #  else
    else: 
          temp_str,offset_koeff, fin_else_flag, prob_list=elif_else('else', offset_koeff, exp_list, var_list, sign, if_str, choice_list, fin_else_flag, prob_list)
          if_str+=temp_str
          return(if_str, offset_koeff, fin_else_flag, prob_list)

此函数决定是在代码中添加elif还是elif / else块。她没有决定是否就干脆把别的,而是取决于输入值e e_string,这是她从main函数接收if_gen。首先,elif块是在while循环中生成的,其中检查了2个条件:概率-块中的elif数和fin_else_flag标志取决于它,并且如果它突然打开,则意味着在此之前已连接了其他东西,因此您需要退出循环。

是否使用else和else附加到elif块的决定是由fork使用相同的make_solution函数决定的,如果附加了else,则立即打开fin_else_flag标志停止块生成。

elif和else的直接连接由elif_else函数执行(请参见下文)。这里需要注意的是,在生成elif块时(以及将其附加到其上时),offset wall_offset用于平滑地整体构建该块。

现在考虑elif_else函数

<b>def elif_else(ee_string, offset_koeff, exp_list, var_list, sign, if_str, choice_list, fin_else_flag, prob_list):
    ee_str = ''
    #   else:  elif [..]:
    if ee_string=='else':
        ee_str += ' '*offset_koeff+ee_string + ':\n'
    elif ee_string=='elif':
        ee_str += ' '*offset_koeff+ee_string+' '+if_sub(exp_list, var_list, sign, prob_list) + ':\n'
    #   -None /  +
    prob_list[2],prob_list[3],sol = make_solution(prob_list[2],prob_list[3])
    if sol!=0:
        prob_list[6],prob_list[7],sol2 = make_solution(prob_list[6],prob_list[7])
        if sol2!=0:
            #  
            ee_str+=action_str_gen(choice_list,offset_koeff+3, prob_list)
        else:
            # None
            ee_str+=' '*(offset_koeff+3)+'None\n'
        return(ee_str, offset_koeff, fin_else_flag, prob_list)
    else:
        #   
        prob_list[6],prob_list[7],sol2 = make_solution(prob_list[6],prob_list[7])
        if sol2==0:
            #  
            ee_str+=action_str_gen(choice_list,offset_koeff+3, prob_list)
        #  if_gen
        if_str, offset_koeff,  fin_else_flag, prob_list = if_gen(exp_list, var_list, if_str, offset_koeff+3, fin_else_flag, prob_list)                 
        ee_str+=if_str
        return(ee_str, offset_koeff, fin_else_flag, prob_list)

该功能负责生成elif或其他线条本身,以及这些线条之后的后续动作或递归块。它还使用ee_string变量(包含elif或else),并形成相应的字符串。然后是一个分支,确定下一步将执行什么:(动作块或无),或(动作块或动作块+递归)。在此fork中,分别分为两个子叉,在每种情况下,都会使用适当的参数调用make_solution函数以进行决策。

应该注意的是,当它出现在代码中时if sol!=0,这意味着我们有意使代码的一部分优于另一部分,因为如果sol!= 0,则它等于-1或1,因此,另一段代码执行的频率将降低(仅当sol == 0时)。尤其是在elif_else_block函数中使用了函数,在该函数中,让我们在块中形成更多的elif而不是给elif和else相等的可能性,对我们来说更有利可图。或者,例如,在elif_else函数中当形成一个动作块或None时我们为该选项提供了优势,而不是递归的目的-否则分支会增长到非常不雅的大小。

我们只需要考虑负责条件和动作块中表达式随机生成的函数。就像我在上面说过的,在这个阶段它们并不起决定性作用,这里介绍它们通常是为了显示最终生成的代码是什么样的。但是由于它们已在生成器中使用,因此我们将简要介绍它们。

该函数负责生成action_str动作

def action_str_gen(choice_list, offset_koeff, prob_list):
    sol = 9
    curr_offset = ' '*offset_koeff
    act_str = ''
    while sol!=0:
        act_str+= curr_offset+rand(rand(choice_list[1]))+'='+rand(rand(choice_list))+'\n'
        prob_list[6],prob_list[7],sol = make_solution(prob_list[6],prob_list[7])
    return(act_str)

这里的一切都非常简单:从嵌套列表choise_list(我们记得它由v ar_list(变量列表)和exp_list(表达式列表)组成),此函数由以下形式的一行组成:a = a + b或b = b 。那些。一个表达式被分配给该变量,或另一个变量(包括其自身)。rand函数从列表中随机选择一个元素,此处仅此一个元素是为了不产生可怕的字符串。

def rand(t_list):
    return(t_list[random.randint(0,len(t_list)-1)])

条件的if_sub表达式生成函数看起来更大。

def if_sub(exp_list, var_list, sign, prob_list):
    sub_str = ''
    sol = 9
    choice_list = [exp_list, var_list]
    flag = 0
    while sol!=0:
        prob_list[6],prob_list[7],sol = make_solution(prob_list[6],prob_list[7])
        sub_str+='(('+rand(rand(choice_list))+')'+rand(sign2)+'('+rand(rand(choice_list))+'))'
        if flag == 1 and sol==1:
            sub_str+=')'
            flag=0
        or_and_exp = or_and(prob_list)
        if len(or_and_exp):
            sub_str+=or_and_exp
        else:
            break
        prob_list[6],prob_list[7],sol2 = make_solution(prob_list[6],prob_list[7])
        if sol2 == 1 and (sub_str[-1]=='D' or sub_str[-1]=='R') and flag == 0:
            sub_str+='('
            flag = 1
    
    if sub_str[-1] == '(':
        if sub_str[-2]=='d':
           sub_str=sub_str[0:-4]
        elif sub_str[-2]=='r':
             sub_str=sub_str[0:-3]
        else:
            sub_str=sub_str[0:-1]
    elif sub_str[-1]=='d':
         sub_str=sub_str[0:-3]
    elif sub_str[-1]=='r':
         sub_str=sub_str[0:-2]
    else:
         None
    if flag == 1:
        sub_str+=')'
        return(sub_str)
    else:
        return(sub_str)

它按类型生成表达式:((a)> =(ba))或((a)> =(a))或((b)<=(b))同时,左侧和右侧都可以具有各种选项,并且可以作为单独的变量以及表达式或其组。此处也使用逻辑运算符orand,为方便起见使用or_and_exp函数选择它们

def or_and(prob_list):
    prob_list[8],prob_list[9],sol = make_solution(prob_list[8],prob_list[9])
    if sol==-1:
        return('and')
    elif sol==1:
        return('or')
    else:
        return('')

if_sub 函数的其余部分切断了表达式的多余部分,并在必要时添加括号-我认为在此处考虑这些带有铃鼓的舞蹈是不方便的。

好,仅此而已。例如,您可以启动生成器,如下所示:

var_list = ['a','b']
exp_list = ['a+b','b-a', 'b//a']
sign = ['+','-','/','*','//']
sign2 = ['>','<','==','>=','<=','!=']
a = 3
b = 2       
prob_list = [0.5 for y in range(0,10)]      
while True:
     if_str = ''
     if_str, offset_koeff, fin_else_flag, prob_list = if_gen(exp_list, var_list, if_str, 0,0, prob_list)
     try:
         exec(compile(if_str,'gen','exec'))
         print(if_str)
         input()
         
     except ZeroDivisionError:
         None
     except:
         print('error')
         print(if_str)
         input()

首先,输入包含一个带有probabilities的prob_list,然后在一个无限循环中,调用主函数if_gen并启动生成的生成字符串以供执行。值得单独处理ZeroDivisionError,因为用表达式的这种随机构造将其除以零是很常见的。启动后,只需按Enter键即可显示下一代。大多数情况下,它们将非常简单,但是经常分支,甚至非常分支。好了,在开始时导入random也会很不错;)对于那些不想手工收集所有内容的人,可以从Github下载文件(文件if_gen.py)。

总之,我想说的是,我介绍的代码已经经过数十万代的测试,没有错误,同时它展示了我最终想要看到的if-elif-else方案的整个调色板。一次,由于错误,我给了代码的一部分递归的可能性太高了,我得到了52,000(!)代,并且行了(尽管comp被暂停了30秒)。这也表明算法的可靠性。

可能可以在某个地方更简洁地编写代码,在某个地方进行优化,以另一种方式编写主要功能,但是主要的是,该代码可以工作并每秒产生约250代,我认为这是可以接受的。

我从来没有认为此代码可以自给自足-它只是未来数字有机体的模块,是为研究目的而编写的,因此几乎没有任何实际应用。同时,对于使用上述代码的任何人造成的任何后果,我概不负责。我敦促所有人用刀切面包而不是其他东西。

在下一篇文章中,我们将考虑第二个模块,它将负责经验的随机形成。这个主题有望比if生成器有趣得多,并且我一定会在发布后立即发布结果。

All Articles