Fairness in Optaplanner, but with hours instead of shifts - java

Im having some trouble with my fairness constraint, I want to make sure employees get:
Even amount of shifts during weeks.
Even amount of shifts in total.
Basically I want to avoid that fairness is only checking weeks (so that if there's 2 extra shifts, it won't be the same two employees getting those every week, potentially)
And I want to avoid fairness checking only the total (so that maybe one employee get much more shifts one week, then none the next, but overall theyd all still get even hours)
So I tried to follow what the docs for Optaplanner said in regards to a fairness constraint, and made two constraints for that, but unlike the docs preview that uses shifts, I need mine to be estimated in hours... So now, my code:
public int accumulateFairnessInHoursPerEmployeePerWeek(Week week)
{
//System.out.println("WEEK FAIRNESS CONSTRAINT:");
int actualWorkload = 0;
int totalAssignmentsDuringWeek = 0;
for(Employee emp : getEmployees())
{
List<Assignment> assignmentsForEmployeeDuringWeek = new ArrayList<>();
for(Assignment currentAss : getAssignmentsForSpecificWeek(week))
{
if(currentAss.getEmployee() == emp)
{
assignmentsForEmployeeDuringWeek.add(currentAss);
}
}
totalAssignmentsDuringWeek += getDurationForAssignments(assignmentsForEmployeeDuringWeek)/3600;
actualWorkload += (int) Math.pow(getDurationForAssignments(assignmentsForEmployeeDuringWeek)/3600, 2);
//System.out.println(emp.getName() + " has " + getDurationForAssignments(assignmentsForEmployeeDuringWeek)/3600 + " hours. Score: " + actualWorkload + " total: " + actualWorkload + " " + ass.getShift().getStartDate());
}
int idealWorkLoad = (int) Math.pow(totalAssignmentsDuringWeek, 2)/getEmployees().size();
//System.out.println("IDEAL: " + idealWorkLoad + " ACTUAL: " + actualWorkload + " FAIRNESS: " + (actualWorkload -idealWorkLoad));
return (actualWorkload - idealWorkLoad);
}
public int accumulateFairnessInHoursPerEmployeeInTotal()
{
System.out.println("TOTAL FAIRNESS CONSTRAINT:");
int actualWorkload = 0;
int totalDuration = 0;
for(Employee emp : getEmployees())
{
List<Assignment> assignmentsForEmployee = new ArrayList<>();
for(Assignment currentAss : getAssignments())
{
if(currentAss.getEmployee() == emp)
{
assignmentsForEmployee.add(currentAss);
}
}
totalDuration += getDurationForAssignments(assignmentsForEmployee)/3600;
actualWorkload += (int) Math.pow(getDurationForAssignments(assignmentsForEmployee)/3600, 2);
System.out.println(emp.getName() + " has " + getDurationForAssignments(assignmentsForEmployee)/3600 + " hours. Score: " + actualWorkload);
}
int idealWorkLoad = (int) Math.pow(totalDuration, 2)/getEmployees().size();
System.out.println("IDEAL: " + idealWorkLoad + " ACTUAL: " + actualWorkload + " FAIRNESS: " + (actualWorkload - idealWorkLoad));
return (actualWorkload - idealWorkLoad);
}
And here's the drools:
rule "EvenWorkloadPerEmployeeTotal"
when
$service : Service
(
$service.accumulateFairnessInHoursPerEmployeeInTotal() != 0
)
then
if(isDroolActivated(kcontext.getRule().getName(), $service))
{
setDroolRating(scoreHolder, kcontext, $service.getDroolStrength(drools), $service.accumulateFairnessInHoursPerEmployeeInTotal());
}
end
rule "EvenWorkloadPerEmployeePerWeek"
when
$week : Week()
$service : Service
(
$service.accumulateFairnessInHoursPerEmployeePerWeek($week) != 0
)
then
if(isDroolActivated(kcontext.getRule().getName(), $service))
{
setDroolRating(scoreHolder, kcontext, $service.getDroolStrength(drools), $service.accumulateFairnessInHoursPerEmployeePerWeek($week));
}
end
It seemingly works most of the time, especially in smaller datasets... However, when I use a bigger dataset...
This is my results:
A has 76.0 hours. Score: 5776
B has 118.0 hours. Score: 19700
C has 76.0 hours. Score: 25476
D has 83.0 hours. Score: 32365
E has 88.0 hours. Score: 40109
F has 72.0 hours. Score: 45293
G has 68.0 hours. Score: 49917
H has 64.0 hours. Score: 54013
I has 96.0 hours. Score: 63229
J has 94.0 hours. Score: 72065
K has 92.0 hours. Score: 80529
L has 67.0 hours. Score: 85018
M has 98.0 hours. Score: 94622
N has 95.0 hours. Score: 103647
O has 101.0 hours. Score: 113848
P has 90.0 hours. Score: 121948
Q has 93.0 hours. Score: 130597
R has 108.0 hours. Score: 142261
S has 124.0 hours. Score: 157637
T has 116.0 hours. Score: 171093
IDEAL: 157560 ACTUAL: 171093 FAIRNESS: 13533
The numbers go pretty high...
And I doubt anyone finds it fair that G and H gets only 64-68 hours, but S must work for 124 hours
I'm wondering if theres another/better way to estimate fairness when using time instead of shifts to calculate fairness?
EDIT: Probably worth noting that I tried with days as well, but the numbers seemed far too small using those, it was like it didn't care much for a single day too much on one employee compared to another.
I'm using these constraints at the same time, but not with other constraints involved

