如何通过排除列表中的一个元素来计算元素的最小值和最大值时防止溢出

How to prevent overflow when calculating min & max sum of the elements by excluding one element in the list

提问人:Jahnavi Achanta 提问时间:9/30/2023 最后编辑:Mark RotteveelJahnavi Achanta 更新时间:10/4/2023 访问量:79

问:

我需要找到可以通过精确求和(大小 - 1)数组中的元素来计算的最小值和最大值

示例:
arr = [1,2,3,4,5] minSum = 1+2+3+4 = 10 , maxSum = 2+3+4+5 = 14

最初,我编写了以下代码

public static void miniMaxSum(List<Integer> arr) {
    List<Integer> sorted = arr.stream().sorted().collect(Collectors.toList());
    Integer totalSum = sorted.stream().mapToInt(Integer::intValue).sum();
    Integer minSum = totalSum - sorted.get(sorted.size()-1);
    Integer maxSum = totalSum - sorted.get(0);
    System.out.println(minSum + " "+maxSum);
}

对于简单的测试用例,它按预期工作,但对于具有较高值的数字,它会溢出并导致负值,因此我使用了 BigInteger。

public static void miniMaxSum(List<Integer> arr) {
    List<Integer> sorted = arr.stream().sorted().collect(Collectors.toList());
    BigInteger totalSum = BigInteger.valueOf(sorted.stream().mapToInt(Integer::intValue).sum());
    BigInteger minSum = totalSum.subtract(BigInteger.valueOf(sorted.get(sorted.size()-1)));
    BigInteger maxSum = totalSum.subtract(BigInteger.valueOf(sorted.get(0)));
    System.out.println(minSum + " "+maxSum);
}

即便如此,它也会导致负值

输入 : 793810624 895642170 685903712 623789054 468592370

输出 : -1722871536 -1295821736

为什么即使在使用 BigInteger 后也会导致负值,应该如何处理?

Java java-stream biginteger

评论

4赞 Kozydot 9/30/2023
您需要先将整数转换为整数,然后再对整数求和。BigInteger
0赞 Stephen C 9/30/2023
是的。该代码当前使用 / 算术计算总和,然后将结果转换为 .这为时已晚,无法避免溢出。intIntegerBigInteger
0赞 Stephen C 9/30/2023
(郑重声明......使用这些输入值,您可以使用 / 而不是 进行计算,并且仍然可以获得正确的答案。longLongBigInteger

答:

1赞 Mark Rotteveel 9/30/2023 #1

问题在于,当你创建时,溢出已经发生了,因为你把整数相加为 ,这意味着结果也是一个 .只有这样,您的代码才会将此溢出结果转换为 .totalSumintintBigInteger

相反,您需要使用以下命令进行计算:BigInteger

BigInteger totalSum = sorted.stream()
        .reduce(BigInteger.ZERO, 
                (sum, value) -> sum.add(BigInteger.valueOf(value)),
                BigInteger::add);

这相当于做:

BigInteger totalSum = sorted.stream()
        .map(v -> BigInteger.valueOf(v.longValue()))
        .reduce(BigInteger.ZERO, BigInteger::add);
0赞 Reilas 10/1/2023 #2

"...为什么即使在使用 BigInteger 后也会导致负值,应该如何处理?..."

利用 map 方法将解析为 BigInteger 值。
并且,reduce方法对元素进行总计。

List<Integer> sorted = new ArrayList<>(arr);
Collections.sort(sorted);
BigInteger min
    = sorted.stream()
            .limit(sorted.size() - 1)
            .map(x -> new BigInteger(String.valueOf(x)))
            .reduce(BigInteger::add)
            .get();
BigInteger max
    = sorted.stream()
            .skip(1)
            .map(x -> new BigInteger(String.valueOf(x)))
            .reduce(BigInteger::add)
            .get();
System.out.println(min + " "+max);

输出

10 14
2赞 Holger 10/4/2023 #3

正如其他人所说,将值转换为数据丢失发生后,并不能解决您的问题。操作本身必须使用比 更大的数据类型来执行。BigIntegerint

但是对列表进行排序,只是为了获得最小值和最大值,也是非常低效的。有一个内置操作,可以一次执行所有必要的操作,获取最小值、最大值和总和。它用于求和,这足以处理所有不能超过 2³¹ 元素的普通集合。summaryStatistics()long

public static void miniMaxSum(List<Integer> arr) {
    IntSummaryStatistics s = arr.stream()
            .mapToInt(Integer::intValue).summaryStatistics();
    long totalSum = s.getSum();
    long minSum = totalSum - s.getMax();
    long maxSum = totalSum - s.getMin();
    System.out.println(minSum + " " + maxSum);
}

进一步注意,这里没有理由使用盒装或对象。IntegerLong