Java 预计算大型值表

Java 预计算大型值表,java,algorithm,Java,Algorithm,我的程序中有一个数学公式,它接受两个值,都在0和1之间,并且做了大量的工作来找到答案 我还想做相反的事情,也就是说,我想知道什么输入值会产生特定的输出。我不能从分析的角度来做这件事,因为同样的答案可以从大量的输入中得到,而且公式也太复杂了 我的问题是,我目前正在做类似的事情,这需要相当长的时间来计算 for(double i = 0; i <= 1 ; i += 0.0001) for(double j = 0; j <= 1; j+= 0.0001)

我的程序中有一个数学公式,它接受两个值,都在0和1之间,并且做了大量的工作来找到答案

我还想做相反的事情,也就是说,我想知道什么输入值会产生特定的输出。我不能从分析的角度来做这件事,因为同样的答案可以从大量的输入中得到,而且公式也太复杂了

我的问题是,我目前正在做类似的事情,这需要相当长的时间来计算

  for(double i = 0; i <= 1 ; i += 0.0001)
      for(double j = 0; j <= 1; j+= 0.0001)
           answer = formula(i,j); //do the math
           if( Math.abs(answer - answerWanted) < 0.001)
                //close match found

for(double i=0;i为什么不将值存储在数据库中并使用搜索来匹配它。数据库使用索引可以加快搜索速度

假设您有一个以公式和值为列的表,您可以使用范围选择器,例如

select formula, value from pre_computed_values 
    where value >= givenvalue - Epsilon and value <= givenvalue - Epsilon
从预先计算的值中选择公式、值

其中value>=给定值-Epsilon和value另一种选择是使用更智能的搜索算法。最佳选择取决于您的函数,但一个良好的开端可能是Nelder-Mead(下坡单纯形)算法:

这将大大减少计算量。局部极小值可能是某些搜索算法的一个问题,但Nelder Mead可以从许多/大部分算法中得到


如果您发现重复搜索相同的值,还可以添加一个简单的缓存机制。

基本上,您有一个10000×10000的
值数组。如果将其保留在内存中,将占用大约800Mb的Java堆内存

以下是一些可能有帮助的策略:

  • 将数据保存在数据库表中。您可能会获得亚毫秒的访问时间(取决于数据库产品、调优、访问模式等),内存中的缓存会有所改进。假设您存储了
    {i,j,value}
    三元组,则需要对
    {i,j}
    进行索引以进行前向查找,并
    {value}
    用于反函数

  • 如果公式连续且相对平滑,则可以减少存储的数据点数量(例如,减少到1000 x 1000),并使用插值为中间数据点提供近似值

  • 如果公式没有局部极小值和极大值,您可以使用爬山上的变化来计算反函数



在所有这一切中,你需要考虑逆函数不可能是1到1的函数。在多个<代码> {i,j}上可能出现值。

点,以及可能未定义函数的其他值。

公式有多复杂?如果不交替快速递增和递减,您可以将增量值更改为大于.0001的值,然后在知道答案的两个值后,使用连续较小的增量来绑定答案你想要的是介于两者之间


如果您打算用相应的输入编译一个可能结果的列表,我建议您使用哈希表。访问时间是O(1),因此您需要担心的是空间需求和创建表所需的时间。

尝试从Double到
set的哈希映射。


HashMap另一种可能性取决于方程的性质——如果输出值与输入值的关系图不包含不连续性或其他类似的缺陷,您可以预先计算一个更粗糙的数组(避免存储您正在查看的数组的400多兆字节),然后尝试收敛到一个答案

预先计算一个比你所看到的更粗糙的网格,然后尝试通过采取网格大小一半的步进间隔并检查(你必须计算)八个相邻点来细化你的答案。选择最好的,将网格一分为二并重复,直到达到所需的精度。这导致每步计算8次(您始终拥有上一步的中心值),从100x100到分辨率只需7步,总共56次调用您的计算函数

粗糙的网格只需要足够精细,这样你就不会被困在偏离目标的马鞍上

即使在1000x1000网格上,网格的最大容量也为8兆字节,并通过32次计算将其收敛。

您还可以使用它来查找给定输出的函数输入值


hth

可能的输出值范围是多少?类型是什么?输入值是否总是0.0001的倍数?是否需要“+ε”在where子句中?这可能不起作用,因为它是这样。考虑到用矩阵表示的正向函数需要约800Mb,用哈希表表示的逆函数可能需要约10Gb。(如果包含对象头,则键将各占16字节,值将各占16字节,Map.Entry将各占28+字节,哈希数组将有5000-1亿个条目…)我想试试这个,也许如果我降低分辨率,它可能是合理的。但是,我似乎无法让
existing=newset;
工作。如何分配我没有的内存know@StephenC:硬件问题:-)@罗杰:你必须自己定义pair类,因为Java没有这个类……如果需要帮助定义一个可以存储在一个集合中的类,你需要实现一个接口(我相信是可比较的).Oops-如果您使用
Pair
而不是带有
double
字段的自定义
Pair
类,则情况会更糟。现在我们讨论的是每个值约56字节。空间需求约为10Gb。请参见上面的计算。除非公式足够平滑,您可以将所需的数据集减小到可管理的大小,然后捕获回答如上所述。10GB听上去有点大,除非你有足够的空间来杀死。只有两个参数,这将是一个非常糟糕的做法。
// fill in answers
for(double i = 0; i <= 1 ; i += 0.0001)
    for(double j = 0; j <= 1; j+= 0.0001) {
        answer = formula(i,j);
     Set<Double> existing;
        if (Answers.hasKey(answer)) {
          existing = Answers.get(answer);
        }
        else {
          existing = new Set<Pair<Double, Double>>;
       }
       existing.add(new Pair(i, j));
       Answers.set(answer, existing);
    }
}