Take a look at the tennis example, specifically this line.
rule "fairAssignmentCountPerTeam"
when
accumulate(
$t : Team()
and accumulate(
TeamAssignment(team == $t);
$assignmentCount : count() // This could be a sum of hours too
);
$result : loadBalance($assignmentCount)
)
then
scoreHolder.addMediumConstraintMatch(kcontext, - (int) $result.getMeanDeviationSquaredSumRootMillis());
end

Related

Need first output to be a number, but its that numbers concatenation

My code is as follows:
import java.util.Scanner;
public class QuestionOne {
public static void main(String[] args) {
int numberofDays;
int sharePoints;
Scanner keyboard = new Scanner(System.in);
System.out.print("Number of days in the period: ");
numberofDays = keyboard.nextInt();
System.out.print("Share points on the first day: ");
sharePoints = keyboard.nextInt();
while (numberofDays < 10 || numberofDays > 20) {
System.out.println("The number of days doesn’t meet the required criteria, enter it again");
System.out.print("Number of days in the period: ");
numberofDays = keyboard.nextInt();
}
System.out.println("Day " + " Share Points");
for (int i = 1; i <= numberofDays; i++) {
if (numberofDays % 2 == 0)
if (i <= numberofDays / 2) {
sharePoints = sharePoints + 50;
System.out.println(i + " " + sharePoints);
} else {
sharePoints = sharePoints - 25;
System.out.println(i + " " + sharePoints);
}
else {
if (i <= numberofDays / 2 + 1) {
sharePoints = sharePoints + 50;
System.out.println(i + " " + sharePoints);
} else {
sharePoints = sharePoints - 25;
System.out.println(i + " " + sharePoints);
}
}
}
}
}
This code should output to something like (as an example if the user were to enter the values 11 for the day and 550 for share price):
Day Share Points
1 550
2 600
3 650
4 700
5 750
6 800
7 775
8 750
9 725
10 700
11 675
however when I enter 11 for the day, and 550 for the share price, my code looks like:
Day Share Points
1 600
2 650
3 700
4 750
5 800
6 850
7 825
8 800
9 775
10 750
11 725
From what I can tell according to my code, I have coded it so that it adds 50 every time until the number six- whereas I want it to display the first number as the user enters it in, then to start adding and subtracting accordingly (note that everything is as I want it in my output except for the first number, and the proceeding numbers, as a result, being different). My wording may not be very accurate, but I hope the example outputs are enough to explain what I want as my output.
Sometimes it can be useful to process a base case before a for loop.
In your case that would look like printing the appropriate line for day 1 and then starting your loop at i=2 instead of i=1.

Rising tuition cost after ten years

