I'm looking for an intelligent way to approach a version of the common bin-packing problem. Given a number of bags (as I'm calling them) with a certain capacity, and list of items that take up a certain amount of space, the task is to determine if all of the items can fit in the bags; and if so, how. I've got an exhaustive DFS working right now, but it takes... forever. My DFS is iterative and requires copying entire states at every step, which is very expensive. Here's my code for a specific problem with 4 bags with 10 capacity (the truly relevant portions of this code are just the pack() method and the State class if you don't want to look at it all):
import java.util.ArrayList;
import java.util.Stack;
public class BagProblem {
int numBags;
int bagCapacity;
ArrayList<Item> items = new ArrayList<Item>();
public static void main(String[] args) {
BagProblem bp = new BagProblem(4, 10);
bp.pack();
}
public BagProblem(int numBags, int bagCapacity) {
this.numBags = numBags;
this.bagCapacity = bagCapacity;
items = new ArrayList<Item>();
items.add(new Item("item0", 6));
items.add(new Item("item1", 6));
items.add(new Item("item2", 6));
items.add(new Item("item5", 3));
items.add(new Item("item6", 3));
items.add(new Item("item7", 3));
items.add(new Item("item8", 2));
items.add(new Item("item9", 2));
items.add(new Item("item10", 2));
items.add(new Item("item11", 2));
items.add(new Item("item12", 2));
items.add(new Item("item13", 2));
items.add(new Item("item14", 1));
}
// find a valid way to pack and print the items in each Bag, or
// print failure
public void pack() {
Stack <State> s = new Stack<State>();
Bag[] currBags = new Bag[numBags];
for (int i = 0; i < numBags; i++) {
currBags[i] = new Bag(bagCapacity);
}
s.push(new State(currBags));
while(!s.isEmpty()) {
State currState = s.pop();
for (Item i : items) {
if (!currState.containsItem(i)) {
State newState = new State(currState.bags);
newState.numItems = currState.numItems;
if (newState.addItem(i)) {
s.push(newState);
if (newState.numItems == items.size()) {
System.out.println("success");
System.out.println(newState);
return;
}
}
}
}
}
System.out.println("failure");
}
private class State {
Bag[] bags;
int numItems;
public State(Bag[] currBags) {
bags = new Bag[numBags];
for (int i = 0; i < numBags; i++) {
bags[i] = new Bag(bagCapacity);
}
// figure out how to actually copy this
for (int j = 0; j < numBags; j++) {
Bag bagToCopy = currBags[j];
for (Item item : bagToCopy.contents) {
Item newItem = new Item(item.name, item.size);
bags[j].size = bagToCopy.size;
bags[j].contents.add(newItem);
}
}
}
public boolean addItem(Item i) {
for (Bag b : bags) {
if (b.addItem(i)) {
numItems++;
return true;
}
}
return false;
}
public boolean containsItem(Item i) {
for (Bag b : bags) {
for (Item item : b.contents) {
if (item.name.equals(i.name))
return true;
}
}
return false;
}
public String toString() {
String output = "";
for (Bag b : bags) {
for (Item j : b.contents) {
output += j.name + " ";
}
output += "\n";
}
return output;
}
}
private class Bag {
int capacity;
int size;
ArrayList<Item> contents;
public Bag(int capacity) {
this.capacity = capacity;
this.size = 0;
contents = new ArrayList<Item>();
}
public boolean addItem(Item i) {
if(size + i.size > capacity)
return false;
contents.add(i);
size += i.size;
return true;
}
public String toString() {
String output = "";
for (Item i : contents) {
output += i.name + " ";
}
return output + "\n";
}
}
private class Item {
String name;
int size;
public Item(String name, int size) {
this.name = name;
this.size = size;
}
public String toString() {
return name;
}
}
}
After approximately one million years, this does spit out a correct answer (you probably won't want to actually wait that long if you try to run this):
success
item14 item7 item6 item5
item13 item12 item2
item11 item10 item1
item9 item8 item0
Each line indicates a separate bag. How can I speed this up? I know there are heuristics about trying to place the largest item first, etc., but what I'm really interested in is getting the basic DFS (or maybe I should try backtracking?) to have less overhead; I'll try to get fancier later.
Any help would be greatly appreciated.
I don't use Java but your implementation seems quite inefficient (as you've mentioned yourself) due to overcomplicating it. The algorithm itself is also very strange, I did not attempt to replicate it and just used the obvious O(bags^items) brute force algorithm that tries to put the first item into each bag, for each of those cases tries to put the second item into each bag, etc...
Instead of replicating the entire state repeatedly on the stack, you can put an item in a bag, explore the branch of the tree with this change, then take the item out of the bag.
Here is an example that completes instantly for your test case in C#.
static int[] itemSize;
static int[] bagFreeSpace;
static bool[,] doesBagContainItem; // in case this looks weird, [,] is a matrix, in java it would be [][]
static bool pack(int item)
{
// output the solution if we're done
if (item == itemSize.Length)
{
for (int i = 0; i < bagFreeSpace.Length; i++)
{
Console.WriteLine("bag" + i);
for (int j = 0; j < itemSize.Length; j++)
if (doesBagContainItem[i, j])
Console.Write("item" + j + "(" + itemSize[j] + ") ");
Console.WriteLine();
}
return true;
}
// otherwise, keep traversing the state tree
for (int i = 0; i < bagFreeSpace.Length; i++)
{
if (bagFreeSpace[i] >= itemSize[item])
{
doesBagContainItem[i,item] = true; // put item into bag
bagFreeSpace[i] -= itemSize[item];
if (pack(item + 1)) // explore subtree
return true;
bagFreeSpace[i] += itemSize[item]; // take item out of the bag
doesBagContainItem[i,item] = false;
}
}
return false;
}
static void Main(string[] args)
{
itemSize = new int[] { 6, 6, 6, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1 };
bagFreeSpace = new int[] { 10, 10, 10, 10 };
doesBagContainItem = new bool[bagFreeSpace.Length, itemSize.Length];
if (!pack(0))
Console.WriteLine("No solution");
}
Note: if you want to parallelize execution, you need to give each worker its own copy of the state (or 1 copy per job), but only at the point of branching, they can still then proceed as above, without replicating the state.
Related
So I have this project and im writing the add method for my catalog class and this add method needs to add an item to a sorted array into the right place using insertion sort, unless the array has nothing in it in that case i just want to add it in normally. this whole project must use an array I cannot use an arraylist or anything else.
The problem I am having here is that the way my program currently is, its only adding one object to my array and each time i try to add a new one during run tine it jst replaces the item already in there. I know that my problem is something in the body of my while loop and the way i initialize my position variable.
here is the method im having trouble with.
public void addItem(Item theItem)
{
int position = size;
if(size != 0){
while (position > 0 && theItem.compareTo(items[position - 1]) < 0){
items[position] = items[position - 1];
position--;
}
items[position] = theItem;
}
else{
items[size] = theItem;
size++;
}
here is my compareTo method
public int compareTo(Item other){
if(this.getItemType().equals(other.getItemType())){
return this.itemnum - other.itemnum;
}
//item types are not equal
else
return this.getItemType().compareTo(other.getItemType());
//try writing code to compare by price as well
}
The most likely problem in your code is this line:
items[position-1] = items[position];
This will copy an item in you array from the current position to the position to the left of it.
When you insert a new item you want to copy items from the left to the current position to make room for the new item to the left.
Change it to
items[position] = items[position-1];
A size++ is also missing after the while block, inside the first if block.
I realized this when adding a second call to addItem in my test code below.
You could also put a single size++ statement outside of the if statement.
A Complete, Minimal, Reproducible Example that I used trying to fix it. I have used Integer instead of Item to avoid having to add more classes.
public class Main {
private int size = 0;
private Integer[] items = new Integer[20];
public static void main(String... args) {
new Main().execute(); // Moving us into a non-static context
}
public void execute() {
System.arraycopy(new Integer[] {1,2,3,4,6,7,8,9}, 0, items, 0, 8);
size = 8;
// items = [1,2,3,4,6,7,8,9,null,null,...]
addItem(5);
addItem(5); // test adding a second item
// items = [1,2,3,4,5,6,7,8,9,null,null,...]
for (Integer i : items) {
System.out.println(i);
}
}
public void addItem(Integer item) {
int position = size;
if (size != 0) {
while (position > 0 && item.compareTo(items[position - 1]) < 0) {
// items[position-1] = items[position]; // Result [1,2,3,4,5,null,null,...]
items[position] = items[position-1]; // Result [1,2,3,4,5,6,7,8,9,null,null,...]
position--;
}
items[position] = item;
size++; // this line was missing as well
} else {
items[size] = item;
size++;
}
// or a single size++; here, removing the other two
}
}
The ugly solution by making new array
public int[] addItem(int item, int[] items){
int[] tempArr = new int[items.length + 1];
boolean hasAlready = false;
for(int i = 0 ; i < items.length; i++){
if(hasAlready)tempArr[i + 1] = items[i];
else if(item < items[i]){
tempArr[i] = item;
tempArr[i + 1] = items[i];
hasAlready = true;
}else {
tempArr[i] = items[i];
}
}
//items = tempArr; if items is global variable
return tempArr;
}
One can use existing utility functions, Arrays.binarySearch, and System.arraycopy. Your loop was 1 off.
public void addItem(Item theItem) {
Comparator<Item> comparator = Comparator.comparing(Item::getItemType)
.thenComparingInt(it -> it.itemnum);
int position = Arrays.binarySearch(items, 0, size, theItem, comparator);
// If position >= 0 the item was found (maybe no need to insert?)
if (position < 0) {
position = ~position; // Insert position of not found item
}
System.arraycopy(items, position, items, position + 1, size - position);
items[position] = theItem;
size++;
}
Binary search results in the non-negative index when found, or the negative ~index when not found. Here binary search is done on a subarray from 0 upto size (excluded).
Same as Roger Gustavsson
public class Main {
private int size = 0;
private Integer[] items = new Integer[20];
public static void main(String... args) {
new Main().execute(); // Moving us into a non-static context
}
public void execute() {
System.arraycopy(new Integer[] {1,2,3,4,6,7,8,9}, 0, items, 0, 8);
size = 8;
// items = [1,2,3,4,6,7,8,9,null,null,...]
addItem(5);
// items = [1,2,3,4,5,6,7,8,9,null,null,...]
for (Integer i : items) {
System.out.println(i);
}
}
public void addItem(Integer item) {
if (size == 0) {
items[size] = item;
size++;
return;
}
int position = size;
while (position > 0 && item.compareTo(items[position - 1]) < 0) {
items[position] = items[position - 1];
position--;
}
items[position] = item;
size++;
}
}
on what you are trying to achieve, i think next solution will be starting point from where you can build your own solution depending your specific needs. i have Changed your main method a little bit, and i do not know if your classes implements comparable /Comparator or not.
public void addItem(Item theItem) {
int position = position(items, theItem); // position is a method that finds best position for inseriton
if (items[position] == null){ // if items at best position is null then add new element there
items[position] = theItem;
} else{
items[size] = theItem; // if not add element at last position
swapUp(size); // and swap them up to perfect position.
}
size++;
}
method that find best position looks like this.
private static int position(Item[] items, Item newItem) {
if (isEmpty(items))
return 0;
int pos=0;
int target=items.length-1;
while(pos < target){
int m = pos+(target-pos)/2;
if (items[m] !=null){
if(newItem.getNumber()>items[m].getNumber()){ // comparing depending on item number
pos=m+1;
}else{
target=m;
}
}else{
target = m;
}
}
return pos;
}
as you can see method is looking for position depending on item number, you can change this with your type, or do both type and number comparison. Swaping up is handled by thus 2 method.
private void swapUp(int lastPosition){
if (lastPosition == -1){
return;
}
Item lastItem = items[lastPosition];
Item p = items[lastPosition-1];
if (lastItem.getNumber() < p.getNumber())
replace(lastPosition, lastPosition-1);
else
lastPosition = 0;
swapUp(lastPosition-1);
}
private void replace(int from, int to){
Item temporary = items[from];
items[from] = items[to];
items[to] = temporary;
}
and again i'm doing comparison of numbers you can implement any kind of comparison you want. i saw your previous question and modeled your classes
Music{number=1111, name='White and Nerdy', price=2.5, pro='"Weird Al" Yankovic'}
Music{number=2222, name='Amish Paradise', price=2.22, pro='"Weird Al" Yankovic'}
Music{number=3333, name='The Saga Begins', price=2.0, pro='"Weird Al" Yankovic'}
Movie{number=4444, name='UHF', price=9.99, pro='"Weird Al" Yankovic'}
Movie{number=5555, name='The Dark Crystal', price=8.99, pro='"Jim Henson'}
Movie{number=6666, name='Die Hard', price=13.99, pro='Bruce Willis'}
Movie{number=6969, name='The Adventures of Mr. Winky', price=9.99, pro='Richard Dickinson'}
Book{number=7777, name='When I Grow Up', price=7.98, pro='"Weird Al" Yankovic'}
Book{number=8888, name='The Chronicles of Pern: First Fall', price=5.99, pro='"Anne McCaffrey'}
Book{number=9999, name='Get gud you scrub', price=2.5, pro='Steve "Troll" Rathier'}
as you can see they are in sorted order.
I have five values in an ArrayList like {50,25,50,30,10} . I want to set ranks in every value, So please tell me how I could do this. The output is like
50->1st rank
50->1st rank
30->2nd rank
20->3rd rank
10->4th rank
you should check this and this.
It is easy to do with lambda expression which is available from java 1.8.
Here is code i have got from above reference link
List<Player> players = new ArrayList<Player>() {{
add(new Player(1L, "a", 5));
add(new Player(2L, "b", 7));
add(new Player(3L, "c", 8));
add(new Player(4L, "d", 9));
add(new Player(5L, "e", 3));
add(new Player(6L, "f", 8));
}};
int[] score = {Integer.MIN_VALUE};
int[] no = {0};
int[] rank = {0};
List<Ranking> ranking = players.stream()
.sorted((a, b) -> b.getScores() - a.getScores())
.map(p -> {
++no[0];
if (score[0] != p.getScores()) rank[0] = no[0];
return new Ranking(rank[0], score[0] = p.getScores());
})
// .distinct() // if you want to remove duplicate rankings.
.collect(Collectors.toList());
System.out.println(ranking);
// result:
// rank=1, score=9
// rank=2, score=8
// rank=2, score=8
// rank=4, score=7
// rank=5, score=5
// rank=6, score=3
Please try this code.
package snakePack;
import java.util.ArrayList;
public class MainSnake {
public static void main(String[] args) {
Snake blackMambo = new Snake("blackMambo",2,0);
Snake rattle = new Snake("rattle",9,0);
Snake green = new Snake("green",6,0);
Snake cobra = new Snake("cobra",78,0);
Snake kingCobra = new Snake("kingCobra",5,0);
Snake whiteCobra = new Snake("whiteCobra",5,0);
Snake python = new Snake("python",5,0);
Snake yellow = new Snake("yellow",5,0);
Snake blackCobra = new Snake("blackCobra",1000,0);
Snake desertCobra = new Snake("desertCobra",5,0);
ArrayList<Snake> list = new ArrayList<Snake>();
list.add(blackMambo);list.add(rattle);list.add(green);list.add(cobra);list.add(kingCobra);
list.add(whiteCobra);list.add(python);list.add(yellow);list.add(blackCobra);list.add(desertCobra);
ArrayList<Integer> mongoosebigSnakes = new ArrayList<Integer>();
Integer tempHelperFrog = 0;
Integer rankMe = 0;
for (int grossHopper = 0; grossHopper < list.size(); grossHopper++) {
Integer strongSnake = 0;
for (int spider = 0; spider < list.size(); spider++) {
if (list.get(spider).getSnakePoisionRate() > strongSnake) {
boolean bool = false;
if (mongoosebigSnakes.size() != 0) {
for (int dragonFly = 0; dragonFly < mongoosebigSnakes.size(); dragonFly++) {
if (mongoosebigSnakes.get(dragonFly).intValue() == list.get(spider).getSnakePoisionRate().intValue()) {
bool = true;
}
}
if (bool != true) {
bool = false;
strongSnake = list.get(spider).getSnakePoisionRate();
}
} else {
if (tempHelperFrog != list.get(spider).getSnakePoisionRate()) {
strongSnake = list.get(spider).getSnakePoisionRate();
}
}
}
}
tempHelperFrog = strongSnake;
mongoosebigSnakes.add(strongSnake);
++rankMe;
for (int x = 0; x < list.size(); x++) {
if (strongSnake == list.get(x).getSnakePoisionRate()) {
list.get(x).setSnakeRank(rankMe);
}
}
}
System.out.println(" Hey guys get ready to see who has strong poison >>>");
Integer ratHelperCounter = 0;
for (Snake snake : list) {
System.out.println(++ratHelperCounter + " :" + snake.toString());
}
System.out.println("it's awesome" + " huge me !!!! ");
}
}
Bean class:
package snakePack;
public class Snake {
String snakeName;
Integer snakePoisionRate;
Integer snakeRank;
public Snake(String string, int i, int j) {
this.snakeName = "";
this.snakePoisionRate = i;
this.snakeRank = j;
}
public String getSnakeName() {
return snakeName;
}
public void setSnakeName(String snakeName) {
this.snakeName = snakeName;
}
public Integer getSnakePoisionRate() {
return snakePoisionRate;
}
public void setSnakePoisionRate(Integer snakePoisionRate) {
this.snakePoisionRate = snakePoisionRate;
}
public Integer getSnakeRank() {
return snakeRank;
}
public void setSnakeRank(Integer snakeRank) {
this.snakeRank = snakeRank;
}
#Override
public String toString() {
return "Snake [snakeName=" + snakeName + ", snakePoisionRate="
+ snakePoisionRate + ", snakeRank=" + snakeRank + "]";
}
}
I 'm looking for a way to read a range of elements in an array of unknown dimension ( not length).
The client can send a read request for an object and specify the range to read. The input String could be like this : "1:2:3:2,2:3:1:4" for example. This would mean he wants to read the elements in the range from [1][2][3][2] to [2][3][1][4] of an array.
To read a concrete element I created this function:
public Object readValue(Object obj,int[] positions ) {
Object value = null; //Result
int objDimension = getDimension(obj); //Dimesion of the array
System.out.println("Dimension: " + objDimension );
try {
Object[] aux = (Object[]) obj;
for (int i = 0; i < objDimension - 1; i++) {
int pos = positions[i];
aux = (Object[]) aux[pos];
}
value = aux[positions[objDimension - 1]];
System.out.println("Result: " + value);
} catch (ArrayIndexOutOfBoundsException e) {
// TODO: Send a fault to the client.
System.out.println("Error: "+e.getMessage());
}
return value;
}
public static int getDimension(Object value) {
Class<?> clazz = value.getClass();
String className = clazz.getName();
int dimension = 0;
for (int i = 0; i < className.length(); i++) {
if (className.charAt(i) != '[') {
dimension = i;
break;
}
}
return dimension;
}
//Example.
public static void main(String[] args) {
// TODO code application logic here
TestMultiDimensioNRead test = new TestMultiDimensioNRead();
Integer[][][][] testSubject = new Integer[5][2][4][];
testSubject[0][0][2] = new Integer[8];
testSubject[0][0][0] = new Integer[15];
testSubject[0][0][1] = new Integer[20];
testSubject[0][0][3] = new Integer[2];
testSubject[1][1][2] = new Integer[7];
testSubject[1][1][2][0] = 80;
test.readValue(testSubject,new int[]{1, 1, 2, 0});
}
I was thinking a good way may be to calculate the differens between each dimension length.
If anyone can come with a good idea, I would really appreciatee.
Thanks in advance.
EDIT 1: The code posted in this question does read the value of a given position in an array of unknown dimension. My problem is to read all the elements that are between to given points. This might not have been clear in the initial question.
You could use a recursive solution:
public class Test {
private class TestMultiDimensioNRead {
public Integer readValue(Object testSubject, int[] coordinates) {
return readValue(testSubject, coordinates, 0);
}
private Integer readValue(Object testSubject, int[] coordinates, int which) {
if (testSubject instanceof Object[]) {
Object[] subject = (Object[]) testSubject;
if (coordinates.length > which + 1) {
return readValue(subject[coordinates[which]], coordinates, which + 1);
} else {
return (Integer) subject[coordinates[which]];
}
} else {
// Throw some sort of exception?
return -1;
}
}
public Iterator<Integer> readValues(Object testSubject, int[] coordinates, int count) {
return readValues(testSubject, coordinates, count, 0);
}
private Iterator<Integer> readValues(Object testSubject, int[] coordinates, int count, int level) {
if (testSubject instanceof Object[]) {
Object[] subject = (Object[]) testSubject;
if (coordinates.length > level + 1) {
return readValues(subject[coordinates[level]], coordinates, count, level + 1);
} else {
return new Iterator<Integer>() {
int i = 0;
Integer[] intSubject = (Integer[]) subject;
#Override
public boolean hasNext() {
return i <= count;
}
#Override
public Integer next() {
return intSubject[coordinates[level] + (i++)];
}
};
}
} else {
// Throw some sort of exception?
return null;
}
}
}
public void test() {
TestMultiDimensioNRead test = new TestMultiDimensioNRead();
Integer[][][][] testSubject = new Integer[5][2][4][];
testSubject[0][0][2] = new Integer[8];
testSubject[0][0][0] = new Integer[15];
testSubject[0][0][1] = new Integer[20];
testSubject[0][0][3] = new Integer[2];
testSubject[1][1][2] = new Integer[7];
testSubject[1][1][2][0] = 80;
testSubject[1][1][2][1] = 79;
testSubject[1][1][2][2] = 78;
Iterator<Integer> them = test.readValues(testSubject, new int[]{1, 1, 2, 0}, 3);
for (Integer x = them.next(); them.hasNext(); x = them.next()) {
System.out.println(x);
}
System.out.println();
}
public static void main(String args[]) {
try {
new Test().test();
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
}
Prints 80 as expected.
There's probably more to do in terms of sanity checks but this seems to work.
Found the way to do it, maybe it's helpfull at somepoint for someone.
I didn't include any checks, this is more a test case to see that is works.
public class TestReadMultiDimensionArray {
private int[] startPosition; //Start position.
private int[] endPosition; //End position.
private boolean inRange = false; //If the current position is in range.
private List<Object> result; //List to store the values we find.
public TestReadMultiDimensionArray() {
result = new ArrayList<>();
}
public static void main(String[] args) {
TestReadMultiDimensionArray test = new TestReadMultiDimensionArray();
Integer[][][][] testSubject = new Integer[2][2][4][];
//(0,0,y,z)
testSubject[0][0][0] = new Integer[]{1}; //(0,0,0,0)
testSubject[0][0][1] = new Integer[]{2}; //(0,0,1,0)
testSubject[0][0][2] = new Integer[]{3}; //(0,0,2,0)
testSubject[0][0][3] = new Integer[]{4}; //(0,0,3,0)
//(0,1,y,z)
testSubject[0][1][0] = new Integer[]{5}; //(0,1,0,0)
testSubject[0][1][1] = new Integer[]{6}; //(0,1,1,0)
testSubject[0][1][2] = new Integer[]{7, 8, 9}; //(0,1,2,0) (0,1,2,1) (0,1,2,2)
testSubject[0][1][3] = new Integer[]{10}; //(0,1,3,0)
//(1,0,y,z)
testSubject[1][0][0] = new Integer[]{11, 12}; //(1,0,0,0)..
testSubject[1][0][1] = new Integer[]{13, 14, 15};
testSubject[1][0][2] = new Integer[]{16, 17, 18};
testSubject[1][0][3] = new Integer[]{19, 20, 21}; //..(1,0,3,2)
//(1,1,y,z)
testSubject[1][1][0] = new Integer[]{22, 23}; //(1,1,0,0)..
testSubject[1][1][1] = new Integer[]{24, 25, 26};
testSubject[1][1][2] = new Integer[]{27, 28, 29, 30, 31, 32, 33, 34};
testSubject[1][1][3] = new Integer[]{35, 36}; //..(1,1,3,1)
//Launch the test.
test.readValue(testSubject);
}
/**
*
* #param obj The Array from where we want to get the data.
*/
public void readValue(Object obj) {
//Where should it start.
startPosition = new int[]{0, 1, 0, 0};
//Where should it stop.
endPosition = new int[]{1, 1, 1, 2};
System.out.println("Start Position:" + Arrays.toString(startPosition) + " End Position:" + Arrays.toString(endPosition));
int[] currentPosition = new int[]{-1, -1, -1, -1};
//Call to the method.
testRead((Object[]) obj, 0, currentPosition);
//Result to array.
Object[] arrayToReturn = result.toArray(new Object[0]);
System.out.println("Result: " + Arrays.toString(arrayToReturn));
}
/**
* Recursive method that looks for the values in a multi-dimensional array, in a given range. /!\ No checks are implemented here, wrong input can end in a
* StackOverFlow.
*
* #param obj The array in Object[] form.
* #param currentDimension The dimension we are currently in.
* #param result The reference to the list that will store all the values we found.
* #param currentPosition The current position we are in.
*/
private void testRead(Object[] obj, int currentDimension, int[] currentPosition) {
for (int i = 0; i < obj.length; i++) {
currentPosition[currentDimension] = i;
if (Arrays.equals(startPosition, currentPosition) && currentDimension == (currentPosition.length - 1)) {
//Found the start position.
System.out.println("############ START ############");
inRange = true;
}
if ((i >= startPosition[currentDimension] && i <= endPosition[currentDimension]) || inRange == true) {
//We are in the write track to get to the values we are looking for.
if (obj[i] instanceof Object[]) {
//The data contained in the cell is an array.
testRead((Object[]) obj[i], currentDimension + 1, currentPosition);
} else {
//The data contained in the cell is a scalar. This is what we where looking for.
System.out.println(Arrays.toString(currentPosition) + " Data: " + obj[i]);
result.add(obj[i]);
}
}
if (Arrays.equals(endPosition, currentPosition) && currentDimension == (currentPosition.length - 1)) {
//Found the end position.
System.out.println("############ END ############");
inRange = false;
}
}
}
}
Any question or idea to better the code is welcome.
I have an array that I want to sort in ascending order. However, I want to sort them with reference to a boolean array.I would like to sort the values that are true in ascending order, followed by the values that are false in ascending order.
Little stuck on how to get there.
This is what I have currently:
Object[] arr = new Object[6];
arr[0] = new Object(2);
arr[1] = new Object(5);
arr[2] = new Object(3);
arr[3] = new Object(1);
arr[4] = new Object(6);
arr[5] = new Object(4);
Available[] avalarr = new Available[6];
availarr[0] = new Available (true);
availarr[1] = new Available (false);
availarr[2] = new Available (false);
availarr[3] = new Available (true);
availarr[4] = new Available (true);
availarr[5] = new Available (false);
I need the output to be:
1 2 6 3 4 5
Code:
import java.util.Arrays;
public class SelectiveSort {
public static void main(String[] args) {
Item [] items = new Item [6];
items[0] = new Item(2, true);
items[1] = new Item(5, false);
items[2] = new Item(3, false);
items[3] = new Item(1, true);
items[4] = new Item(6, true);
items[5] = new Item(4, false);
System.out.println("Before Sorting:");
// Removed enhanced for loop
for(int i = 0; i < items.length; i++) {
System.out.print(items[i].getIntValue() + " ");
}
// Sorting
Arrays.sort(items);
System.out.println("\n\nAfter Sorting:");
// Removed enhanced for loop
for(int i = 0; i < items.length; i++) {
System.out.print(items[i].getIntValue() + " ");
}
System.out.println();
}
}
class Item implements Comparable<Item> {
private int _intValue;
private boolean _boolValue;
public Item(int intValue, boolean boolValue) {
_intValue = intValue;
_boolValue = boolValue;
}
public int getIntValue() { return _intValue; }
public boolean getBoolValue() { return _boolValue; }
#Override
public int compareTo(Item otherItem) {
// Using explicit comparison
int boolComparison = (_boolValue == otherItem._boolValue) ? 0 :
(_boolValue) ? 1 : -1;
return (boolComparison != 0) ? -boolComparison :
( (_intValue == otherItem.getIntValue()) ? 0 :
(_intValue > otherItem.getIntValue()) ? 1 : -1);
}
}
Output:
Before Sorting:
2 5 3 1 6 4
After Sorting:
1 2 6 3 4 5
Explanation:
The idea is to let your "Item" implement Comparable, and override the compareTo(Item otherItem) function based on the desired order.
Once that is done, all you need to do is to call Arrays.sort() on your array of Item.
Version 2 (w/o Comparable/Comparator):
public class SelectiveSort {
public static void main(String[] args) {
Item [] items = new Item [6];
items[0] = new Item(2, true);
items[1] = new Item(5, false);
items[2] = new Item(3, false);
items[3] = new Item(1, true);
items[4] = new Item(6, true);
items[5] = new Item(4, false);
System.out.println("Before Sorting:");
for(int i = 0; i < items.length; i++) {
System.out.print(items[i].getIntValue() + " ");
}
// Sorting
bubbleSort(items);
System.out.println("\n\nAfter Sorting:");
for(int i = 0; i < items.length; i++) {
System.out.print(items[i].getIntValue() + " ");
}
System.out.println();
}
public static void bubbleSort(Item [] items) {
int n = items.length;
do {
int newN = 0;
for(int i = 1; i < n; i++) {
if(compareTo(items[i-1], items[i]) == 1) {
Item temp = items[i-1];
items[i-1] = items[i];
items[i] = temp;
newN = i;
}
}
n = newN;
} while (n != 0);
}
public static int compareTo(Item item1, Item item2) {
int boolComparison = (item1.getBoolValue() == item2.getBoolValue())
? 0 : (item1.getBoolValue()) ? 1 : -1;
return (boolComparison != 0) ? -boolComparison :
( (item1.getIntValue() == item2.getIntValue()) ? 0 :
(item1.getIntValue() > item2.getIntValue()) ? 1 : -1);
}
}
(To expand on my comment:
You need a basic "thing":
class Thing {
boolean newAvailable;
int order;
public Thing(boolean newAvailable, int order) {
...
}
}
...and a Comparable...
class CompareThings implements Comparator<Thing> {
...
int compare(Thing t1, Thing t2) {
if (t1.newAvailable!=t2.newAvailable)
return t1.newAvailable==true ? 1 : -1;
return t1.order-t2.order;
}
}
(Note that t1.newAvailable==true is redundant, but I think it clarifies what's going on here.)
Now build an array of Thing and call Arrays.sort(Thing[] things, CompareThings);
I've done the breadth-first search in a normal way.
now I'm trying to do it in a multithreaded way.
i have one queue which is shared between the threads.
i use synchronize(LockObject) when i remove a node from the queue ( FIFI queue )
so what I'm trying to do is that.
when i thread finds a solution all the other threads will stop immediately.
i assume you are traversing a tree for your BFS.
create a thread pool.
for each unexplored children in the node, retrieve a thread from the thread pool (perhaps using a Semaphore). mark the child node as 'explored' and explore the node's children in a BFS manner. when you have found a solution or done exploring all the nodes, release the semaphore.
^ i've never done this before so i might have missed out something.
Assuming you want to do this iteratively (see note at the bottom why there may be better closed solutions), this is not a great problem for exercising multi threading. The problem is that multithreading is great if you don't depend on previous results, but here you want the minimum amount of coins.
As you point out, a breadth first solution guarantees that once you reach the desired amount, you won't have any further solutions with less coins in a single threaded environment. However, in a multithreaded environment, once you start calculating a solution, you cannot guarantee that it will finish before some other solution. Let's imagine for the value 21: it can be a 20c coin and a 1c or four 5c coins and a 1c; if both are calculating simultaneously, you cannot guarantee that the first (and correct) solution will finish first. In practice, it is unlikely the situation will happen, but when you work with multithreading you want the solution to work in theory, because multithreads always fail in the demonstration, no matter if they should not have failed until the death heat of the universe.
Now you have 2 possible solutions: one is to introduce choke points at the beginning of each level; you don't start that level until the previous level is finished. The other is once you reach a solution continue doing all the calculations with a lower level than the current result (which means you cannot purge the others). Probably with all the synchronization needed you get better performance by going single threaded, but let's go on.
For the first solution, the natural form is to iterate increasing the level. You can use the solution provided by happymeal, with a Semaphore. An alternative is to use the new classes provided by java.
CoinSet getCoinSet(int desiredAmount) throws InterruptedException {
// Use whatever number of threads you prefer or another member of Executors.
final ExecutorService executor = Executors.newFixedThreadPool(10);
ResultContainer container = new ResultContainer();
container.getNext().add(new Producer(desiredAmount, new CoinSet(), container));
while (container.getResult() == null) {
executor.invokeAll(container.setNext(new Vector<Producer>()));
}
return container.getResult();
}
public class Producer implements Callable<CoinSet> {
private final int desiredAmount;
private final CoinSet data;
private final ResultContainer container;
public Producer(int desiredAmount, CoinSet data, ResultContainer container) {
this.desiredAmount = desiredAmount;
this.data = data;
this.container = container;
}
public CoinSet call() {
if (data.getSum() == desiredAmount) {
container.setResult(data);
return data;
} else {
Collection<CoinSet> nextSets = data.addCoins();
for (CoinSet nextSet : nextSets) {
container.getNext().add(new Producer(desiredAmount, nextSet, container));
}
return null;
}
}
}
// Probably it is better to split this class, but you then need to pass too many parameters
// The only really needed part is to create a wrapper around getNext, since invokeAll is
// undefined if you modify the list of tasks.
public class ResultContainer {
// I use Vector because it is synchronized.
private Vector<Producer> next = new Vector<Producer>();
private CoinSet result = null;
// Note I return the existing value.
public Vector<Producer> setNext(Vector<Producer> newValue) {
Vector<Producer> current = next;
next = newValue;
return current;
}
public Vector<Producer> getNext() {
return next;
}
public synchronized void setResult(CoinSet newValue) {
result = newValue;
}
public synchronized CoinSet getResult() {
return result;
}
}
This still has the problem that existing tasks are executed; however, it is simple to fix that; pass the thread executor into each Producer (or the container). Then, when you find a result, call executor.shutdownNow. The threads that are executing won't be interrupted, but the operation in each thread is trivial so it will finish fast; the runnables that have not started won't start.
The second option means you have to let all the current tasks finish, unless you keep track of how many tasks you have run at each level. You no longer need to keep track of the levels, though, and you don't need the while cycle. Instead, you just call
executor.submit(new Producer(new CoinSet(), desiredAmount, container)).get();
And then, the call method is pretty similar (assume you have executor in the Producer):
public CoinSet call() {
if (container.getResult() != null && data.getCount() < container.getResult().getCount()) {
if (data.getSum() == desiredAmount)) {
container.setResult(data);
return data;
} else {
Collection<CoinSet> nextSets = data.addCoins();
for (CoinSet nextSet : nextSets) {
executor.submit(new Producer(desiredAmount, nextSet, container));
}
return null;
}
}
}
and you also have to modify container.setResult, since you cannot depend that between the if and setting the value it has not been set by some other threads (threads are really annoying, aren't they?)
public synchronized void setResult(CoinSet newValue) {
if (newValue.getCount() < result.getCount()) {
result = newValue;
}
}
In all previous answers, CoinSet.getSum() returns the sum of the coins in the set, CoinSet.getCount() returns the number of coins, and CoinSet.addCoins() returns a Collection of CoinSet in which each element is the current CoinSet plus one coin of each possible different value
Note: For the problem of the coins with the values 1, 5, 10 and 20, the simplest solution is take the amount and divide it by the largest coin. Then take the modulus of that and use the next largest value and so on. That is the minimum amount of coins you are going to need. This rule applies (AFAICT) when the following property if true: if for all consecutive pairs of coin values (i.e. in this case, 1-5, 5-10, 10-20) you can reach any int multiple of the lower element in the pair with with a smaller number of coins using the larger element and whatever coins are necessary. You only need to prove it to the min common multiple of both elements in the pair (after that it repeats itself)
I gather from your comment on happymeal's anwer that you are trying to find how to reach a specific amount of money by adding coins of 1c, 5c, 10c and 20c.
Since each coin denomination divides the denomination of the next bigger coin, this can be solved in constant time as follows:
int[] coinCount(int amount) {
int[] coinValue = {20, 10, 5, 1};
int[] coinCount = new int[coinValue.length];
for (int i = 0; i < coinValue.length; i++) {
coinCount[i] = amount / coinValue[i];
amount -= coinCount[i] * coinValue[i];
}
return coinCount;
}
Take home message: Try to optimize your algorithm before resorting to multithreading, because algorithmic improvements can yield much greater improvements.
I've successfully implemented it.
what i did is that i took all the nodes in the first level, let's say 4 nodes.
then i had 2 threads. each one takes 2 nodes and generate their children. whenever a node finds a solution it has to report the level that it found the solution in and limit the searching level so other threads don't exceed the level.
only the reporting method should be synchronized.
i did the code for the coins change problem. this is my code for others to use
Main Class (CoinsProblemBFS.java)
package coinsproblembfs;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Scanner;
/**
*
* #author Kassem M. Bagher
*/
public class CoinsProblemBFS
{
private static List<Item> MoneyList = new ArrayList<Item>();
private static Queue<Item> q = new LinkedList<Item>();
private static LinkedList<Item> tmpQ;
public static Object lockLevelLimitation = new Object();
public static int searchLevelLimit = 1000;
public static Item lastFoundNode = null;
private static int numberOfThreads = 2;
private static void InitializeQueu(Item Root)
{
for (int x = 0; x < MoneyList.size(); x++)
{
Item t = new Item();
t.value = MoneyList.get(x).value;
t.Totalvalue = MoneyList.get(x).Totalvalue;
t.Title = MoneyList.get(x).Title;
t.parent = Root;
t.level = 1;
q.add(t);
}
}
private static int[] calculateQueueLimit(int numberOfItems, int numberOfThreads)
{
int total = 0;
int[] queueLimit = new int[numberOfThreads];
for (int x = 0; x < numberOfItems; x++)
{
if (total < numberOfItems)
{
queueLimit[x % numberOfThreads] += 1;
total++;
}
else
{
break;
}
}
return queueLimit;
}
private static void initializeMoneyList(int numberOfItems, Item Root)
{
for (int x = 0; x < numberOfItems; x++)
{
Scanner input = new Scanner(System.in);
Item t = new Item();
System.out.print("Enter the Title and Value for item " + (x + 1) + ": ");
String tmp = input.nextLine();
t.Title = tmp.split(" ")[0];
t.value = Double.parseDouble(tmp.split(" ")[1]);
t.Totalvalue = t.value;
t.parent = Root;
MoneyList.add(t);
}
}
private static void printPath(Item item)
{
System.out.println("\nSolution Found in Thread:" + item.winnerThreadName + "\nExecution Time: " + item.searchTime + " ms, " + (item.searchTime / 1000) + " s");
while (item != null)
{
for (Item listItem : MoneyList)
{
if (listItem.Title.equals(item.Title))
{
listItem.counter++;
}
}
item = item.parent;
}
for (Item listItem : MoneyList)
{
System.out.println(listItem.Title + " x " + listItem.counter);
}
}
public static void main(String[] args) throws InterruptedException
{
Item Root = new Item();
Root.Title = "Root Node";
Scanner input = new Scanner(System.in);
System.out.print("Number of Items: ");
int numberOfItems = input.nextInt();
input.nextLine();
initializeMoneyList(numberOfItems, Root);
System.out.print("Enter the Amount of Money: ");
double searchValue = input.nextDouble();
int searchLimit = (int) Math.ceil((searchValue / MoneyList.get(MoneyList.size() - 1).value));
System.out.print("Number of Threads (Muste be less than the number of items): ");
numberOfThreads = input.nextInt();
if (numberOfThreads > numberOfItems)
{
System.exit(1);
}
InitializeQueu(Root);
int[] queueLimit = calculateQueueLimit(numberOfItems, numberOfThreads);
List<Thread> threadList = new ArrayList<Thread>();
for (int x = 0; x < numberOfThreads; x++)
{
tmpQ = new LinkedList<Item>();
for (int y = 0; y < queueLimit[x]; y++)
{
tmpQ.add(q.remove());
}
BFS tmpThreadObject = new BFS(MoneyList, searchValue, tmpQ);
Thread t = new Thread(tmpThreadObject);
t.setName((x + 1) + "");
threadList.add(t);
}
for (Thread t : threadList)
{
t.start();
}
boolean finish = false;
while (!finish)
{
Thread.sleep(250);
for (Thread t : threadList)
{
if (t.isAlive())
{
finish = false;
break;
}
else
{
finish = true;
}
}
}
printPath(lastFoundNode);
}
}
Item Class (Item.java)
package coinsproblembfs;
/**
*
* #author Kassem
*/
public class Item
{
String Title = "";
double value = 0;
int level = 0;
double Totalvalue = 0;
int counter = 0;
Item parent = null;
long searchTime = 0;
String winnerThreadName="";
}
Threads Class (BFS.java)
package coinsproblembfs;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
*
* #author Kassem M. Bagher
*/
public class BFS implements Runnable
{
private LinkedList<Item> q;
private List<Item> MoneyList;
private double searchValue = 0;
private long start = 0, end = 0;
public BFS(List<Item> monyList, double searchValue, LinkedList<Item> queue)
{
q = new LinkedList<Item>();
MoneyList = new ArrayList<Item>();
this.searchValue = searchValue;
for (int x = 0; x < queue.size(); x++)
{
q.addLast(queue.get(x));
}
for (int x = 0; x < monyList.size(); x++)
{
MoneyList.add(monyList.get(x));
}
}
private synchronized void printPath(Item item)
{
while (item != null)
{
for (Item listItem : MoneyList)
{
if (listItem.Title.equals(item.Title))
{
listItem.counter++;
}
}
item = item.parent;
}
for (Item listItem : MoneyList)
{
System.out.println(listItem.Title + " x " + listItem.counter);
}
}
private void addChildren(Item node, LinkedList<Item> q, boolean initialized)
{
for (int x = 0; x < MoneyList.size(); x++)
{
Item t = new Item();
t.value = MoneyList.get(x).value;
if (initialized)
{
t.Totalvalue = 0;
t.level = 0;
}
else
{
t.parent = node;
t.Totalvalue = MoneyList.get(x).Totalvalue;
if (t.parent == null)
{
t.level = 0;
}
else
{
t.level = t.parent.level + 1;
}
}
t.Title = MoneyList.get(x).Title;
q.addLast(t);
}
}
#Override
public void run()
{
start = System.currentTimeMillis();
try
{
while (!q.isEmpty())
{
Item node = null;
node = (Item) q.removeFirst();
node.Totalvalue = node.value + node.parent.Totalvalue;
if (node.level < CoinsProblemBFS.searchLevelLimit)
{
if (node.Totalvalue == searchValue)
{
synchronized (CoinsProblemBFS.lockLevelLimitation)
{
CoinsProblemBFS.searchLevelLimit = node.level;
CoinsProblemBFS.lastFoundNode = node;
end = System.currentTimeMillis();
CoinsProblemBFS.lastFoundNode.searchTime = (end - start);
CoinsProblemBFS.lastFoundNode.winnerThreadName=Thread.currentThread().getName();
}
}
else
{
if (node.level + 1 < CoinsProblemBFS.searchLevelLimit)
{
addChildren(node, q, false);
}
}
}
}
} catch (Exception e)
{
e.printStackTrace();
}
}
}
Sample Input:
Number of Items: 4
Enter the Title and Value for item 1: one 1
Enter the Title and Value for item 2: five 5
Enter the Title and Value for item 3: ten 10
Enter the Title and Value for item 4: twenty 20
Enter the Amount of Money: 150
Number of Threads (Muste be less than the number of items): 2