如何按相同的城市ID对地址进行排序?

huangapple go评论77阅读模式
英文:

How I can sort addresses with the same city ID?

问题

I have Address and Employee models.
Each Address has the cityId. The addresses with the same cityId can be assigned to the same employee and must be sorted.

Employee

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@PlanningEntity
public class Employee {

  @PlanningId
  private Long id;

  @InverseRelationShadowVariable(sourceVariableName = "employee")
  private List<Address> addresses = new ArrayList<>();

}

Address

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@PlanningEntity
public class Address {

    @PlanningId
    private Long id;

    private Long cityId;

    @PlanningVariable(valueRangeProviderRefs = "employeeRange")
    private Employee employee;
}

Constraint # 1

public static Constraint cityConflict(ConstraintFactory constraintFactory) {
  return constraintFactory.forEach(Address.class)
          .groupBy(Address::getEmployee)
          .filter(employee ->
                  employee.getAddresses().stream()
                          .anyMatch(address ->
                                  !Objects.equals(address.getCityId(), employee.getAddresses().get(0).getCityId())
                          )
          ).penalize(CITY_CONFLICT, HardMediumSoftLongScore.ONE_MEDIUM);
}

Other constraints which I have tried:

public static Constraint addressesInSameCity(ConstraintFactory constraintFactory) {
    return constraintFactory.forEach(Address.class)
            .groupBy(Address::getEmployee, Address::getCityId)
            .filter(((employee, cityId) -> {
                Long previousCityId = Long.MIN_VALUE;
                for (Address address : employee.getAddresses()) {
                    Long currentCityId = address.getCityId();
                    if (currentCityId < previousCityId) {
                        return true;
                    }
                    previousCityId = currentCityId;
                }
                return false;
            }))
            .penalize(CITY_CONFLICT, HardMediumSoftLongScore.ONE_MEDIUM);
}

Expected result -
Addresses with the same id must be assigned to the employee and follow each other one.
Something like:

+----------------------+-----------------------+
| Employee 1 | Employee 2 |
+----------------------+-----------------------+
| Address 1 (cityId 4) | Address 4 (cityId 1) |
| Address 2 (cityId 4) | Address 5 (cityId 1) |
| Address 3 (cityId 4) | Address 6 (cityId 2) |
| Address 7 (cityId 5) | Address 8 (cityId 2) |
| Address 9 (cityId 5) | Address 10 (cityId 2) |
+----------------------+-----------------------+

How to achieve this with OptaPlanner constraints?
Thanks in advance for the help

英文:

I have Address and Employee models.
Each Address has the cityId. The addresses with the same cityId can be assigned to the same employee and must be sorted.

Employee

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@PlanningEntity
public class Employee {

  @PlanningId
  private Long id;

  @InverseRelationShadowVariable(sourceVariableName = &quot;employee&quot;)
  private List&lt;Address&gt; addresses = new ArrayList&lt;&gt;();

}

Address


@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@PlanningEntity
public class Address {

    @PlanningId
    private Long id;

    private Long cityId;

    @PlanningVariable(valueRangeProviderRefs = &quot;employeeRange&quot;)
    private Employee employee;
}

Constraint # 1

public static Constraint cityConflict(ConstraintFactory constraintFactory) {
  return constraintFactory.forEach(Address.class)
          .groupBy(Address::getEmployee)
          .filter(employee -&gt; employee.getAddresses().stream()
                  .anyMatch(address -&gt;
                          !Objects.equals(address.getCityId(), address.getAddresses().get(0).getCityId())
                  )
          ).penalize(CITY_CONFLICT, HardMediumSoftLongScore.ONE_MEDIUM);
}

Other constraints which I have tried:

public static Constraint addressesInSameCity(ConstraintFactory constraintFactory) {
    return constraintFactory.forEach(Address.class)
            .groupBy(Address::getEmployee, Address::getCityId)
            .filter(((employee, cityId) -&gt; {
                Long previousCityId = Long.MIN_VALUE;
                for (Address address : inspector.getAddresses()) {
                    Long currentVillageId = address.getCityId();
                    if (currentCityId &lt; previousCityId) {
                        return true; 
                    }
                    previousCityId = currentCityId;
                }
                return false; 
            }))
            .penalize(CITY_CONFLICT, HardMediumSoftLongScore.ONE_MEDIUM);
}

Expected result -
Addresses with the same id must be assigned to the employee and follow each other one.
Something like:



+----------------------+-----------------------+
|      Employee 1      |      Employee 2       |
+----------------------+-----------------------+
| Address 1 (cityId 4) | Address 4 (cityId 1)  |
| Address 2 (cityId 4) | Address 5 (cityId 1)  |
| Address 3 (cityId 4) | Address 6 (cityId 2)  |
| Address 7 (cityId 5) | Address 8 (cityId 2)  |
| Address 9 (cityId 5) | Address 10 (cityId 2) |
+----------------------+-----------------------+

How I can to achieve this with OptaPlanner constraints?
Thanks in advance for the help

答案1

得分: 1

抱歉,我只能提供代码部分的翻译。

英文:

Neither OptaPlanner nor Timefold support the notion of sorting results, in fact it could be considered a misconception.

The solver will produce whatever solution that the constraints will point it towards. So, if you want any particular behavior, you either have to reward it, or penalize its opposite.

In your case, you need to accomplish the following:

  • For every employee,
  • for every pair of addresses,
  • if the the second address should be in front of the first but it is not,
  • add a penalty
  • preferrably not constant, but based on how much the difference is.

This will guide the solver towards giving you a solution with "sorted" addresses. But you need to know that there is no guarantee that this will actually happen - the solver will make best effort, but it may not be able to find a solution that satisfies these constraints.

If this is a hard constraint which must never ever be broken, I recommend designing the model in such a way that it is impossible for the addresses to ever be not sorted. One option is to implement custom moves which take this into account and never assign an address in such a way that the end result is not sorted.

huangapple
  • 本文由 发表于 2023年5月22日 09:48:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76302621.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定