Suppose that the tuition for a university is $10,000 this year and increases 5% every year. In one year, the tuition will be $10,500. Write a program that computes the tuition in ten years and the total cost of four years' worth of tuition after the tenth year.
I can calculate the tenth year tuition easily enough. What has me stumped is how to add the unique tuition values at years 11, 12, 13 and 14.
double Fee = 10000;
double Year = 1;
double TotalFee;
double Rate = 5;
double TotalCost = 15000 + 15500 + 16000 + 16500;
System.out.println("Year " + " Total Fee ");
System.out.println();
while (Year <= 14) {
TotalFee = Fee + ((Fee * ((Year * Rate) - Rate)) / 100);
System.out.println(Year + " " + " "+ TotalFee);`
Year++;
}
System.out.println("Total cost tuition of 4 years starting 10 years from now is " + TotalCost);
The last while loop is my attempt at adding the 4 years. How could I pull out the unique values of TotalCost at iterations 11 to 14 and add them?
Since you want to increase the amount 5% every year, instead of having rate = 5
You should have rate = 1.05.
With the rate as 1.05 you can do this
FeeAtYear1 = 10000*1.05^0 = 10000
FeeAtYear2 = 10000*1.05^1 = 10500
FeeAtYear3 = 10000*1.05^2 = 11025
FeeAtYear4 = 10000*1.05^3 = 11576.25
...
FeeAtYear10 = 10000*1.05^9 = ~16288.95
You don't even need a while loop.
TotalCost = 10000 *1.05^10 + 10000 *1.05^11 + 10000 *1.05^12 + 10000 *1.05^13;

Trying to make a while loop work properly

I'm making a program that calculates the population for a year given the start year (2011) and increases the population by 1.2% every year. The population for 2011 is 7.000 (I'm using decimals, instead of billions). Here is the working part of my code
package src;
import java.util.Scanner;
import java.text.DecimalFormat;
public class Demographics {
public static void main(String[] args) {
Scanner user_input= new Scanner(System.in);
//Part 1
System.out.println("===--- Part 1 ---===");
System.out.println("Population in 2011: 7.000");
System.out.print("What is the desired year? ( > 2011) ");
int startYear = 2011;
int endYear = user_input.nextInt();
while (endYear <= startYear){
System.out.println("Invalid end year.");
System.out.print("What is the desired year? ( > 2011) ");
endYear = user_input.nextInt();
break;
}
double t = 0.012;
double nbr = (endYear - startYear);
double pStart = 7.000;
double pEnd = pStart * Math.exp(nbr * t);
DecimalFormat nf = new DecimalFormat("#.000");
System.out.println("Population in " + endYear + ":(nf.format(pEnd)));
//Part 2
System.out.println("===--- Part 2 ---===");
System.out.print("What is the target population? ( > 7.000) ");
double pTarget = user_input.nextDouble();
while (pTarget <= pStart){
System.out.println("Invalid target population.");
System.out.print("What is the target population? ( > 7.000) ");
pTarget = user_input.nextDouble();
break;
}
while (pStart < pTarget){
startYear++;
pStart = pStart + (pStart * 0.012);
System.out.println("Population in " + startYear + ": " + nf.format((pStart)));
}
}
}
Part 1 of my code calculates the population of a year when the user enters it, then part 2 shows the calculations of how many years it will take when a user enters a population to get to that point.
Here is the code that doesn't work
//Part 3
System.out.println("===--- Part 3 ---===");
t = 1.2;
pStart = 7.000;
pEnd = pStart * Math.exp(nbr * t);
while (pStart < pTarget){
startYear++;
pEnd = pStart + (pStart * 0.012);
if (pEnd >= pStart * 2 ){
System.out.println("Population in " + startYear + ": " + nf.format((pEnd)) + " Population growth rate " + ": " + (t / 2));
}else{
System.out.println("Population in " + startYear + ": " + nf.format((pEnd)) + " Population growth rate " + ": " + t);
}
}
Currently when i have part 3 in my code it does an infinite loop without multiplying the population. What I'm trying to do in part 3 is pretty much the same thing in part 2, but in part 3 it will display the population growth rate (t) and divide it by 2 every time the population doubles. For example:
Population in 2019 : 7.705 ; population growth rate : 1.2%
Population in 2020 : 7.798 ; population growth rate : 1.2%
Population in 2021 : 7.892 ; population growth rate : 1.2%
...
Population in 2068 : 13.873 ; population growth rate : 1.2%
Population in 2069 : 14.040 ; population growth rate : 0.6%
Anyone have any ideas on how to achieve this?
if you have problems in the loop the reason is in the condition!
while (pStart < pTarget){
so to end this code, inside the loop one of this situation has to happen:(according with your condition)
A)pStart should increase in value
B)pTarget should decrement in value
C)a "break;" have to occur
in your code you increase: startYear and pEnd, but this are not the condition to close the loop according with your condition. (i write it before:A,B,C)
1) also startYear are not re initialize it before the loop, and start already at a high value. you have to add bedore the loop:
startYear = 2011;
2) you should as far as possible to create new variables for the segment 3, is not to have problems like the one just described, is to be clear about what you are doing.
my advice for part three is this:
(Considering that I have no clear what I wanted to do reading your code, you have to cange it and make it good for you)
System.out.println("===--- Part 3 ---===");
t = 1.2;
startYear = 2011; // I add it
double pEveryYear = 7000;
while (pEveryYear < pTarget){
startYear++;
pEveryYear = pEveryYear + (pEveryYear * 0.012);
if (pEveryYear >= pTarget ){ // this condition cange only the print in the console
System.out.println("Population in " + startYear + ": " + nf.format((pEveryYear)) + " Population growth rate " + ": " + (t / 2));
break; // if you write it before the system.out you can't read it in the console.
}else{
System.out.println("Population in " + startYear + ": " + nf.format((pEveryYear)) + " Population growth rate " + ": " + t);
}
}
}
this the console output for input like "8000":
===--- Part 3 ---===
Population in 2012: 7084.000 Population growth rate : 1.2
Population in 2013: 7169.008 Population growth rate : 1.2
Population in 2014: 7255.036 Population growth rate : 1.2
Population in 2015: 7342.097 Population growth rate : 1.2
Population in 2016: 7430.202 Population growth rate : 1.2
Population in 2017: 7519.364 Population growth rate : 1.2
Population in 2018: 7609.596 Population growth rate : 1.2
Population in 2019: 7700.912 Population growth rate : 1.2
Population in 2020: 7793.323 Population growth rate : 1.2
Population in 2021: 7886.842 Population growth rate : 1.2
Population in 2022: 7981.485 Population growth rate : 1.2
Population in 2023: 8077.262 Population growth rate : 0.6
It seems you have two variables in the while loop that you try to compare however these do not actually get updated while iterating in the while loop. So after every iteration within the loop, the while condition just stays true.
See the last few lines below for a minor change to your part 3 code :
System.out.println("===--- Part 3 ---===");
t = 1.2;
pStart = 7.000;
pEnd = pStart * Math.exp(nbr * t);
while (pStart < pTarget){
startYear++;
pEnd = pStart + (pStart * 0.012);
if (pEnd >= pStart * 2 ){
System.out.println("Population in " + startYear + ": " + nf.format((pEnd)) + " Population growth rate " + ": " + (t / 2));
} else {
System.out.println("Population in " + startYear + ": " + nf.format((pEnd)) + " Population growth rate " + ": " + t);
}
pStart = startYear; // <-- NEW, you can probably use pStart instead of startYear in this code
}
The only missing thing I see in your code is that you are not updating pStart every time you go through the loop. This will not only make your loop infinite but it's also doing a wrong calculation every time except the first iteration. I added only one line from your code:
System.out.println("===--- Part 3 ---===");
t = 1.2;
pStart = 7.000;
pEnd = pStart * Math.exp(nbr * t);
while (pStart < pTarget){
startYear++;
pEnd = pStart + (pStart * 0.012);
if (pEnd >= pStart * 2 ){
System.out.println("Population in " + startYear + ": " + nf.format((pEnd)) + " Population growth rate " + ": " + (t / 2));
}else{
System.out.println("Population in " + startYear + ": " + nf.format((pEnd)) + " Population growth rate " + ": " + t);
}
pStart = pEnd;
}

how to print an array elements index position

let say I have an array of 31 element each holds expenses of a day of January for example; assuming that I don't have any expenses in certain days.
how can print the element and the index or subscript of that element.
for example day 25 has $100
day 31 has $200
but it print like this
day 1 $100
day 2 $200
for(daysCounter=0; daysCounter<31; daysCounter++) {
if(januaryExpenses[daysCounter]>0) {
System.out.println(" expense for day " + (daysCounter +1) + " is $" + januaryExpenses[daysCounter]);
monthTotal=monthTotal+januaryExpenses[daysCounter];
} else {
System.out.print("");
}
}
System.out.println("Your January expenses are: $" +monthTotal);
The issue is you were insert to array $100 in 0 index not in 24 index and insert $200 in 1 index not in 30 index for that you have:
day 1 $100
day 2 $200
You need to insert data with value 0 in range 0-23 index and insert in 24 index insert $100 and so on with $200.
It seems like you are adding value only for the day which holds expenses i. e.
int[] januaryExpenses = {100,200};
instead of your array januaryExpenses must contain 31 elements. i. e.
int[] januaryExpenses = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,200};
then your code will work
int[] januaryExpenses = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,200};
for(int daysCounter=0; daysCounter<31; daysCounter++) {
if(januaryExpenses[daysCounter]>0) {
System.out.println(" expense for day " + (daysCounter +1) + " is $" + januaryExpenses[daysCounter]);
monthTotal=monthTotal+januaryExpenses[daysCounter];
} else {
System.out.print("");
}
}
System.out.println("Your January expenses are: $" +monthTotal);

Implementing unit conversions [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I am working on a function where I am calculating meter units to amount. Rules for meter calcualtions are :
Minimum fare on 1.6km amt - 12/-
Subsequent 1 km amt - 7/-
25% additional fare for journey for midnight
The function I have written is :
public static void CalculateAutoFare(int kmIndex) {
double fare = 12.00;
double night = 15.00;
double subseIncre = 1.50;
double nightIncre = 0.25;
int j=0;
for (int i=1; i <= kmIndex; i++) {
if (i == 3 || j == 4) {
fare += 1.00f;
j =0;
}
else {
fare += subseIncre;
j++;
}
fare = Math.round(fare*2f)/2f;
double extra = ((double) fare * nightIncre);
night = fare + extra;
night = Math.round(night*2f)/2f;
System.out.println("I = " + i + " Fare = " + fare + " Night = " + night + " 25%Extra = " + extra);
}
System.out.println("Day = " + fare + " Night = " + night);
}
kmIndex is the index of km. Meter readings are as 1.00, 1.10, 1.20, 1.30...1.90, 2.00.... Thus kmIndex for 1.00 = 0, 1.10 = 1, 1.20 - 3 and so on.
Results that I get as per code and should be is mentioned below :
I have worked till 4.00 and where the results are not right are declared undet Should Be of relevant to Day or Night.
I worked a lot on this but couldn't get results as expected. If I try to correct 1 thing then other gets wrong or doesn't give expected results. Can anyone help me out with this. Have spent almost whole day trying to solve this.
I've analyzed the worksheet and I haven't found a formula that gives the results shown there. It seems that some values have some rounding differences. I've realized that only the values with 25 or 75 cents were different. For example, you calculated 18,5 and it should be 19 (the result was 18,75 and you rounded down to 18,5, but it should be rounded up to 19).
So, if you don't have the original formula that was used to create the table, I think the only way to be sure that the results will match the worksheet is to hardcode it. It's not an elegant solution, but it guarantees the correct result.
Small tweak in the code did the trick. You wanted .75 round as .5 so instead of multiply by 2 multiply by it's immediate smaller float.
{
double fare = 12.00;
double night = 15.00;
double subseIncre = 1.50;
double nightIncre = 0.25;
int j=0;
for (int i=1; i <= 22; i++) {
if (i == 3 || j == 4) {
fare += 1.00f;
j =0;
}
else {
fare += subseIncre;
j++;
}
double extra = ((double) fare * nightIncre);
night = fare + extra;
;
System.out.println("I = " + i + " Fare = " + fare + " Night = " + night + " 25%Extra = " + extra);
}
System.out.println("actual Day = " + fare + " Night = " + night);
fare = Math.round(fare*1.99f)/2f;
night = Math.round(night*1.99f)/2f;
System.out.println("rounded Day = " + fare + " Night = " + night);
}

Categories

Resources