一般都是用機器學習、梯度下降或sklearn、pytorch來做函數擬合運算,今天介紹遺傳編程,或稱基因編程/GP,來做這個計算
最終就是構造一棵樹AST,來表示運算的先后、權重:

具體原理可以參考這篇文章:https://blog.csdn.net/ocd_with_naming/article/details/98901749
我們的目標是擬合這個函數:
np.sin(x) + np.log(x)
圖像為:

先來一段java代碼,是加載訓練數據的,x、y的一個list;
private static List<Sample<Double>> load訓練數據()
{
List<Sample<Double>> samples=new ArrayList<>();
samples.add(new DoubleDataSample(new Double[]{ 1.0 , 0.8414709848078965 })); //第一個為x,第二個為y
samples.add(new DoubleDataSample(new Double[]{ 1.1 , 0.9865175398657604 }));
samples.add(new DoubleDataSample(new Double[]{ 1.2000000000000002 , 1.1143606427611812 }));
samples.add(new DoubleDataSample(new Double[]{ 1.3000000000000003 , 1.2259224498846844 }));
samples.add(new DoubleDataSample(new Double[]{ 1.4000000000000004 , 1.3219219666096733 }));
samples.add(new DoubleDataSample(new Double[]{ 1.5000000000000004 , 1.4029600947122192 }));
samples.add(new DoubleDataSample(new Double[]{ 1.6000000000000005 , 1.4695772322872411 }));
samples.add(new DoubleDataSample(new Double[]{ 1.7000000000000006 , 1.5222930615146393 }));
samples.add(new DoubleDataSample(new Double[]{ 1.8000000000000007 , 1.5616342957803144 }));
samples.add(new DoubleDataSample(new Double[]{ 1.9000000000000008 , 1.5881539738598094 }));
//省略很多x/y對
return samples;
}
下面就是整個算法的架子了:
public static void main(String[] args) {
List<Op<Double>> terminals=new ArrayList<>();
terminals.add(Var.of("x", 0)); //由於只有1個自變量,所以這里只有x
//0代表第一個自變量
//如果是向量,則此處可以為x1/0, x2/1, x3/2 以此類推
List<Sample<Double>> samples=load訓練數據();
final ISeq<Op<Double>> OPS = ISeq.of(MathOp.ADD, MathOp.SUB, MathOp.MUL, MathOp.SIN,MathOp.COS, MathOp.LOG); //這些是算法允許使用的操作算子
final ISeq<Op<Double>> TMS = ISeq.of(terminals); //上面的自變量在此處掛接上
final Regression<Double> REGRESSION =
Regression.of(
Regression.codecOf(
OPS, TMS, 5,
t -> t.getGene().size() < 30
),
Error.of(LossFunction::mse), //MSE計算誤差
samples
);
final Engine<ProgramGene<Double>, Double> engine = Engine
.builder(REGRESSION)
.minimizing()
.alterers(
new SingleNodeCrossover<>(0.1),
new Mutator<>())
.build();
final EvolutionResult<ProgramGene<Double>, Double> er =
engine.stream()
.limit(Limits.byExecutionTime(Duration.ofSeconds(5)))
.collect(EvolutionResult.toBestEvolutionResult());
final ProgramGene<Double> program = er.getBestPhenotype()
.getGenotype()
.getGene();
final TreeNode<Op<Double>> tree = program.toTreeNode();
MathExpr.rewrite(tree);
System.out.println("G: " + er.getTotalGenerations());
System.out.println("F: " + new MathExpr(tree));
System.out.println("E: " + REGRESSION.error(tree));
}

