You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java程序调试:6数字组合运算逼近目标值异常

问题描述

实现程序需满足以下要求:

  • 输入:第一行输入6个0-1000的数字,第二行输入目标数字
  • 规则:仅使用输入数字与+、-、*、/构造表达式,加减运算优先级高于乘除,不得使用括号;允许不完全除法,结果可为有理数;每个数字仅能使用一次;表达式合法且至少包含一个数字;禁止拼接数字
  • 输出:|目标值-最接近结果值|,结果与精确值误差小于1e-6即视为正确

运行示例

示例1

输入:
1 2 3 4 5 6
21
输出:
0
说明:所有数字相加可精确得到目标值21

示例2

输入:
16 65 83 61 33 64
934
输出:
0.0625
说明:最优表达式为65 + 83 + 33 + 64 / 16 * 61,计算结果与目标值的差为0.0625

当前实现代码

import java.util.*;

public class project {
  private static Set<Character> operations = new HashSet<>(Arrays.asList('+', '-', '*', '/'));
  private static double minDiff = Double.MAX_VALUE;
  private static String closestExpr = "";

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    double[] numbers = new double[6];
    for (int i = 0; i < 6; i++) {
      numbers[i] = sc.nextDouble();
    }
    double target = sc.nextDouble();

    char[] expr = new char[5];
    for (int i = 0; i < 5; i++) {
      for (char operation : operations) {
        expr[i] = operation;
        permuteExpression(numbers, expr, 0, target);
      }
    }

    System.out.println(minDiff);
  }

  private static void permuteExpression(double[] numbers, char[] expr, int depth, double target) {
    if (depth == expr.length) {
      StringBuilder sb = new StringBuilder();
      sb.append(numbers[0]);
      double result = numbers[0];
      for (int i = 0; i < expr.length; i++) {
        sb.append(expr[i]);
        sb.append(numbers[i + 1]);
        switch (expr[i]) {
          case '+':
            result += numbers[i + 1];
            break;
          case '-':
            result -= numbers[i + 1];
            break;
          case '*':
            result *= numbers[i + 1];
            break;
          case '/':
            result /= numbers[i + 1];
            break;
        }
      }
      double diff = Math.abs(result - target);
      if (diff < minDiff && diff < Math.pow(10, -6)) {
        minDiff = diff;
        closestExpr = sb.toString();
      }
      return;
    }

    for (char operation : operations) {
      expr[depth] = operation;
      permuteExpression(numbers, expr, depth + 1, target);
    }
  }
}

存在的问题

  • 结果精度不足,大量测试用例失败
  • 输入数字较大时,输出始终为1.7976931348623157E308(即Double.MAX_VALUE),例如示例2测试用例中,正确输出应为0.0625,但当前代码输出该最大值

问题分析与解决建议

1. 错误的minDiff更新条件(核心问题)

代码中更新minDiff的条件为:

if (diff < minDiff && diff < Math.pow(10, -6))

该条件错误地过滤了所有误差大于1e-6的结果,但题目要求找到最接近目标值的结果,无论误差大小;1e-6只是判断结果是否「视为正确」的阈值,而非筛选更新的条件。这直接导致示例2这类非高精度匹配的最优解无法被捕获,minDiff始终保持初始的Double.MAX_VALUE。

修正方式:
去掉误差阈值限制,只要当前差值小于当前minDiff就更新:

if (diff < minDiff) {
    minDiff = diff;
    closestExpr = sb.toString();
}

2. 未遍历数字的全排列

当前代码固定使用输入的数字顺序,未生成数字的所有排列组合。题目允许数字顺序任意调整(只要每个数字仅用一次),示例2的最优表达式就使用了与输入不同的数字顺序,代码完全遗漏了这类情况,导致大量可能的最优解未被计算。

解决方式:
递归生成输入数字索引的全排列,遍历所有可能的数字顺序:

// 生成数字索引的全排列
private static void generatePermutations(List<Integer> indices, int start, List<List<Integer>> result) {
    if (start == indices.size()) {
        result.add(new ArrayList<>(indices));
        return;
    }
    for (int i = start; i < indices.size(); i++) {
        Collections.swap(indices, start, i);
        generatePermutations(indices, start + 1, result);
        Collections.swap(indices, start, i);
    }
}

// 在main方法中调用生成排列并遍历
List<Integer> indices = new ArrayList<>();
for (int i = 0; i < 6; i++) indices.add(i);
List<List<Integer>> permutations = new ArrayList<>();
generatePermutations(indices, 0, permutations);

for (List<Integer> perm : permutations) {
    double[] permutedNumbers = new double[6];
    for (int i = 0; i < 6; i++) {
        permutedNumbers[i] = numbers[perm.get(i)];
    }
    char[] expr = new char[5];
    permuteExpression(permutedNumbers, expr, 0, target);
}

3. 表达式计算逻辑错误(优先级未正确处理)

题目要求「加减运算优先级高于乘除」,但当前代码采用从左到右依次计算的方式,完全忽略优先级规则。例如表达式a * b + c,代码会计算(a*b)+c,但按照题目规则应先算b+c再乘以a。

解决方式:
分两步计算表达式:

  1. 先遍历表达式,执行所有加减运算,将中间结果与乘除运算符、未参与加减的数字存入列表
  2. 再遍历列表,执行所有乘除运算得到最终结果

使用BigDecimal保证计算精度:

private static BigDecimal calculateResult(double[] numbers, char[] expr) {
    List<Object> parts = new ArrayList<>();
    parts.add(BigDecimal.valueOf(numbers[0]));
    for (int i = 0; i < expr.length; i++) {
        char op = expr[i];
        BigDecimal num = BigDecimal.valueOf(numbers[i+1]);
        if (op == '+' || op == '-') {
            BigDecimal prev = (BigDecimal) parts.remove(parts.size()-1);
            parts.add(op == '+' ? prev.add(num) : prev.subtract(num));
        } else {
            parts.add(op);
            parts.add(num);
        }
    }
    // 计算乘除
    BigDecimal result = (BigDecimal) parts.get(0);
    for (int i = 1; i < parts.size(); i += 2) {
        char op = (char) parts.get(i);
        BigDecimal num = (BigDecimal) parts.get(i+1);
        if (op == '*') {
            result = result.multiply(num);
        } else {
            // 保留20位小数避免精度损失
            result = result.divide(num, 20, RoundingMode.HALF_UP);
        }
    }
    return result;
}

4. 浮点精度损失

使用double计算会累积浮点误差,导致差值计算不准确。改用BigDecimal进行精确计算,再转换为double计算差值:

BigDecimal result = calculateResult(numbers, expr);
double diff = Math.abs(result.subtract(BigDecimal.valueOf(target)).doubleValue());

5. 冗余的运算符遍历逻辑

main方法中提前循环赋值运算符的代码是冗余的,permuteExpression方法已经递归遍历所有运算符组合,直接调用permuteExpression即可。


内容的提问来源于stack exchange,提问作者kollleey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 15:05:33