简体   繁体   English

使用 Arrays Java - 任何更好的选择

[英]Working with Arrays Java - Any better options available

Any better options available in java 8 for the below problem. java 8 中针对以下问题提供的任何更好的选项。

There are two arrays, one integral array and one incremental array.Apply each of the increment values to the elements in integral array and get the sum of absolute values of the elements of incremental after adding each increment.有两个arrays,一个整数数组和一个增量数组。将每个增量值应用到整数数组中的元素,得到每个增量后增量元素的绝对值之和。

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]

Is there any better options (Performance wise) to do the same in java 8?在 java 8 中是否有更好的选择(性能方面)?

The code you pasted is obviously as efficient as it is going to get.您粘贴的代码显然与将要获得的一样有效。 Computers aren't magic;计算机不是魔法。 if you find some other language or library that has a sumAll function, it'd just be doing this under the hood.如果您发现其他一些语言或库具有sumAll function,那么它只是在后台执行此操作。

If you want it to be more efficient, you need to setup rules.如果你想让它更有效率,你需要设置规则。 Restrict the input or widen the things one is allowed to do, then you can get this to be more efficient.限制输入或扩大允许做的事情,然后你可以让它更有效率。

For example, if you tell me that numArr is known well in advance and therefore any work done to transform numArr into different, more efficient (for this specific task) data types is 'free', because the only thing that is relevant is to return the answer as fast as possible once an incrArr is available, then:例如,如果您告诉我numArr是事先众所周知的,因此任何将numArr转换为不同的、更有效(对于此特定任务)数据类型的工作都是“免费的”,因为唯一相关的是返回一旦incrArr可用,就尽快回答,然后:

  1. Sort numArr in place.numArr进行就地排序。 (free - can be done without knowing incrArr). (免费 - 可以在不知道 incrArr 的情况下完成)。
  2. Build an incremental sum array.构建一个增量总和数组。 This array is the sum of the absolute value of all numbers at this index + all previous indices;该数组是该索引处所有数字的绝对值之和+所有先前索引; {-3, -2, 4, 5} turns into {0, 3, 5, 9, 14} . {-3, -2, 4, 5}变成{0, 3, 5, 9, 14} (free - can be done without knowing incrArr) (免费 - 可以在不知道 incrArr 的情况下完成)

To calculate the sumAbs for a positive increment计算正增量的 sumAbs

