This is the 17th day of my participation in Gwen Challenge
Contents summary
In the last chapter we learned how to use OptaPlanner to solve the n-queen problem. Today we’re going to learn how to use OptaPlanner to solve some examples of advanced mathematics, simple extremum problems. Through this example, we can review some of the knowledge points we have learned, or it is very necessary.
Problem description
In the market management, engineering technology and daily life, we often encounter such problems: under certain conditions, how to make “the lowest cost”, “the most economical material”, “the highest efficiency” and other problems, and this kind of problems can sometimes be summed up in mathematics to find a maximum or minimum value of a function. Let’s look at an example:
A real estate company has 50 apartments to rent out. When the monthly rent is set at 1000 yuan, all apartments will be rented out. Every 50 yuan increase in the monthly rent will result in one more apartment that cannot be rented out. Solution: Let x be the number of unrented apartments, then the income function f (x) =(1000+50x)(50-x)-100(50-x),. .f'(x)=1600-100x, : when x=16, f (x) is the maximum, set the rent at 1800 yuan, the maximum income. Answer: 1800
Problems in modeling
From this example, we can get several information, PlanningEntity and PlanningVariable are PlanningEntity and PlanningVariable are ProblemFact.
PlanningVariable
From the example, it is easy to find a PlanningVariable variable, roomRent rent, and there is only one PlanningEntity in this example. The previous cases we studied were all multiple planningentities in the form of Collection. Property of the Solution class. This time we just need the PlanningEntityProperty annotation for the single object as a property.
@PlanningEntity(difficultyWeightFactoryClass = RoomRetWeightFactory.class)
public class RoomRet extends AbstractPersistable implements Comparable<RoomRet> {
@CustomShadowVariable(variableListenerClass = RoomRetUpdateVariableListener.class, sources = {@PlanningVariableReference(variableName = "roomRet")})
private Integer leasedOut;
@PlanningVariable(valueRangeProviderRefs = "roomRetRange", strengthComparatorClass = RoomStrengthComparator.class)
private Integer roomRet;
public Integer getLeasedOut(a) {
return leasedOut;
}
public void setLeasedOut(Integer leasedOut) {
this.leasedOut = leasedOut;
}
public Integer getRoomRet(a) {
return roomRet;
}
public void setRoomRet(Integer roomRet) {
this.roomRet = roomRet;
}
@Override
public int compareTo(RoomRet o) {
return 0; }}Copy the code
VariableRange Range of variable values
In the example, the range of variables we see is from 1000 rent, increasing by 50 each time, with no upper limit. However, in order to solve the calculation method, we set a limit for it, and the maximum rent can only be 5000, so we need to provide a range of Collection as the variable value of roomRent.
// Range: 1000, 1050, 1100, 1150, ... , 4950, 5000
ValueRangeFactory.createIntValueRange(1000.5000.50);
Copy the code
Solution class
The Solution class is relatively simple, as follows:
@PlanningSolution
public class RoomRetSolution extends AbstractPersistable {
private Integer roomCount = 50;
@PlanningEntityProperty
private RoomRet roomRet;
@PlanningScore
private SimpleScore score;
@ValueRangeProvider(id = "roomRetRange")
public CountableValueRange<Integer> getRoomRetRange(a) {
// Range: 1000, 1050, 1100, 1150, ... , 4950, 5000
return ValueRangeFactory.createIntValueRange(1000.5000.50);
}
public RoomRet getRoomRet(a) {
return roomRet;
}
public void setRoomRet(RoomRet roomRet) {
this.roomRet = roomRet;
}
@ProblemFactProperty
public Integer getRoomCount(a) {
return roomCount;
}
public void setRoomCount(Integer roomCount) {
this.roomCount = roomCount;
}
public SimpleScore getScore(a) {
return score;
}
public void setScore(SimpleScore score) {
this.score = score; }}Copy the code
ShadowVariable ShadowVariable listener
Here we use a ShadowVariable, whose attribute is leasedOut has been rented, because it is not a PlanningVariable, but changes according to the change of roomRent, because every 50 yuan increase, one less house will be rented.
public class RoomRetUpdateVariableListener implements VariableListener<RoomRetSolution.RoomRet> {
@Override
public void beforeEntityAdded(ScoreDirector<RoomRetSolution> scoreDirector, RoomRet roomRet) {
this.setRet(scoreDirector, roomRet); }...@Override
public void afterVariableChanged(ScoreDirector<RoomRetSolution> scoreDirector, RoomRet roomRet) {
this.setRet(scoreDirector, roomRet); }...private void setRet(ScoreDirector<RoomRetSolution> scoreDirector, RoomRet roomRet) {
Integer ret = roomRet.getRoomRet();
if(ret == null) {
return;
}
if (ret <= 1000) {
scoreDirector.beforeVariableChanged(roomRet, "leasedOut");
roomRet.setLeasedOut(50);
scoreDirector.afterVariableChanged(roomRet, "leasedOut");
return;
}
Integer leaseOut = 50 - (roomRet.getRoomRet() - 1000) / 50;
scoreDirector.beforeVariableChanged(roomRet, "leasedOut");
roomRet.setLeasedOut(leaseOut);
scoreDirector.afterVariableChanged(roomRet, "leasedOut");
return; }}Copy the code
We will talk about shadow variables later. Here we will use them briefly to give you a general understanding of ShadowVariable.
Constraint scoring rule
The rule of constraint scoring, we find that there is no constraint condition, there is only one result, that is, the higher the total benefit, the better.
In fact, this can be converted into a rule, and we’ll just use revenue as a score, since OptaPlanner is designed to get the highest score.
package org.optaplanner.examples.house.solver;
dialect "java"
import org.optaplanner.core.api.score.buildin.simple.SimpleScoreHolder;
import org.optaplanner.examples.house.domain.RoomRetSolution;
import org.optaplanner.examples.house.domain.RoomRet;
global SimpleScoreHolder scoreHolder;
rule "Maximum Profit"
when
RoomRet(roomRet ! =null, leasedOut ! =null, $leasedOut : leasedOut, $roomRet : roomRet)
then
Integer profit = ($leasedOut * $roomRet) - ($leasedOut * 100);
scoreHolder.addConstraintMatch(kcontext, profit);
end
Copy the code
Run to solve
Please enable the Debug mode of the OptaPlanner log to view the results of the solution.
logging.level.org.optaplanner=debug
Copy the code
Execution Result:
14:03:35.946 [main ] DEBUG Service org.kie.api.internal.weaver.KieWeavers is implemented by org.kie.internal.services.KieWeaversImpl@b62d79
14:03:40.536 [main ] INFO Solving started: time spent (80), best score (-1init/0), environment mode (REPRODUCIBLE), move thread count (NONE).random (JDK with seed 0).14:03:40.596 [main] DEBUG CHstep (0), time spent (140).score (57800), selected move count (80), picked move (RoomRet-1 {null -> 1800}).14:03:40.596 [main] INFO Construction Heuristicphase (0) ended: time spent (140), best score (57800), score calculation speed (1350/sec), step total (1).14:03:40.596 [main] INFO Solving ended: timespent (140), best score (57800), score calculation speed (585/sec), phase total (1), environment mode (REPRODUCIBLE), move thread count (NONE).
ret = 1800
Copy the code
So rent is equal to 1800 dollars, which is the same thing as the answer.
Among them:
DEBUG CH step (0), time spent (140).score (57800), selected move count (80), picked move (RoomRet-1 {null -> 1800}).
Copy the code
This debug log finds that the result is obtained after only one step. Of course, I did a lot of things before, so you can trace them later.
conclusion
Through this example, we learned how to use ShadowVariable. In fact, we can not use ShadowVariable, just to introduce the use method of ShadowVariable, so this writing method is added to this example. (Ret was misspelled in the code as rent).
conclusion
We’ll look at another example in the next chapter.
Creation is not easy, unauthorized reprint is prohibited. If my article is helpful to you, please like/favorites/follow it to encourage and support 💕💕💕💕 college