我正在寻找Java中具有多个字段的加权排序的有效实现。这个问题在某种程度上类似于How to provide most relevant results with Multiple Factor Weighted SortingNeed help maximizing 3 factors in multiple, similar objects and ordering appropriately。但是,我需要一些有关有效实施的指南。

在下面的示例中,Person类具有ageincome字段,我想基于给定的首选项和降序对具有较低persons和较高age组合的数组列表income进行排序。我为ageincome提供了相同的首选项。首选项的总和应为1。

如您所见,在这个幼稚的实现中,有太多的循环需要迭代,最终,运行大量输入的成本太高。我还探讨了 Guava的CompareChain Apache Commons CompareToBuilder ,但它们似乎没有达到我的目标。

package main.java.utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class SortingTest {

static double income_preference = 0.5;
static double age_preference = 1 - income_preference;

public static void main(String args[]) {
    ArrayList<Person> persons = new ArrayList<Person>();

    persons.add(new Person("A", 60, 55.0));
    persons.add(new Person("B", 45, 50.0));
    persons.add(new Person("C", 20, 50.0));
    persons.add(new Person("D", 55, 60.0));
    persons.add(new Person("E", 30, 85.0));

    // Sort the array list by income (descending order)
    Collections.sort(persons, new Comparator<Person>(){
        @Override
        public int compare(Person p1, Person p2) {
            return (((double)p1.income > (double)p2.income) ? -1 :
                ((double)p1.income < (double)p2.income) ? 1 : 0);
        }
    });

    // Rank based on income
    int income_rank = persons.size();
    for(int i = 0; i < persons.size(); i++) {
        if(i != 0)
            if(persons.get(i).income != persons.get(i-1).income)
                --income_rank;

        persons.get(i).income_rank = income_rank * income_preference;
    }

    System.out.println("List of persons sorted by their income in descending order: ");
    for(Person p : persons)
        System.out.println(p.toString());

    // Sort the array list by age (ascending order)
    Collections.sort(persons, new Comparator<Person>(){
        @Override
        public int compare(Person p1, Person p2) {
            return (((double)p2.age > (double)p1.age) ? -1 :
                ((double)p2.age < (double)p1.age) ? 1 : 0);
        }
    });

    // Rank based on age
    int age_rank = persons.size();
    for(int i = 0; i < persons.size(); i++) {
        if(i != 0)
            if(persons.get(i).age != persons.get(i-1).age)
                --age_rank;

        persons.get(i).age_rank = age_rank * age_preference;
    }

    System.out.println();
    System.out.println("List of persons sorted by their age in ascending order: ");
    for(Person p : persons)
        System.out.println(p.toString());

    // Assign combined rank
    for(Person p : persons)
        p.combined_rank = (p.age_rank + p.income_rank);

    // Sort the array list by the value of combined rank (descending order)
    Collections.sort(persons, new Comparator<Person>(){
        @Override
        public int compare(Person p1, Person p2) {
            return (((double)p1.combined_rank > (double)p2.combined_rank) ? -1 :
                ((double)p1.combined_rank < (double)p2.combined_rank) ? 1 : 0);
        }
    });

    System.out.println();
    System.out.println("List of persons sorted by their combined ranking preference in descending order: ");
    for(Person p : persons)
        System.out.println(p.toString());
}
}

class Person {
String name;
int age; // lower is better
double income; // higher is better
double age_rank;
double income_rank;
double combined_rank;

public Person(String name, int age, double income) {
    this.name = name;
    this.age = age;
    this.income = income;
    this.age_rank = 0.0;
    this.income_rank = 0.0;
    this.combined_rank = 0.0;
}

@Override
public String toString() {
    return ("Person-"+this.name+", age("+this.age+"|"+this.age_rank+"th), income("+this.income+"|"+this.income_rank+"th), Combined Rank("+this.combined_rank+")");
}
}

控制台输出

按收入降序排列的人员列表:

E人,年龄(30 | 0.0th),收入(85.0 | 2.5th),综合排名(0.0)

D人,年龄(55 | 0.0th),收入(60.0 | 2.0th),综合排名(0.0)

A人,年龄(60 | 0.0th),收入(55.0 | 1.5th),综合排名(0.0)

B人,年龄(45 | 0.0th),收入(50.0 | 1.0th),综合排名(0.0)

C人,年龄(20 | 0.0th),收入(50.0 | 1.0th),综合排名(0.0)

按年龄升序排列的人员列表:

C人,年龄(20 | 2.5th),收入(50.0 | 1.0th),综合排名(0.0)

E人,年龄(30 | 2.0th),收入(85.0 | 2.5th),综合排名(0.0)

B人,年龄(45 | 1.5th),收入(50.0 | 1.0th),综合排名(0.0)

D人,年龄(55 | 1.0th),收入(60.0 | 2.0th),综合排名(0.0)

A人,年龄(60 | 0.5th),收入(55.0 | 1.5th),综合排名(0.0)

按组合排名首选项按降序排列的人员列表:

E人,年龄(30 | 2.0th),收入(85.0 | 2.5th),综合排名(4.5)

C人,年龄(20 | 2.5th),收入(50.0 | 1.0th),综合排名(3.5)

D人,年龄(55 | 1.0th),收入(60.0 | 2.0th),综合排名(3)

B人,年龄(45 | 1.5th),收入(50.0 | 1.0th),综合排名(2.5)

A人,年龄(60 | 0.5th),收入(55.0 | 1.5th),综合排名(2.5)

最佳答案

您可以维护两个TreeSet来分别存储年龄和收入信息,因此您可以轻松地从这两棵树中查询排序时的年龄和收入等级。

我们可以从TreeSet调用tailSet(int)方法来获取大于或等于特定数字的数字列表,在这种情况下,它将是年龄/收入等级。

TreeSet ageRank = new TreeSet();
TreeSet incomeRank = new TreeSet();

for(Person p : persons){
   ageRank.add(p.getAge());
   incomeRank.add(p.getIncome());
}

Collections.sort(persons, new Comparator<Person>(){
        @Override
        public int compare(Person p1, Person p2) {
             int ageRank1 = ageRank.tailSet(p1.getAge()).size();
             int ageRank2 = ageRank.tailSet(p2.getAge()).size();
             int incomeRank1 = incomeRank.tailSet(p1.getIncome()).size();
             int incomeRank2 = incomeRank.tailSet(p2.getIncome()).size();
             //Calculate the combined_rank and return result here. Code omitted

        }
});

使用这种方法,对循环进行一个排序就足够进行所有计算。

如果您需要定期更新个人列表,则此方法会派上用场,因为您不需要对年龄和收入进行排序,也不需要在有更新时一次又一次地重新计算所有排名,只需更新这些两棵树。

注意:为了在用于排序的内部ageRank类内使用incomeRankComparator,需要将它们声明为final或实例变量。

关于java - Java中多因素加权排序的有效实现,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30449023/

10-10 22:57