使用遗传算法创建通用数学公式

数据挖掘 算法 遗传算法 进化算法 遗传 基因编程
2021-10-08 16:05:42

假设以下所有条件;

  • 我有 4 个已知数字,都在 0-400 范围内,如下所示:
Variable1   Variable2   Variable3   Variable4
0-400       0-400       0-400       0-400
  • 我知道数字之间存在数学关系。

  • 我想使用遗传算法(计算机代码)根据变量 1 和变量 4 估计/近似变量 2 和变量 3。

  • 此外,重要的是,假设有许多输入样本并且每个样本都会略有不同。因此,“数学公式/算法”的遗传算法优化以在所有情况下估计/近似变量 2 和变量 3 成为可能。

  • (换句话说,遗传算法将能够针对许多输入样本中的已知变量 2 和变量 3 优化数学公式,每个样本都有一个相似但略有不同的数学公式。)

然后如何将以下内容写入遗传算法:

Variable2=?
Variable3=?

任何涉及变量 2 和变量 3 的?数学函数 ( ) 可能在哪里+/-/*/:/√/^2/cos/sin/tan/etc.

换句话说; 我希望遗传算法建立一个通用的数学公式。

如何将变量 2 和变量 3 定义为数学公式的结果,以便通过计算机算法进行估计?

我不知道如何解决这个问题。我使用的遗传算法软件可以根据需要使用尽可能多的变量,并且它们可以在不同的范围内。

例如,我可以像这样轻松地编写我的算法;

Variable2=Variable1(op)Variable4
Variable3=Variable1(op)Variable4

其中 Variable1 是遗传算法的第一个变量,范围为0-400,Variable4 是遗传算法的第二个变量,范围为0-400,最后(op)是遗传算法的第三个变量,例如范围为1-4where1代表+,2代表-,3代表*,4_:

然而,这种算法的复杂性是非常有限和粗略的;它没有针对一个好的和复杂的真实估计算法进行优化。此外,一旦引入辅助运算符,例如:

Variable2=[Variable1 or Variable4](op)[Variable1 or Variable4](op)[Variable1 or Variable4]
Variable3=[Variable1 or Variable4](op)[Variable1 or Variable4](op)[Variable1 or Variable4]

对此的编码复杂性将开始迅速增加,并且可能需要使用数学计算()对其进行优先排序等。更复杂计算的编码复杂性似乎变得难以管理。

有没有更好更直接的方法让遗传算法基于变量 1 和变量 4 将变量 2 和变量 3 近似/估计为整体优化的通用数学公式/算法?

2个回答

函数逼近方法称为“符号回归”或“基因表达编程”(GEP)。如果您不必重新发明轮子,那么很少有共享软件(Eureqa、GeneXproTools、DTReg)和一个 python 库。

你可以从这里获得Eureqa的试用版但是,学术版是免费的并且功能齐全。您还可以从这里获得 GeneXproTools 的 DEMO版本或者这里获得DTReg我曾经使用它们几年,但我建议使用 Eureqa。它非常易于使用,可用于分类和回归。所有遗传算法设置都在后台设置,因此您只需选择所需的函数和数学运算。您还可以定义自己的自定义适应度函数。此外,它是多线程的,也可以在AWS云上运行,而且收敛速度非常快。

GeneXproTools 和 DTReg 适用于高级用户,但我个人不推荐它们,尤其是 GeneXproTools。它是单线程的,它的开发似乎从 2015 年就停止了。

如果你可以在 python 中编码,你也可以使用geppy库。Github 存储库在这里我从来没有使用过它,因为我已经用 Eureqa 完成了我需要的任何事情。

最后说明: 如果您不必以编程方式解决问题,请仅使用 Eureqa。

在此处输入图像描述

我建议使用形式的一系列操作, op1(var) op2 op1(var) op2 op1(var) ... 其中op1指的是一元操作并op2指的是二元操作。强制一元运算消除了区分情况。如果您不想在变量前面进行一元运算,只需使用标识函数即可。

我为一元操作的列表/元组的一元组合编写了两个实现。一个在 Haskell 中,一个在 Python 中:

square:: Num a => a -> a
square x = x * x

compose_multiple:: [a -> a] -> (a -> a)
compose_multiple []     = (\x -> x)                    -- for the empty case, use the identity
compose_multiple (f:fs) = f . compose_multiple fs  -- recursive case

这给了我们 256

compose_multiple [square, square, square] 2

在 Python 中:

def compose(op1, op2):
    def result(x):
        return op1(op2(x))
    return result

def compose_multiple(operations: tuple):
    if len(operations) == 1:
            return operations[0]
    return compose(compose_multiple(operations[:-1]), operations[-1])

这给了我们 256

def square(x):
  return x * x
compose_multiple((square, square, square))(2)

二元运算组合的实现应该在一元情况下工作。

最终代码的复杂性可以通过使用中缀表示法来降低(在 Haskell 中使用函数定义中的括号 () 很容易做到这一点)。我没有在 Python 中使用中缀表示法的经验。也许https://pypi.org/project/infix/可能会有所帮助。