Java代码优化会导致数值不准确和错误

Java代码优化会导致数值不准确和错误,java,algorithm,optimization,math,numerical-stability,Java,Algorithm,Optimization,Math,Numerical Stability,我试图在Java中实现一个版本的,我试图通过只计算一次来进行一些优化,所有可以只计算一次的东西 这是一个迭代算法,关于矩阵的更新,像素x集群成员矩阵U(一行中的值之和必须为1.0),这是我要优化的更新规则: 其中x是矩阵x(像素x特征)的元素,v属于矩阵v(簇x特征)。而m是一个范围从1.1到无限的参数,c是簇数。使用的距离是欧几里德范数 如果我必须以平庸的方式实施这个公式,我会: for(int i = 0; i < X.length; i++) { i

我试图在Java中实现一个版本的,我试图通过只计算一次来进行一些优化,所有可以只计算一次的东西

这是一个迭代算法,关于矩阵的更新,像素x集群成员矩阵
U
(一行中的值之和必须为1.0),这是我要优化的更新规则:

其中x是矩阵
x
(像素x特征)的元素,v属于矩阵
v
(簇x特征)。而
m
是一个范围从
1.1
无限
的参数,
c
是簇数。使用的距离是欧几里德范数

如果我必须以平庸的方式实施这个公式,我会:

    for(int i = 0; i < X.length; i++)
    {
        int count = 0;
        for(int j = 0; j < V.length; j++)
        {
                double num = D[i][j];
                double sumTerms = 0;
                for(int k = 0; k < V.length; k++)
                {
                     double thisDistance = D[i][k];
                     sumTerms += Math.pow(num / thisDistance, (1.0 / (m - 1.0)));
                }
                U[i][j] = (float) (1f / sumTerms);
         }
     }
其中,我在计算距离时,将分母预计算到矩阵
D
的附加列中:

    for (int i = 0; i < X.length; i++)
    {
        for (int j = 0; j < V.length; j++)
        {
            double sum = 0;
            for (int k = 0; k < nDims; k++)
            {
                final double d = X[i][k] - V[j][k];
                sum += d * d;
            }

            D[i][j] = sum;
            D[i][B.length] += Math.pow(1 / D[i][j], exp);
        }
    }
for(int i=0;i
通过这样做,我遇到了“平庸”计算和第二个计算之间的数值差异,这导致
U
中的数值不同(不是在第一次迭代中,而是很快)。我猜问题在于,将非常小的数字按指数乘以高值(
U
的元素范围为0.0到1.0,
exp
,因为
m=1.1
,is
10
)会导致非常小的值,然而,通过将分子和分母相除并然后求幂,结果在数值上似乎更好。问题是它涉及更多的操作

更新

我在迭代0时得到的一些值:

这是矩阵
D
未优化的第一行:

384.663244482.72717379.0881245.4205

这是优化方式下矩阵
D
的第一行(请注意,最后一个值是预计算的分母):

384.6657 44482.7215 17379.0847 1245.4225 1.4098E-26

这是未优化的
U
的第一行:

0.99999213 2.3382613E-21 2.8218658E-17 7.900302E-6

这是
U
优化的第一行:

0.9999921 2.338395E-21 2.822035E-17 7.900674E-6

迭代1

这是矩阵
D
未优化的第一行:

414.3861 44469.39 17300.092 1197.7633

这是优化方式下矩阵
D
的第一行(请注意,最后一个值是预计算的分母):

414.3880 44469.38 17300.090 1197.7657 2.0796E-26

这是未优化的
U
的第一行:

0.99997544.9366603E-21 6.216704E-17 2.4565863E-5

这是
U
优化的第一行:

0.3220644 1.5900239E-21 2.0023086E-17 7.912171E-6

最后一组值表明,由于传播的错误(我仍然希望我犯了一些错误),它们非常不同,甚至违反了这些值的总和必须为1.0的约束


我做错什么了吗?是否有一个可能的解决方案,使代码优化和数值稳定?任何建议或批评都将不胜感激。

此问题与浮点稳定性无关

在第二次迭代和后续迭代中,您会得到错误的分母值,因为您忘记在累积和之前清除其单元格


迭代1的正确分母是
6.697905e-27
,也就是说几乎是
2.0796E-26-1.4098E-26

快速评论在看这个之前,您的公式有xj vi和xj vk,但是您的平庸算法使用D[i][j]和D[i][k]。是我遗漏了什么,还是第二个应该是D[j][k]?你是对的,它们是不同的,但并不影响结果。我会用一个不同的公式来更精确地解释,谢谢你指出这一点。我不想惹人生气,只是想确保我能理解;但是按照前面的评论上面的公式是求和的平方;平庸的准则似乎没有这样做;优化后的代码似乎是分母而不是分子的平方。优化后的代码使用V[j][k]而不是V[i][k]。我猜你公式中的c代表V的大小;为了清楚起见,最好将范围从k=0改为k=1。什么是B和B.length?为什么使用+=?数字差异?您有示例值吗?对浮点运算进行重新排序几乎总是会导致“微小”的差异。我看到的和aepryus看到的一样,但最终你的问题是关于数学,而不是关于算法。我的第一个想法是你遇到了ISO十进制数学问题。用java.util.BigDecimal检查一下——这会扼杀你的性能,但你应该看到数字的稳定性——如果是这样,你就必须开始权衡权衡了。我现在觉得自己太愚蠢了。我应该得出你的结论的。谢谢你把时间花在我愚蠢的工作上
    for (int i = 0; i < X.length; i++)
    {
        for (int j = 0; j < V.length; j++)
        {
            double sum = 0;
            for (int k = 0; k < nDims; k++)
            {
                final double d = X[i][k] - V[j][k];
                sum += d * d;
            }

            D[i][j] = sum;
            D[i][B.length] += Math.pow(1 / D[i][j], exp);
        }
    }