有更好的选择吗

rekjcdws  于 2021-06-26  发布在  Java
关注(0)|答案(3)|浏览(208)

对于下面的问题,Java8提供了更好的选项。
有两个数组,一个整数数组和一个增量数组。将每个增量值应用于整数数组中的元素,然后将每个增量相加,得到增量元素的绝对值之和。

import java.util.Arrays;

public class ArrayChallenge {

    public static void main(String[] args) {
        long[] nA = {-3, -2, 4, 5};
        long[] iA = {2, 4, -6};
        long[] sumArr = findAbsValueSum(nA, iA);
        System.out.println(Arrays.toString(sumArr));

    }

    public static long[] findAbsValueSum(long[] numArr, long[] incrArr) {
        long[] sumArr = new long[incrArr.length];

        for (int i = 0; i < incrArr.length; i++) {
            long sum = 0;
            for (int j = 0; j < numArr.length; j++) {
                sum = sum + Math.abs(numArr[j] + incrArr[i]);
                numArr[j] = numArr[j] + incrArr[i];
            }
            sumArr[i] = sum;
        }
        return sumArr;
    }
}

Result:
[14, 28, 14]

在Java8中有没有更好的选项(性能方面)来实现同样的功能?

fruv7luv

fruv7luv1#

你粘贴的代码显然是高效的,因为它将得到。电脑不是魔法;如果你发现其他语言或图书馆 sumAll 功能,它只是在引擎盖下做这个。
如果你想它更有效,你需要设置规则。限制输入或扩大允许做的事情,这样你可以让这更有效。
例如,如果你告诉我 numArr 是众所周知的提前,因此任何工作所做的转变 numArr 对于不同的、更有效的(对于这个特定的任务)数据类型是“免费的”,因为唯一相关的事情就是尽可能快地一次返回答案 incrArr 如果可用,则:
排序 numArr 到位(免费-可以做不知道incrarr)。
构建增量和数组。此数组是此索引处所有数字的绝对值+以前所有索引的总和; {-3, -2, 4, 5} 变成 {0, 3, 5, 9, 14} . (免费-可以在不知道incrarr的情况下完成)

计算正增量的SUMAB

对于这个例子,假设你的增量( I )是 2 .
首先,对 -I 发生;我们称之为 IDX(-I) . 在这里, IDX(-2) =1(因为 numArr[1]-2 ). 如果 -I 不在列表中,是最近的较小数字(如果列表中没有-2,请查找-3)(成本:o(logn))。
对于这个数字,以及所有的数字 numArr 在这个指数之下,答案是微不足道的:它是所有这些数字的绝对值之和,减去xi。这是 O(1) :这很简单 sumArr[IDX(-I)] - (IDX(-I) * I) .
接下来找到索引0(cost:o(logn))。对于所有0以上的数字,答案同样微不足道。我们首先需要所有正数的和,也就是 sumArr[sumArr.length - 1] - sumArr[idx(0)] ,然后为其中的每个数字加上x
i,类似于我们处理负数的方式。
这就把有趣的部分放在了中间,比如 -1 -它只起作用 1 总计(-1+2=+1)。没有快速的解决方法,因此仅对于输入的这一部分,我们必须遍历它(因此从 IDX(-I)IDX(0) 排他性,做数学。这在技术上是o(n),除了n是严格限制的;永远不会超过 I 除非列表中有重复项(如果有,也可以通过在自由预计算阶段创建权重数组来批量处理这些重复项),而且通常要少得多;它是重叠:输入中所有介于0和-i之间的值。

增量为负

同样的算法也适用,但相反:对于诸如 -6 ,0或以下的所有数字都是微不足道的,6或更高的所有数字也是微不足道的。循环只需要覆盖1到5之间的所有数字。
这会产生一个o(logn)+o(restricted-n)的算法,而不是现有的o(n)算法。在纯数学术语中,它仍然是o(n),但在几乎所有的场景中,它的运算量都要少几个数量级。
构建求和表本身就是o(n),所以如果preptime不是“免费的”,那么这些都没有意义,而且您所描述的是它将获得的最快速度。

nnvyjq4y

nnvyjq4y2#

代替(原始代码段)

sum = sum + Math.abs(numArr[j] + incrArr[i]);
numArr[j] = numArr[j] + incrArr[i];

第一个微优化(切换两行以删除第二行的总和):

numArr[j] = numArr[j] + incrArr[i];
sum = sum + Math.abs(numArr[j]);

以及更多的微观优化(使用复合赋值)

numArr[j] += incrArr[i];
sum = sum + Math.abs(numArr[j]);

更微观的优化(使用变量而不是数组访问)

var n = numArr[j] += incrArr[i];
sum = sum + Math.abs(n);

但是最终jit编译器已经在这样做了(对于更大的数组)。
无法知道这是否真的有帮助,或者差异是否敏感,因为这是微观优化,没有进行测试/计时

7kjnsjlb

7kjnsjlb3#

“流”版本可能如下所示:

public static long[] findAbsValueSumStream(long[] numArr, long[] incrArr) {

    return Arrays.stream(incrArr)
                 .map(inc -> IntStream.range(0, numArr.length)
                                      .mapToLong(i -> Math.abs(numArr[i] += inc))
                                      .sum()
                 )
                 .toArray();
}

更新
可以使用较短的形式:
羔羊 (i -> {long abs = Math.abs(numArr[i] + inc); numArr[i] += inc; return abs;}) 可替换为等效的 (i -> Math.abs(numArr[i] += inc)) 它为给定的测试数据提供相同的输出:

long[] nA = {-3, -2, 4, 5};
long[] iA = {2, 4, -6};
long[] sumArr = findAbsValueSum(nA, iA);
System.out.println("loop: " + Arrays.toString(sumArr));

long[] sumArrStream = findAbsValueSumStream(nA, iA);
System.out.println("stream: " + Arrays.toString(sumArrStream));

输出:

loop: [14, 28, 14]
stream: [14, 28, 14]

但是,流解决方案的性能看起来并不高,因为它使用了类似的嵌套循环。
此外,这里根本不应该使用流,因为其中一个输入数组 numArr 在处理流时被修改,因此它有副作用,不能并行运行以提高性能,因为结果不正确。

相关问题