For this example, let's say your increment ( I ) is 2 .对于此示例,假设您的增量 ( I ) 为2

  1. First, do a binary search for the index at which -I occurs;首先,对出现-I的索引进行二分查找; we shall call this IDX(-I) .我们称之为IDX(-I) Here, IDX(-2) = 1 (because numArr[1] is -2 ).这里, IDX(-2) = 1 (因为numArr[1]-2 )。 If -I isn't in the list, the nearest smaller number (Had -2 not been in your list, find -3 instead).如果-I不在列表中,则为最接近的较小数字(如果 -2 不在您的列表中,则改为查找 -3)。 (cost: O(logn)). (成本:O(logn))。
  2. For this number, and all numbers in numArr below this index, the answer is trivial: It is the sum of the absolute value of all those numbers, minus X*I.对于这个数字,以及numArr中低于该索引的所有数字,答案很简单:它是所有这些数字的绝对值之和,减去 X*I。 This is O(1) : it is simply sumArr[IDX(-I)] - (IDX(-I) * I) .这是O(1) :它只是sumArr[IDX(-I)] - (IDX(-I) * I)
  3. Next find the index of 0 (cost: O(logn)).接下来找到0的索引(成本:O(logn))。 For all numbers 0 and up, the answer is again trivial.对于所有 0 及以上的数字,答案又是微不足道的。 We need the sum of all positive numbers first, which is sumArr[sumArr.length - 1] - sumArr[idx(0)] , then add X*I to this for each number in it, analogous to how we handled the negative numbers.我们首先需要所有正数的总和,即sumArr[sumArr.length - 1] - sumArr[idx(0)] ,然后为其中的每个数字添加 X*I,类似于我们处理负数的方式.
  4. This leaves the interesting ones in between, such as -1 - which contributes only 1 to the sum total (-1 + 2 = +1).这留下了介于两者之间的有趣的,例如-1 - 它仅对总和贡献1 (-1 + 2 = +1)。 There is no speedy way out, so for only this slice of the input, we must iterate through it(so from IDX(-I) to IDX(0) exclusive, doing the math. This is technically O(n), except n is heavily limited; it can never be more than I unless there are duplicates in your list (and if there are, there are ways to handle those in bulk as well by making a weight array in the free precalculation phase), and is usually much less; it is the overlap: All values in the input which are between 0 and -I.没有快速的出路,所以只有这部分输入,我们必须遍历它(所以从IDX(-I)IDX(0)独占,做数学。这在技术上是 O(n),除了 n受到很大限制;除非您的列表中有重复项,否则它永远不会超过I (如果有,也可以通过在免费的预计算阶段制作一个权重数组来批量处理这些重复项),并且通常很多少;这是重叠:输入中的所有值都在 0 和 -I 之间。

the increment is negative增量为负

The exact same algorithm applies, but reversed: For an increment such as -6 , all numbers at 0 or below are trivial, as are all numbers at 6 or higher.完全相同的算法适用,但相反:对于诸如-6之类的增量,0 或以下的所有数字都是微不足道的,6 或更高的所有数字也是如此。 The loop needs to only cover all numbers between 1 and 5, inclusive.循环只需要覆盖 1 到 5 之间的所有数字,包括 1 和 5。

This results in an algorithm that is O(logn) +O(restricted-n) instead of the O(n) algorithm you have.这导致算法为 O(logn) +O(restricted-n) 而不是您拥有的 O(n) 算法。 In purely mathematical terms, it's still O(n), but in almost all scenarios it's orders of magnitude fewer operations.用纯粹的数学术语来说,它仍然是 O(n),但在几乎所有情况下,它的运算量都少了几个数量级。

Building the sum tables is itself O(n), so if the preptime is not 'free', there is no point to any of this, and what you described is as fast as it is going to get.构建总和表本身就是 O(n),所以如果准备时间不是“免费的”,那么这一切都没有意义,而且你所描述的速度将达到最快。

The "stream" version may look like this: “流”版本可能如下所示:

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();
}

Update更新
Shorter form can be used:可以使用更短的形式:
a lambda (i -> {long abs = Math.abs(numArr[i] + inc); numArr[i] += inc; return abs;})一个 lambda (i -> {long abs = Math.abs(numArr[i] + inc); numArr[i] += inc; return abs;})
may be replaced with equivalent (i -> Math.abs(numArr[i] += inc))可以替换为等效的(i -> Math.abs(numArr[i] += inc))

It provides the same output for the given test data:它为给定的测试数据提供了相同的 output:

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));

Output: Output:

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

However, the stream solution does not look as more performant because it uses similar nested loop.但是,stream 解决方案看起来并没有更高的性能,因为它使用了类似的嵌套循环。

Moreover, stream should not be used here at all because one of the input arrays numArr is modified while processing the stream, so it has side effects and cannot be run in parallel to increase performance because the results would be incorrect.此外,这里根本不应该使用 stream,因为输入 arrays numArr之一在处理 stream 时被修改,所以它有副作用,因为并行运行不会提高性能。

instead of (original code segment)而不是(原始代码段)

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

first micro-optimization ( switched the 2 lines to remove the sum from second one):第一次微优化(切换两行以从第二行中删除总和):

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

and more micro-optimization (using compound assignment)和更多的微优化(使用复合赋值)

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

and even more micro-optimization (using variable instead of array access)甚至更多的微优化(使用变量而不是数组访问)

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

but eventually the JIT-compiler is already doing that (for bigger arrays).但最终 JIT 编译器已经在这样做了(对于更大的数组)。

unable to know if that really helps or if the difference is sensitive at all since this is micro-optimization and no testing/timing was done无法知道这是否真的有帮助,或者差异是否敏感,因为这是微优化并且没有进行测试/计时

Please find the efficient way to find answer to the above problem请找到找到上述问题答案的有效方法

import java.util.Arrays;

public class BenchMarkedSolution {

public static long[] findAbsValueSum(long[] numArr, long[] incrArr) {
        int n = numArr.length;
        Arrays.sort(numArr);
        long[] res = new long[incrArr.length];
        long[] cumArr = new long[n];
        long sum = 0, cumIncr = 0;
        for (int i = 0; i < n; ++i) {
            sum += numArr[i];
            cumArr[i] = sum;
        }
        for (int i = 0; i < incrArr.length; ++i) {
            cumIncr += incrArr[i];
            int p = Arrays.binarySearch(numArr, -cumIncr);
            p = p < 0 ? Math.max(-p - 2, 0) : p;
            res[i] = Math.abs(cumArr[p] + cumIncr * (p + 1)) + Math.abs((cumArr[n - 1] - cumArr[p]) + cumIncr * (n - p - 1));
        }
        return res;
    }

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM