Java Comparator reversed() 方法有奇怪的结果

标签 java sorting comparator knapsack-problem

我有一个关于分数背包问题的编程作业。作为问题的解决方案,我们需要按照利润/权重比的降序排列项目。我使用自定义比较器对项目进行排序。

检查下面 Item 类中的 profitByWeightComparator:

class Item {
  private int itemNumber;
  private int profit;
  private int weight;

  public Item() {
    profit=weight=0;
  }
  public Item(int itemNumber, int profit, int weight) {
    this.itemNumber = itemNumber;
    this.profit = profit;
    this.weight = weight;
  }
  public String toString() {
    return "("+this.getItemNumber()+", "+this.getProfit()+", "+this.getWeight()+")";
  }
  public static Comparator<Item> profitByWeightComparator = new Comparator<Item>() {
    @Override
    public int compare(Item item1, Item item2) {
      double item1Ratio = (double)(item1.getProfit()/item1.getWeight());
      double item2Ratio = (double)(item2.getProfit()/item2.getWeight());
      return new Double(item1Ratio).compareTo(new Double(item2Ratio));
    }
  };
  public int getItemNumber() {
    return this.itemNumber;
  }
  public int getProfit() {
    return this.profit;
  }
  public int getWeight() {
    return this.weight;
  }
}

我使用 Comparator 类的 reversed() 方法进行降序排序。

检查下面 KnapSack 类中的 fillGreedyByProfitPerWeight() 方法:

class KnapSack {
  Item [] items;
  int capacity;
  KnapSack() {
    Scanner sc = new Scanner(System.in);
    System.out.println("Enter number of items: ");
    int n = sc.nextInt();
    items = new Item[n];
    System.out.println("Enter the capacity of the KnapSack: ");
    this.capacity = sc.nextInt();

    int profit = 0;
    int weight = 0;

    for(int i = 0; i < n; i++) {
      System.out.println("Enter Item "+(i+1)+" (Format - Profit<Space>Weight):");
      profit = sc.nextInt();
      weight = sc.nextInt();
      items[i] = new Item((i+1), profit, weight);
    }
    fillGreedyByProfitPerWeight();
  }
  public void fillGreedyByProfitPerWeight() {
    Arrays.sort(items,Item.profitByWeightComparator.reversed());
    fillKnapSack("Greedy by Profit Per Weight");
  }
  public void fillKnapSack(String strategy) {
    double totalProfit = 0d;
    int currItemProfit = 0, currItemWeight = 0, i=0, tempCapacity = capacity;
    ArrayList<Integer> filledItems = new ArrayList<Integer>();
    System.out.println(Arrays.toString(items));
    for(i = 0; i < items.length; i++) {
      currItemProfit = items[i].getProfit();
      currItemWeight = items[i].getWeight();
      if(currItemWeight <= tempCapacity) {
        filledItems.add(items[i].getItemNumber());
        totalProfit += currItemProfit;
        tempCapacity -= currItemWeight;
      }
      else {
        break;
      }
    }
    double fraction = (double)tempCapacity/currItemWeight;
    totalProfit += (double)(currItemProfit*fraction);
    System.out.println("KnapSack filled using strategy: "+strategy+".\nMaximum Profit: "+totalProfit);
    System.out.print("Items added: ");
    for(int k:filledItems) {
      System.out.print(k+" ");
    }
    if(fraction != 0d)
      System.out.print("and "+tempCapacity+"/"+currItemWeight+" of "+items[i].getItemNumber());
    System.out.println("\n");
  }

  public static void main(String[] args) {
    KnapSack obj = new KnapSack();
  }
}

但是,在某些情况下它会导致奇怪的结果。例如当我尝试以下输入时:

No. of Items: 5
Capacity: 20
Item 1 (Format - Profit<space>Weight): 20 1
Item 2 (Format - Profit<space>Weight): 10 1
Item 3 (Format - Profit<space>Weight): 10 2
Item 4 (Format - Profit<space>Weight): 40 8
Item 5 (Format - Profit<space>Weight): 60 11

Maximum Profit 的期望输出为 125(第 1 项、第 2 项、第 5 项、第 3 项和第 4 项的 5/8),但得到的输出如下: (请检查链接,因为我还不允许嵌入图像)

Output Screenshot

请注意,排序顺序困惑了,因为第 5 项不应该按相反的排序顺序排在最后,它必须排在第 3 位。罪魁祸首可能是什么?

最佳答案

您的比较器错误地执行除法:不是将 int 转换为 double 然后除法,而是先除法,然后将结果转换为 double。那时分数已经消失,因此结果排序不正确。

如下更改比较器:

public static Comparator<Item> profitByWeightComparator = new Comparator<Item>() {
    @Override
    public int compare(Item item1, Item item2) {
        double item1Ratio = ((double)item1.getProfit())/item1.getWeight();
        double item2Ratio = ((double)(item2.getProfit())/item2.getWeight();
        return Double.compare(item1Ratio, item2Ratio);
    }
};

现在除法在 double 中完成,并且在不截断小数部分的情况下完成比较。

如果权重严格为正,并且 profit * weight 不会溢出 int,您可以完全跳过除法,比较比率如下:

public static Comparator<Item> profitByWeightComparator = new Comparator<Item>() {
    @Override
    public int compare(Item item1, Item item2) {
        return Integer.compare(
            item1.getProfit() * item2.getWeight()
        ,   item2.getProfit() * item1.getWeight()
        );
    }
};

关于Java Comparator reversed() 方法有奇怪的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46156621/

相关文章:

java - Hibernate 将东方符号而不是拉丁符号写入 SQL Server

c++ - 即使在数组上执行了上限,也超过了时间限制

sorting - Jasper Reports 使用 comparatorExpression 进行交叉表排序

hadoop - 如何在 Hadoop 的 map-reduce 作业中通过自定义比较器对键进行排序?

java - 如何从 Java 执行完全独立的应用程序。喜欢独立进程

java - 使用 @ContextConfiguration 在 JUnit 上使用不同的构造函数参数测试 Spring bean

java - Android锁屏更换壁纸/背景图片

scala - 从 Scala 中的方法返回一个 Ordered[T]

java - 按自定义字典顺序对字符串进行排序

java - 为我的 PriorityQueue 实现自定义比较器