Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I wrote a program which constituted by several class,but the calculation is too slow(Program in bold), I hope get my java program running on GPU to speed up the computation,or is there another way to speed up the running speed,How do I change my code?
Calculation of the program are as follows:
public class ComputeThreadPool {
public static double[][] distance = new double[40][8];
public static HashMap<String,Double> simMap = new HashMap<String,Double>();
static class WorkThread implements Runnable {
private Map<String, Double> testWordTFMap;
private Map<String, Double> trainWordTFMap;
private Map<String, double[]> words;
private String trainname;
public WorkThread(Map<String, Double> map1, Map<String, Double> map2, Map<String, double[]> words,String trainname) {
this.testWordTFMap = map1;
this.trainWordTFMap = map2;
this.words = words;
this.trainname=trainname;
}
#Override
public void run() {
System.out.println(Thread.currentThread().getName()+" Start. Command = "+command);
double mul = 0, testAbs = 0, trainAbs = 0;
WordsSimilarity computeS = new WordsSimilarity();
double wf = 0;
Set<Map.Entry<String, Double>> testWordTFMapSet = testWordTFMap.entrySet();
for (Iterator<Map.Entry<String, Double>> it = testWordTFMapSet.iterator(); it.hasNext(); ) {
Map.Entry<String, Double> me = it.next();
Set<Map.Entry<String, Double>> trainWordTFMapSet = trainWordTFMap.entrySet();
***for (Iterator<Map.Entry<String, Double>> it2 = trainWordTFMapSet.iterator(); it2.hasNext(); ) {
Map.Entry<String, Double> me2 = it2.next();
wf = computeS.similarity(me.getKey(), me2.getKey(), words);
if (wf > 0.45)
mul += wf * me.getValue() * me2.getValue();
}
}***
for (Iterator<Map.Entry<String, Double>> it3 = testWordTFMapSet.iterator(); it3.hasNext(); ) {
Map.Entry<String, Double> me3 = it3.next();
testAbs += me3.getValue() * me3.getValue();
}
testAbs = Math.sqrt(testAbs);
Set<Map.Entry<String, Double>> trainWordTFMapSet = trainWordTFMap.entrySet();
for (Iterator<Map.Entry<String, Double>> it4 = trainWordTFMapSet.iterator(); it4.hasNext(); ) {
Map.Entry<String, Double> me4 = it4.next();
trainAbs += me4.getValue() * me4.getValue();
}
trainAbs = Math.sqrt(trainAbs);
simMap.put(trainname,mul / (testAbs * trainAbs));
System.out.println(Thread.currentThread().getName() + " Start. " );
processCommand();
System.out.println(Thread.currentThread().getName() + " End.");
}
private void processCommand() {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static HashMap<String,Double> main(Map<String, Double> testWordTFMap,Map<String, TreeMap<String, Double>> trainFileNameWordTFMap,Map<String, double[]> words) {
int num=0;
ExecutorService executor = Executors.newFixedThreadPool(6);
Set<Map.Entry<String,TreeMap<String,Double>>> trainFileNameWordTFMapSet = trainFileNameWordTFMap.entrySet();
for(Iterator<Map.Entry<String,TreeMap<String,Double>>> it = trainFileNameWordTFMapSet.iterator(); it.hasNext();){
Map.Entry<String, TreeMap<String,Double>> me = it.next();
num=num++;
Runnable worker = new WorkThread(testWordTFMap,me.getValue(),words,me.getKey());
executor.execute(worker);
}
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("Finished all threads");
return simMap;
}
}
wf is calculated as follows:
public static double similarity(String word1, String word2,Map<String, double[]> words) {
double[] count1=words.get(word1);
double[] count2=words.get(word2);
double sum=0;
double Abs1=0;
double Abs2=0;
if(count1 == null || count2 == null) {
return 0;
}
for (int c = 0; c < count1.length; c++) {
sum += count1[c] * count2[c];
Abs1 += count1[c] * count1[c];
Abs2 += count2[c] * count2[c];
}
return sum / (Abs1 * Abs2);
}
You would need to find an implementation of the JVM that runs on the GPU or a runtime environment/shell that targets the GPU in which you can run the standard JVM; but unless the JVM is built for the GPU you may or may not get performance gains.
However I would say, you should be able to find optimisations within the code first. Such as using enhanced for loops. Other than the compute word similarity there doesn't seem to much there that should be causing excessive run time.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 months ago.
Improve this question
I have a wrapped in class Marks HashMap which is subjectMark
public class Marks{
private final Map<String, Integer> subjectMark = new HashMap<>();
}
and I want somehow to add new maps and iterate over this HashMap in my general class RecordBook.
public class RecordBook {
private final Map<Integer, Marks> semesterSubjectMark = new HashMap<>();
public void addSemester(int semester, String subject, int mark){
//how to add such map - Map<semester, Map<subject, mark>>
//semesterSubjectMark.put(semester, Map<subject, mark>)
}
public void gpa(){
for(var semester : semesterSubjectMark.entrySet()){
//for(var subject : (inner map which wrapped in class Marks).entrySet()){
// ...
// }
}
}
}
I think that I should implement methods to add new maps like
public void addSubjectMark(String subject, int mark){
subjectMark.put(subject, mark);
}
in Marks. But how to iterate over all these maps?
I tried to iterate over with for-each, but it doesn't work for inner Map.
public class Marks {
private final Map<String, Integer> subjectMark = new HashMap<>();
public void addSubjectMark(String subject, int mark) {
subjectMark.put(subject, mark);
}
public Map<String, Integer> getSubjectMark() {
return subjectMark;
}
}
Above is Marks;
public class RecordBook {
private final Map<Integer, Marks> semesterSubjectMark = new HashMap<>();
public void addSemester(int semester, String subject, int mark) {
Marks marks = semesterSubjectMark.get(semester);
if (marks == null) {
marks = new Marks();
semesterSubjectMark.put(semester, marks);
}
marks.addSubjectMark(subject, mark);
}
public void gpa() {
int totalMark = 0;
int totalCredit = 0;
for (Map.Entry<Integer, Marks> entry : semesterSubjectMark.entrySet()) {
Marks marks = entry.getValue();
for (Map.Entry<String, Integer> entry1 : marks.getSubjectMark().entrySet()) {
String subject = entry1.getKey();
int mark = entry1.getValue();
int credit = 0;
if (subject.equals("Math")) {
credit = 2;
}
if (subject.equals("English")) {
credit = 1;
}
// It's something like above if's, but I don't want to write them all.
totalMark += mark * credit;
totalCredit += credit;
}
}
System.out.println("totalCredit:" + totalCredit);
System.out.println("totalCredit:" + totalCredit);
System.out.println("avg:" + totalMark / totalCredit);
}
}
Here u go brother, not sure if method GPA() is what u want, but that's how I sees it;
To iterate over the nested maps, you should retrieve appropriate subjectMark from the map entry:
public void gpa() {
for (var semester : semesterSubjectMark.entrySet()) {
// semester is Map.Entry<Integer, Marks>
for (var subject : semester.getValue().getSubjectMark().entrySet()) {
// subject is Map.Entry<String, Integer>
}
}
To add an entry to the outer map:
public void addSemester(int semester, String subject, int mark) {
semesterSubjectMark.computeIfAbsent(semester, (k) -> new Marks())
.getSubjectMark().put(subject, mark);
}
I'm trying to use ojAlgo library in Java for Integer Optimization but I'm unable to provide it the objective function I intend to.
I'd like to minimize the function: (A - B.X)'(A - B.X), where A is a (n x 1) matrix, B is a (n x n) diagonal matrix and X is a (n x 1) matrix with the optimization variables. I want the result in X to consist of only integers .
I was able to set a different objective function which was to maximize B.X. How do I change it to (A - B.X)'(A - B.X)? Here is the code so far.
import org.apache.log4j.Logger;
import org.ojalgo.optimisation.Expression;
import org.ojalgo.optimisation.ExpressionsBasedModel;
import org.ojalgo.optimisation.Optimisation;
import org.ojalgo.optimisation.Variable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.InputMismatchException;
import java.util.List;
public class AllocationOptimization {
protected Logger log = Logger.getLogger(AllocationOptimization.class);
// This is the objective function, since weight() is assigned to it. How to change this objective function to what I want?
private List<Variable> makeVariables(HashMap<String, BigDecimal> matrixB) {
List<Variable> result = new ArrayList<>();
for (String assetName : matrixB.keySet()) {
result.add(new Variable(assetName).weight(matrixB.get(assetName)));
}
return result;
}
private ExpressionsBasedModel createExpressionModel(List<Variable> variables) {
final ExpressionsBasedModel model = new ExpressionsBasedModel();
for (Variable v : variables) {
model.addVariable(v);
}
return model;
}
private void addExpressionConstraints(ExpressionsBasedModel model, List<Variable> variables,
HashMap<String, BigDecimal> matrixB,
HashMap<String, BigDecimal> wantedAbsoluteSharesMap,
BigDecimal idealTotalPrice) {
Expression expression = model.addExpression("C1").upper(idealTotalPrice);
int i = 0;
for (String assetName : matrixB.keySet()) {
expression.set(variables.get(i), matrixB.get(assetName));
i += 1;
}
for (Variable v : variables) {
long absShares = wantedAbsoluteSharesMap.get(v.getName()).longValue();
v.lower((long) Math.max(0, 0.8 * absShares)).upper((long) Math.max(Math.max(0, 1.2 * absShares), 5));
}
}
private void setIntegerSolving(ExpressionsBasedModel model) {
for (Variable v : model.getVariables()) {
v.setInteger(true);
}
}
private HashMap<String, Long> getIntegerOptimizationResult(ExpressionsBasedModel model, HashMap<String, BigDecimal> matrixB) {
Optimisation.Result result = model.maximise();
return prepareResult(result, matrixB);
}
private HashMap<String, Long> prepareResult(Optimisation.Result result, HashMap<String, BigDecimal> matrixB) {
int i = 0;
HashMap<String, Long> optimizedResult = new HashMap<>();
BigDecimal sumAssetPrices = new BigDecimal("0.0");
for (String assetName : matrixB.keySet()) {
long sharesCount = result.get(i).longValue();
log.debug(assetName + ": " + sharesCount);
optimizedResult.put(assetName, sharesCount);
sumAssetPrices = sumAssetPrices.add(matrixB.get(assetName).multiply(BigDecimal.valueOf(sharesCount)));
i += 1;
}
log.debug("Total assets value after converting shares to integer numbers: " + sumAssetPrices);
return optimizedResult;
}
public HashMap<String, Long> optimizeSharesCount(HashMap<String, BigDecimal> constraint1,
HashMap<String, BigDecimal> matrixB,
BigDecimal constraint2) throws InputMismatchException {
List<Variable> variableList = makeVariables(matrixB);
ExpressionsBasedModel model = createExpressionModel(variableList);
addExpressionConstraints(model, variableList, matrixB, constraint1, constraint2);
setIntegerSolving(model);
HashMap<String, Long> resultMap = getIntegerOptimizationResult(model, matrixB);
return resultMap;
}
private HashMap<String, BigDecimal> createWantedAbsoluteSharesTest1() {
HashMap<String, BigDecimal> absShares = new HashMap<>();
absShares.put("NFLX", new BigDecimal("2"));
absShares.put("MSFT", new BigDecimal("4"));
absShares.put("GOOG", new BigDecimal("0"));
absShares.put("AAPL", new BigDecimal("25"));
return absShares;
}
private HashMap<String, BigDecimal> createAssetPricesMapTest1() {
HashMap<String, BigDecimal> assetPrices = new HashMap<>();
assetPrices.put("NFLX", new BigDecimal("601.06"));
assetPrices.put("MSFT", new BigDecimal("296.75"));
assetPrices.put("GOOG", new BigDecimal("2843.78"));
assetPrices.put("AAPL", new BigDecimal("149.07"));
return assetPrices;
}
public static void main(String[] args) {
AllocationOptimization allocationOptimization = new AllocationOptimization();
// For testing
HashMap<String, BigDecimal> constr1 = allocationOptimization.createWantedAbsoluteSharesTest1();
HashMap<String, BigDecimal> matrixB = allocationOptimization.createAssetPricesMapTest1();
BigDecimal constr2 = new BigDecimal("5348.25");
HashMap<String, Long> optimizedResult = null;
try {
optimizedResult = allocationOptimization.optimizeSharesCount(constr1, matrixB, constr2);
} catch (Exception e) {
e.printStackTrace();
}
assert optimizedResult != null;
allocationOptimization.log.info("optimizedResult size: " + optimizedResult.size());
}
}
You assigned weights to the Variable:s. That makes them part of the objective function. You can also assign weights to Expression:s. Anything/everything that has a weight is summed up to form the objective function.
Expression objective = model.addExpression("Whole Objective").weight(BigDecimal.ONE);
for (Variable variableR : variables) {
objective.set(variableR, linearParameter);
for (Variable variableC : variables) {
objective.set(variableR, variableC, quadraticParameter);
}
}
Is equivalent to:
Expression objective = model.addExpression("Objective Part").weight(BigDecimal.ONE);
for (Variable variableR : variables) {
variableR.weight(linearParameter);
for (Variable variableC : variables) {
objective.set(variableR, variableC, quadraticParameter);
}
}
I modified the objective function and added necessary constraints, following #apete's comments. Posting my solution here for others.
private List<Variable> makeVariables(HashMap<String, BigDecimal> matrixB) {
List<Variable> result = new ArrayList<>();
for (String assetName : matrixB.keySet()) {
result.add(new Variable(assetName));
}
return result;
}
private ExpressionsBasedModel createObjective(ExpressionsBasedModel model, List<Variable> variables,
HashMap<String, BigDecimal> matrixA,
HashMap<String, BigDecimal> matrixB) {
// Anything and everything with that has a weight is summed up to form the objective function
Expression objective = model.addExpression("Objective function").weight(BigDecimal.ONE);
for (Variable variable : variables) {
String assetName = variable.getName();
objective.set(variable, new BigDecimal("-2").multiply(matrixA.get(assetName)).multiply(matrixB.get(assetName)));
objective.set(variable, variable, matrixB.get(assetName).pow(2));
}
return model;
}
private void addExpressionConstraints(ExpressionsBasedModel model, List<Variable> variables,
HashMap<String, BigDecimal> matrixB,
HashMap<String, BigDecimal> wantedAbsoluteSharesMap,
HashMap<String, BigDecimal> matrixA,
BigDecimal idealTotalPrice, BigDecimal accountBalance) {
Expression expression1 = model.addExpression("C1").upper(idealTotalPrice);
for (Variable variable : variables) {
expression1.set(variable, matrixB.get(variable.getName()));
}
for (Variable v : variables) {
// No negative values constraint
v.lower(0);
}
// This constraint is used to compensate for the constants arising in the quadratic objective function
BigDecimal sumSquaresUserAllocation = new BigDecimal("0.0");
for (String assetName : this.assetsList) {
sumSquaresUserAllocation = sumSquaresUserAllocation.add(matrixA.get(assetName).pow(2));
}
Expression expression2 = model.addExpression("C2").upper(new BigDecimal("1.01").multiply(sumSquaresUserAllocation.multiply(new BigDecimal("-1"))));
expression2.lower(new BigDecimal("0.99").multiply(sumSquaresUserAllocation.multiply(new BigDecimal("-1"))));
for (Variable variable : variables) {
String assetName = variable.getName();
expression2.set(variable, new BigDecimal("-2").multiply(matrixA.get(assetName)).multiply(matrixB.get(assetName)));
expression2.set(variable, variable, matrixB.get(assetName).pow(2));
}
}
Finally, instead of using the model.maximise() function, I used model.minimise() to minimize the objective function.
I am working on a problem I came across in an interview.
Input contains Population|City|State|Interstates list
Output needs to be sorted in descending order by population first, then alphabetically by city and state, and then the interstates need to be sorted in ascending order too.
Sample input:
27|Chicago|Illinois|I-94;I-90;I-88;I-57;I-55
83|New York|New York|I-78;I-95;I-87;I-80
15|Phoenix|Arizona|I-10;I-17;I-8
15|Philadelphia|Pennsylvania|I-95;I-76
Sample output:
83
New York, New York
Interstates: I-78, I-80, I-87, I-95
27
Chicago, Illinois
Interstates: I-55, I-57, I-88, I-90, I-94
15
Philadelphia, Pennsylvania
Interstates: I-76, I-95
Phoenix, Arizona
Interstates: I-8, I-10, I-17
Here's my approach so far. I am currently stuck in the if block where I've added a comment. I am not sure if I am going in the right direction. I am looking for a hint to take the right approach here.
Scanner sc = new Scanner(System.in);
String line;
List<String> al = new ArrayList<>();
//Outer map sorts reverse by population, inner map1 sorts by city, inner
map2 sorts by state
Map<Integer, Map<String, Map<String, String>>> outerMap = new TreeMap<>
(Collections.reverseOrder());
Map<String, Map<String, String>> innerMap1 = new TreeMap<>();
Map<String, String> innerMap2 = new TreeMap<>();
while(sc.hasNextLine() && (line = sc.nextLine()).length()!=0) {
//Ignore if input contains this character
if(line.contains("#")) {
line = sc.nextLine();
}
al.add(line);
}
for(int i = 0; i < al.size(); i++) {
int outerMapKey = Integer.parseInt(al.get(i).split("\\|")[0]);
String innerMap1Key = al.get(i).split("\\|")[1];
String innerMap2Key = al.get(i).split("\\|")[2];
String value = al.get(i);
outerMap.get(outerMapKey);
if(outerMap.containsKey(outerMapKey)) {
innerMap1 = outerMap.get(outerMapKey);
/* Logic to put values in inner maps
This is going to get very convoluted, not sure if I have the
right approach
*/
}
else {
innerMap1 = new TreeMap<>();
innerMap2 = new TreeMap<>();
innerMap2.put(innerMap2Key, value);
innerMap1.put(innerMap1Key, innerMap2);
outerMap.put(outerMapKey, innerMap1);
}
}
Thank you for all your help so far. I am posting my code (working now) based on feedback here. Please take a look and suggest how it can be improved.
public static void main(String[] args) {
Map<String, List<PopulationByCityState>> map = readAndProcessInput();
printSortedOutput(map);
}
private static Map<String, List<PopulationByCityState>> readAndProcessInput() {
Map<String, List<PopulationByCityState>> map = readInput();
sortByPopulationCityAndState(map);
return map;
}
private static Map<String, List<PopulationByCityState>> readInput() {
System.out.println("Enter input:");
Scanner sc = new Scanner(System.in);
String line;
Map<String, List<PopulationByCityState>> map = new TreeMap<>(Collections.reverseOrder());
while (sc.hasNextLine() && (line = sc.nextLine()).length() != 0) {
if (line.contains("#")) {
line = sc.nextLine();
}
populateMap(line, map);
}
return map;
}
private static void populateMap(String line, Map<String, List<PopulationByCityState>> map) {
String[] s = line.split("\\|");
String[] is = s[3].split(";");
String key = s[0];
PopulationByCityState p = new PopulationByCityState();
p.setPopulation(Long.parseLong(s[0]));
p.setCity(s[1]);
p.setState(s[2]);
List<String> interstates = new ArrayList<>();
for (String aString : is) {
interstates.add(aString);
}
sortInterstates(interstates);
p.setInterstates(interstates);
if (map.containsKey(key)) {
map.get(key).add(p);
} else {
List<PopulationByCityState> al = new ArrayList<>();
al.add(p);
map.put(key, al);
}
}
private static void sortInterstates(List<String> interstates) {
Collections.sort(interstates, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
int n1 = Integer.parseInt(o1.split("-")[1]);
int n2 = Integer.parseInt(o2.split("-")[1]);
return n1 - n2;
}
});
}
private static void sortByPopulationCityAndState(Map<String, List<PopulationByCityState>> map) {
for (Map.Entry entry : map.entrySet()) {
List<PopulationByCityState> list = (List<PopulationByCityState>) entry.getValue();
Collections.sort(list, new Comparator<PopulationByCityState>() {
#Override
public int compare(PopulationByCityState o1, PopulationByCityState o2) {
int c;
c = (int) (o2.getPopulation() - o1.getPopulation());
if (c == 0) {
c = o1.getCity().compareTo(o2.getCity());
}
if (c == 0) {
c = o1.getState().compareTo(o2.getState());
}
return c;
}
});
}
}
private static void printSortedOutput(Map<String, List<PopulationByCityState>> map) {
for (Map.Entry<String, List<PopulationByCityState>> entry : map.entrySet()) {
System.out.println(entry.getKey());
System.out.println();
List<PopulationByCityState> list = entry.getValue();
for (PopulationByCityState p : list) {
System.out.println(p.getCity() + ", " + p.getState());
List<String> interstates = p.getInterstates();
System.out.print("Interstates: ");
int s = 0;
for (String is : interstates) {
s++;
System.out.print(is);
if (s != interstates.size()) {
System.out.print(", ");
}
}
System.out.println();
System.out.println();
}
}
}
Your approach relies on over complicated and not meaningful structure and also uses a Comparator that will only sort the first level of the map :
Map<Integer, Map<String, Map<String, String>>> outerMap = new TreeMap<>
(Collections.reverseOrder());
A finer approach could rely on using a class that represents each individual information that you need to represent a population for a state : PopulationForState
Here is a very simple representation of it (that is of course improvable but that should help you to understand the logic) :
public class PopulationForState{
private long population;
private String city;
private String state;
private List<String> interstates;
...
// getters
}
Add instances of them in a List and use a comparator that sorted them in descending order by population first, then alphabetically by city and state.
The interstates field may be sorted independently or directly during the sort of outer elements.
You could provide a sort method in PopulationForState, for example sortInnerStates() that sorts them in ascending order.
Personally, I would make it independently to keep the processing less coupled between.
So you could write something like :
List<PopulationForState> populationForStates = new ArrayList<>();
populationForStates.add(new PopulationForState(...));
populationForStates.add(new PopulationForState(...));
Collection.sort(populationForStates, Comparator.comparing(PopulationForState::population).reversed()
.thenComparing(PopulationForState::getCity)
.thenComparing(PopulationForState::getState);
populationForStates.stream()
.forEach(PopulationForState::sortInnerStates);
If you have a structure such the one posted in above post:
public class PopulationForState{
public long population;
public String city;
public String state;
public List<String> interstates;
//Do encapsulate
}
You can sort it with one comparator:
Collections.sort(populatisForStates, new Comparator<PopulationForState>(){
public int compare(PopulationForState first, PopulationForState scnd) {
int compare = first.population - scnd.population;
if(compare != 0) return compare;
compare = first.city.compareTo(scnd.city);
if(compare != 0) return compare;
return first.state.compareTo(scnd.state);
}
});
Sorting Interstates is similar and you just need to use Collections.sort(interstates) on each instance.
How can I return the key of the smallest difference after substracting two values from a hashmap?
Example
first loop -> 10 - 8 = 2;
second loop -> 10 - 9 = 1;
third loop -> 10 - 7 = 3;
therefore second loop -> 10 - 9 = 1 is the smallest, so the key is "Three".
Code
import java.util.HashMap;
public class Difference {
HashMap<String,Double> hashMap = new HashMap<>();
double firstValue = 0;
double secondValue = 0;
double difference = 0;
public Difference() {
hashMap.put("One", 10.0);
hashMap.put("Two", 8.0);
hashMap.put("Three", 9.0);
hashMap.put("Four", 7.0);
firstValue = hashMap.get("One");
for (String key : hashMap.keySet()) {
if(!key.equals("One")) {
secondValue = hashMap.get(key);
difference = Math.abs(secondValue - firstValue);
}
}
}
public static void main(String[] args) {
new Difference();
}
}
Please Help. Thanks.
You can achieve that using something like the following:
public class MainTest {
public static void main(String[] args) {
HashMap<String,Double> hashMap = new HashMap<>();
hashMap.put("One", 10.0);
hashMap.put("Two", 8.0);
hashMap.put("Three", 9.0);
hashMap.put("Four", 7.0);
hashMap.put("Five", 10.1);
System.out.println(getSmallestDiffKeyJava8(hashMap, "One"));
}
/* This works only with java 8 */
private static String getSmallestDiffKeyJava8(Map<String, Double> map, String constantKey) {
double constant = map.get(constantKey);
return map.entrySet().stream()
.filter(entry -> !constantKey.equals(entry.getKey())) // Remove the constant from the values we process
.map(entry -> new SimpleEntry<>(entry.getKey(), Math.abs(entry.getValue() - constant))) // Map to a new entry with the key and the diff
.min((o1, o2) -> (int)(o1.getValue() - o2.getValue())) // Find the min
.map(Entry::getKey)
.get();
}
/* This works with older versions as well */
private static String getSmallestDiffKey(Map<String, Double> map, String constantKey) {
double constant = map.get(constantKey);
String key = null;
Double diff = null;
for (Entry<String, Double> entry : map.entrySet()) {
if (!constantKey.equals(entry.getKey())) {
double d = Math.abs(entry.getValue() - constant);
if (diff == null || diff > d) {
diff = d;
key = entry.getKey();
}
}
}
return key;
}
}
Try to use code something like that:
String smallestKey;
if(difference !=0 && difference < Math.abs(secondValue - firstValue);){
difference = Math.abs(secondValue - firstValue);
smallestKey = key;
}
I think your problem is simpler than you think. I know it's not the best one but here's a solution :
import java.util.HashMap;
public class Difference {
HashMap<String,Double> hashMap = new HashMap<>();
double firstValue = 0;
double secondValue = 0;
double difference = 0;
HashMap<Double, String> theMap = new HashMap<Double, String>();
public Difference() {
hashMap.put("One", 10.0);
hashMap.put("Two", 8.0);
hashMap.put("Three", 9.0);
hashMap.put("Four", 7.0);
firstValue = hashMap.get("One");
for (String key : hashMap.keySet()) {
if(!key.equals("One")) {
secondValue = hashMap.get(key);
difference = Math.abs(secondValue - firstValue);
theMap.put(difference, key);
}
}
Set<Double> dbl = theMap.keySet();
Double smallestDifference = findSmallest(dbl);
String smallestValue = hashMap.get(smallestDifference);
}
public Double findSmallest(Set<Double> setDbl){
Double smallest = 99999999.0;
for(Double d : setDbl){
if(d < smallest)
smallest = d;
}
return smallest;
}
public static void main(String[] args) {
new Difference();
}
}
I'm working on emulator of ATM in java. The overall pattern in project is Command.
So I have 4 commands - getInfo, deposit,withdraw and exit.
I'm facing problems with an implementation of greedy algorithm in withdrawal method. It should return Map were first Integer is "denomination" and second Integer is "amount" left in ATM after we withdrew.
public Map<Integer, Integer> withdrawAmount(int expectedAmount)
So it takes expected amount as an argument and has to subtract it from ATM with the least possible amount of bills.
public class CurrencyManipulator
{
// denominations is a map where each denomination and it's quantity stored
private String currencyCode;
private Map<Integer, Integer> denominations = new HashMap<>();
public String getCurrencyCode()
{
return currencyCode;
}
public CurrencyManipulator(String currencyCode)
{
this.currencyCode = currencyCode;
}
public void addAmount(int denomination, int count)
{
if (denominations.containsKey(denomination))
{
denominations.put(denomination, denominations.get(count) + count);
} else
{
denominations.put(denomination, count);
}
}
public int getTotalAmount()
{
int sum = 0;
for (Map.Entry<Integer, Integer> pair : denominations.entrySet())
{
sum = pair.getKey() * pair.getValue();
}
return sum;
}
public boolean hasMoney()
{
return denominations.size() != 0;
}
public boolean isAmountAvailable(int expectedAmount)
{
return expectedAmount <= getTotalAmount();
}
public Map<Integer, Integer> withdrawAmount(int expectedAmount) throws NotEnoughMoneyException
{
}
}
So I need this method to return a map or throw exception if amount asked "expectedAmount" is higher then money available in ATM.
If we take $600 it could be - three bills: $500 + $50 + $50 OR $200 + $200 + $200, the preferred option is $500 + $50 + $50
Example, you have to give $600
The ATM has the following bill-count:
500 - 2
200 - 3
100 - 1
50 - 12
The result should be:
500 - 1
100 - 1
This what I came up with:
public Map<Integer, Integer> withdrawAmount(int expectedAmount) throws NotEnoughMoneyException
{
denominations.put(50,1);
denominations.put(500,1);
denominations.put(200,3);
HashMap<Integer, Integer> map = new HashMap<>();
TreeMap<Integer, Integer> sortedMap = new TreeMap<>(Collections.reverseOrder());
sortedMap.putAll(denominations);
ArrayList<Integer> bills = new ArrayList<>();
bills.addAll(sortedMap.keySet());
int num;
for (int i = 0; i < bills.size(); i++)
{
if (bills.get(i) <= expectedAmount)
{
num = expectedAmount / bills.get(i);
map.put(bills.get(i), num);
expectedAmount -= num * bills.get(i);
}
}
System.out.println(map);
return map;
}
It returns the map of needed bills and their quantity.
Now my question is..how do i compare it with the "denominations" map i have and subtract new map from it?
seems to be working code if someone ever needs it
public Map<Integer, Integer> withdrawAmount(int expectedAmount) throws NotEnoughMoneyException
{
denominations.put(50,2);
denominations.put(500,1);
denominations.put(100,1);
HashMap<Integer, Integer> map = new HashMap<>();
TreeMap<Integer, Integer> sortedDenominations = new TreeMap<>(Collections.reverseOrder());
sortedDenominations.putAll(denominations);
ArrayList<Integer> bills = new ArrayList<>();
bills.addAll(sortedDenominations.keySet());
int num;
for (int i = 0; i < bills.size(); i++)
{
if (bills.get(i) <= expectedAmount)
{
num = expectedAmount / bills.get(i);
map.put(bills.get(i), num);
expectedAmount -= num * bills.get(i);
}
}
System.out.println(map);
for (Map.Entry<Integer,Integer> denominPresent:sortedDenominations.entrySet()){
int value;
for (Map.Entry<Integer,Integer> deominNeeded:map.entrySet()){
if(denominPresent.getKey().equals(deominNeeded.getKey())){
value = denominPresent.getValue()-deominNeeded.getValue();
if (value>=0) sortedDenominations.put(denominPresent.getKey(),value);
else throw new NotEnoughMoneyException();
}
}
}
System.out.println(sortedDenominations);
return sortedDenominations;
}
Another solution. Will work if you initialize the denominations variable using TreeMap
...
private Map<Integer, Integer> denominations = new TreeMap<>(Comparator.reverseOrder());
...
public Map<Integer, Integer> withdrawAmount(int expectedAmount) throws NotEnoughMoneyException {
final Map<Integer, Integer> map = new TreeMap<>(Comparator.reverseOrder());
// calculate denomination map to cash
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
int denomination = entry.getKey();
if (denomination <= expectedAmount) {
int num = Math.min(expectedAmount / denomination, entry.getValue());
map.put(denomination, num);
expectedAmount -= num * denomination;
}
if (expectedAmount == 0) {
break;
}
}
if (expectedAmount != 0) {
throw new NotEnoughMoneyException();
}
map.forEach((key, value) -> {
denominations.compute(key, (denomination, count) -> {
return (count == value) ? null : count - value;
});
});
return map;
}