Got this question during an interview. Wanted to know if there was a better solution:
Given N tasks, and the dependencies among them, please provide an execution sequence, which make sure jobs are executed without violating the dependency.
Sample File:
5
1<4
3<2
4<5
First line is the number of total tasks.
1<4 means Task 1 has to be executed before task 4.
One possible sequence would be:
1 4 5 3 2
My solution uses a DAG to store all the numbers, followed by a topological sort. Is there a less heavy-handed way of solving this problem?:
DirectedAcyclicGraph<Integer, DefaultEdge> dag = new DirectedAcyclicGraph<Integer, DefaultEdge>(DefaultEdge.class);
Integer [] hm = new Integer[6];
//Add integer objects to storage array for later edge creation and add vertices to DAG
for(int x = 1; x <= numVertices; x++){
Integer newInteger = new Integer(x);
hm[x] = newInteger;
dag.addVertex(newInteger);
}
for(int x = 1; x < lines.size()-1; x++){
//Add edges between vertices
String[] parts = lines.get(x).split("<");
String firstVertex = parts[0];
String secondVertex = parts[1];
dag.addDagEdge(hm[Integer.valueOf(firstVertex)], hm[Integer.valueOf(secondVertex)]);
}
//Topological sort
Iterator<Integer> itr = dag.iterator();
while(itr.hasNext()){
System.out.println(itr.next());
}
As already said by several users (Gassa, shekhar suman, mhum and Colonel Panic) the problem is solved by finding a topological sorting. As long as the iterator in dag returns the elements in that order it's correct.
I don't where the DirectedAcyclicGraph class is from, so I can't help with that. Otherwise, this method does the parsing as yours and uses a simple algorithm (actually, the first one springing to my mind)
public static int[] orderTasks (String[] lines){
// parse
int numTasks = Integer.parseInt(lines[0]);
List<int[]> restrictions = new ArrayList<int[]>(lines.length-1);
for (int i = 1; i < lines.length; i++){
String[] strings = lines[i].split("<");
restrictions.add(new int[]{Integer.parseInt(strings[0]), Integer.parseInt(strings[1])});
}
// ordered
int[] tasks = new int[numTasks];
int current = 0;
Set<Integer> left = new HashSet<Integer>(numTasks);
for (int i = 1; i <= numTasks; i++){
left.add(i);
}
while (current < tasks.length){
// these numbers can't be written yet
Set<Integer> currentIteration = new HashSet<Integer>(left);
for (int[] restriction : restrictions){
// the second element has at least the first one as precondition
currentIteration.remove(restriction[1]);
}
if (currentIteration.isEmpty()){
// control for circular dependencies
throw new IllegalArgumentException("There's circular dependencies");
}
for (Integer i : currentIteration){
tasks[current++]=i;
}
// update tasks left
left.removeAll(currentIteration);
// update restrictions
Iterator<int[]> iterator = restrictions.iterator();
while (iterator.hasNext()){
if (currentIteration.contains(iterator.next()[0])){
iterator.remove();
}
}
}
return tasks;
}
BTW, in your hm array initialization you define it has having 6 elements. It leaves the 0 position null (not a problem since you don't call it anyway) but in the general case the number of tasks could be greater than 5 and then you'll have and IndexOutOfBoundsException
Another punctilious remark, when adding the edges, in case of circular dependencies, if the message of the Exception raised by DAG is not clear enough, the user could be confused. Again, since I don't know where that class is from, I can't know.
Related
I have a small problem, I want to go through a list and compare two objects of the array. Each object has 3 elements, I use a StringTokenizer to be able to remove the separator, so each object has 3 elements. I would like to know how to make a method that gets the third element of each object and compare them. And if that element is less than another delete that element and the 2 before it.
I tried to make them with an iterator but I wouldn't know very well that it started from the 3 element and increased the position by 3.
Iterator<Integer> it = lisM.iterator();
int num;
while (it.hasNext()){
num = it.next();
System.out.println(num);
}
Is --> if, I was wrong to put it in the picture
This only answers part of your question. I could not understand the question completely, please edit it and I can edit my answer.
You should not remove items from a list whilst in a for loop, therefore you can, for example, create another boolean list with the same size divided by 3 and just fill it with true Booleans then set the position divided by 3 to false if you want to delete the three items. Then you can create a new list, iterate over the boolean list and add 3 "Objects" which are actually Strings (thanks #JB Nizet) at a time, every time the boolean list element is true. When it is false you just don't add the elements and by doing so you are practically deleting the two elements before that element together with that element.
You casted a String to an int, that does not work you have to parse the Strings.
I corrected some of your code and added the boolean list here:
ArrayList<String> lisM = new ArrayList<>(); // here I initialise the list as an array list with strings.
ArrayList<Boolean> booleanList = new ArrayList<>();
for (int i = 0; i < lisM.size() / 3; i++) {
booleanList.add(true);
}
for(int i = 3; i < lisM.size();i+=3) {
int m = Integer.parseInt(lisM.get(i)); // here I changed the casting to parsing and moved it out of the for loop, there is no need to initialize it again every single time since you do not change it in the second for loop.
for (int j = 6; j < lisM.size(); j += 6) {
int m1 = Integer.parseInt(lisM.get(j));// here I changed the casting to parsing again.
if (m > m1) { // this makes no sense here because you are going over all of the elements of the list and comparing them to all of them. But I kept it here for the sake of example.
booleanList.set(i/3,false);
}
// if you want to go over the whole list you will have to clear the list and start over again for every element.
}
}
and here is how you could create the new list without the elements you do not want:
ArrayList<String> newLisM = new ArrayList<>();
for (int i = 0; i <booleanList.size(); i++) {
if(booleanList.get(i))
for (int j = 0; j < 3; j++) {
newLisM.add(lisM.get(i+j));
}
}
This is a chunk of code in Java, I'm trying to output random numbers from the tasks array, and to make sure none of the outputs are repeated, I put them through some other loops (say you have the sixth, randomly-chosen task "task[5]"; it goes through the for loop that will check it against every "tCheck" element, and while task[5] equals one of the tCheck elements, it will keep trying to find another option before going back to the start of the checking forloop... The tCheck[i] elements are changed at the end of each overall loop of output to the new random number settled on for the task element).
THE PROBLEM is that, despite supposedly checking each new random task against all tCheck elements, sometimes (not always) there are repeated tasks output (meaning, instead of putting out say 2,3,6,1,8,7,5,4, it will output something like 2,3,2,1,8,7,5,4, where "2" is repeated... NOT always in the same place, meaning it can sometimes end up like this, too, where "4" is repeated: 3,1,4,5,4,6,7,8)
int num = console.nextInt();
String[] tasks = {"1","2","3","4","5","6","7","8"};
String[] tCheck = {"","","","","","","",""};
for(int i = 0; i<= (num-1); i++){
int tNum = rand.nextInt(8);
for(int j = 0; j <=7; j++){
if(tasks[tNum].equals(tCheck[j])){
while(tasks[tNum].equals(tCheck[j])){
tNum = rand.nextInt(8);
}
j = 0;
}
}
tCheck[i] = tasks[tNum];
System.out.println(tasks[tNum]+" & "+tCheck[i]);
}
None of the other chunks of code affect this part (other than setting up Random int's, Scanners, so on; those are all done correctly). I just want it to print out each number randomly and only once. to never have any repeats. How do I make it do that?
Thanks in advance.
Firstly, don't use arrays. Use collections - they are way more programmer friendly.
Secondly, use the JDK's API to implement this idea:
randomise the order of your elements
then iterate over them linearly
In code:
List<String> tasks = Arrays.asList("1","2","3","4","5","6","7","8");
Collections.shuffle(tasks);
tasks.forEach(System.out::println);
Job done.
you can check if a certain value is inside your array with this approach.
for(int i = 0; i<= (num-1); i++){
int tNum = rand.nextInt(8);
boolean exist = Arrays.asList(tasks).contains(tNum);
while(!exist){
//your code
int tNum = rand.nextInt(8);
exist = Arrays.asList(tasks).contains(tNum);
}
}
if you are using an arraylist then you can check it with contains method since you are using an array we have to get the list from the array using asList() and then use the contains method. with the help of the while loop it will keep generating random numbers untill it generates a non duplicate value.
I used to created something similar using an ArrayList
public class Main {
public static void main(String[] args) {
String[] array = { "a", "b", "c", "d", "e" };
List<String> l = new ArrayList<String>(Arrays.asList(array));
Random r = new Random();
while(!l.isEmpty()){
String s = l.remove(r.nextInt(l.size()));
System.out.println(s);
}
}
}
I remove a random position in the list until it's empty. I don't use any check of content. I believe that is kind of effective (Even if I create a list)
So given a string such as: 0100101, I want to return a random single index of one of the positions of a 1 (1, 5, 6).
So far I'm using:
protected int getRandomBirthIndex(String s) {
ArrayList<Integer> birthIndicies = new ArrayList<Integer>();
for (int i = 0; i < s.length(); i++) {
if ((s.charAt(i) == '1')) {
birthIndicies.add(i);
}
}
return birthIndicies.get(Randomizer.nextInt(birthIndicies.size()));
}
However, it's causing a bottle-neck on my code (45% of CPU time is in this method), as the strings are over 4000 characters long. Can anyone think of a more efficient way to do this?
If you're interested in a single index of one of the positions with 1, and assuming there is at least one 1 in your input, you can just do this:
String input = "0100101";
final int n=input.length();
Random generator = new Random();
char c=0;
int i=0;
do{
i = generator.nextInt(n);
c=input.charAt(i);
}while(c!='1');
System.out.println(i);
This solution is fast and does not consume much memory, for example when 1 and 0 are distributed uniformly. As highlighted by #paxdiablo it can perform poorly in some cases, for example when 1 are scarce.
You could use String.indexOf(int) to find each 1 (instead of iterating every character). I would also prefer to program to the List interface and to use the diamond operator <>. Something like,
private static Random rand = new Random();
protected int getRandomBirthIndex(String s) {
List<Integer> birthIndicies = new ArrayList<>();
int index = s.indexOf('1');
while (index > -1) {
birthIndicies.add(index);
index = s.indexOf('1', index + 1);
}
return birthIndicies.get(rand.nextInt(birthIndicies.size()));
}
Finally, if you need to do this many times, save the List as a field and re-use it (instead of calculating the indices every time). For example with memoization,
private static Random rand = new Random();
private static Map<String, List<Integer>> memo = new HashMap<>();
protected int getRandomBirthIndex(String s) {
List<Integer> birthIndicies;
if (!memo.containsKey(s)) {
birthIndicies = new ArrayList<>();
int index = s.indexOf('1');
while (index > -1) {
birthIndicies.add(index);
index = s.indexOf('1', index + 1);
}
memo.put(s, birthIndicies);
} else {
birthIndicies = memo.get(s);
}
return birthIndicies.get(rand.nextInt(birthIndicies.size()));
}
Well, one way would be to remove the creation of the list each time, by caching the list based on the string itself, assuming the strings are used more often than they're changed. If they're not, then caching methods won't help.
The caching method involves, rather than having just a string, have an object consisting of:
current string;
cached string; and
list based on the cached string.
You can provide a function to the clients to create such an object from a given string and it would set the string and the cached string to whatever was passed in, then calculate the list. Another function would be used to change the current string to something else.
The getRandomBirthIndex() function then receives this structure (rather than the string) and follows the rule set:
if the current and cached strings are different, set the cached string to be the same as the current string, then recalculate the list based on that.
in any case, return a random element from the list.
That way, if the list changes rarely, you avoid the expensive recalculation where it's not necessary.
In pseudo-code, something like this should suffice:
# Constructs fastie from string.
# Sets cached string to something other than
# that passed in (lazy list creation).
def fastie.constructor(string s):
me.current = s
me.cached = s + "!"
# Changes current string in fastie. No list update in
# case you change it again before needing an element.
def fastie.changeString(string s):
me.current = s
# Get a random index, will recalculate list first but
# only if necessary. Empty list returns index of -1.
def fastie.getRandomBirthIndex()
me.recalcListFromCached()
if me.list.size() == 0:
return -1
return me.list[random(me.list.size())]
# Recalculates the list from the current string.
# Done on an as-needed basis.
def fastie.recalcListFromCached():
if me.current != me.cached:
me.cached = me.current
me.list = empty
for idx = 0 to me.cached.length() - 1 inclusive:
if me.cached[idx] == '1':
me.list.append(idx)
You also have the option of speeding up the actual searching for the 1 character by, for example, useing indexOf() to locate them using the underlying Java libraries rather than checking each character individually in your own code (again, pseudo-code):
def fastie.recalcListFromCached():
if me.current != me.cached:
me.cached = me.current
me.list = empty
idx = me.cached.indexOf('1')
while idx != -1:
me.list.append(idx)
idx = me.cached.indexOf('1', idx + 1)
This method can be used even if you don't cache the values. It's likely to be faster using Java's probably-optimised string search code than doing it yourself.
However, you should keep in mind that your supposed problem of spending 45% of time in that code may not be an issue at all. It's not so much the proportion of time spent there as it is the absolute amount of time.
By that, I mean it probably makes no difference what percentage of the time being spent in that function if it finishes in 0.001 seconds (and you're not wanting to process thousands of strings per second). You should only really become concerned if the effects become noticeable to the user of your software somehow. Otherwise, optimisation is pretty much wasted effort.
You can even try this with best case complexity O(1) and in worst case it might go to O(n) or purely worst case can be infinity as it purely depends on Randomizer function that you are using.
private static Random rand = new Random();
protected int getRandomBirthIndex(String s) {
List<Integer> birthIndicies = new ArrayList<>();
int index = s.indexOf('1');
while (index > -1) {
birthIndicies.add(index);
index = s.indexOf('1', index + 1);
}
return birthIndicies.get(rand.nextInt(birthIndicies.size()));
}
If your Strings are very long and you're sure it contains a lot of 1s (or the String you're looking for), its probably faster to randomly "poke around" in the String until you find what you are looking for. So you save the time iterating the String:
String s = "0100101";
int index = ThreadLocalRandom.current().nextInt(s.length());
while(s.charAt(index) != '1') {
System.out.println("got not a 1, trying again");
index = ThreadLocalRandom.current().nextInt(s.length());
}
System.out.println("found: " + index + " - " + s.charAt(index));
I'm not sure about the statistics, but it rare cases might happen that this Solution take much longer that the iterating solution. On case is a long String with only a very few occurrences of the search string.
If the Source-String doesn't contain the search String at all, this code will run forever!
One possibility is to use a short-circuited Fisher-Yates style shuffle. Create an array of the indices and start shuffling it. As soon as the next shuffled element points to a one, return that index. If you find you've iterated through indices without finding a one, then this string contains only zeros so return -1.
If the length of the strings is always the same, the array indices can be static as shown below, and doesn't need reinitializing on new invocations. If not, you'll have to move the declaration of indices into the method and initialize it each time with the correct index set. The code below was written for strings of length 7, such as your example of 0100101.
// delete this and uncomment below if string lengths vary
private static int[] indices = { 0, 1, 2, 3, 4, 5, 6 };
protected int getRandomBirthIndex(String s) {
int tmp;
/*
* int[] indices = new int[s.length()];
* for (int i = 0; i < s.length(); ++i) indices[i] = i;
*/
for (int i = 0; i < s.length(); i++) {
int j = randomizer.nextInt(indices.length - i) + i;
if (j != i) { // swap to shuffle
tmp = indices[i];
indices[i] = indices[j];
indices[j] = tmp;
}
if ((s.charAt(indices[i]) == '1')) {
return indices[i];
}
}
return -1;
}
This approach terminates quickly if 1's are dense, guarantees termination after s.length() iterations even if there aren't any 1's, and the locations returned are uniform across the set of 1's.
I am trying to program an example of a RadixSort in Java, and while I understand how the algorithm works I am running into a few issues with my implementation of a Linked List of Queues.
I believe my issue is when when I update the linked list at the nth position with a new Queue as it's value. I believe I am using the same Queue for each nodes update and that is causing me to get the same values for every node in my linked list.
So when starting with an array of int[] theArray = {4,3,5,9,7,2,4,1,6,5};
I end up with a linked list of 10 nodes each consisting a queue of:{4,3,5,9,7,2,4,1,6,5}
I thought by using the new keyword it would create a fresh instance but it seems to be carrying over the old values each iteration.
Can someone either explain or point me in the right direction to understanding why this is happening?
EDIT: (Forgot to attach code)
package radixsort;
import java.util.*;
/**
* #author dlanz
*/
public class RadixSort {
public static void main(String[] args) {
int[] theArray = {4,3,5,9,7,2,4,1,6,5};
RadixSort theSort = new RadixSort();
System.out.println(Arrays.toString(theArray)); //Outputs the original array
theSort.sort(theArray);
System.out.println(Arrays.toString(theArray)); //Outputs the original array (no modifictions)
}
public void sort(int[] theArray) {
int significant;
int curVal;
int modulo = 10;
int ofInterest = 1;
LinkedList<Queue> lists = new LinkedList<>();
Queue<Integer> queue = new LinkedList<>();
int max = theArray[0];
for(int i = 0; i < theArray.length; i++) {
if ( theArray[i] > max) {
max = theArray[i];
}
}
significant = String.valueOf(max).length();
Queue<Integer> thisQueue;
for(int j = 1; j <= significant; j++){
lists.clear();
for(int i = 0; i < 10; i++){
lists.add(i, queue);
}
System.out.println(lists); //Outputs a list of 10 elements each with a value of null
for(int value : theArray){
curVal = value % modulo;
curVal = curVal / ofInterest;
System.out.println(curVal); //Correctly outputs the expected result
System.out.println(lists.get(curVal)); //With each iteration this outputs 10 elements each with a queue of all values.
thisQueue = new LinkedList<>();
thisQueue = lists.get(curVal);
thisQueue.add(value);
lists.set(curVal, thisQueue);// This seems to insert the generated queue into every linked lists node.
}
int k = 0;
for(int i = 0; i < 10; i++){
Queue<Integer> curQueue = lists.get(i);
if(!curQueue.isEmpty()){
theArray[k] = curQueue.remove();
k++;
}
}
ofInterest = ofInterest * 10;
modulo = modulo * 10;
}
}
}
EDIT 2:
I've been playing around with it, and it seems as if thisQueue, lists and queue are shared. When I perform certain actions on thisQueue such as thisQueue.add(1), the value of '1' is added across the board. If I do the same on lists with lists.add(1) every node in lists is populated with the value 1.
I remember reading something about object values being passed by reference (not the objects themselves though), can this have any thing to do with what I am experiencing?
Edit 3:
I've also noticed that if I use literals instead of variables in the .add() lines such as
thisQueue.add(value);
The values do not duplicate as mentioned in EDIT 2. I tried to cast the variables used as int even though they were declared as Int, but still got the same results.
Oddly enough I appreciate how no one responded to this question. I figured it out on my own while creating a set of sample code and formulating a less specific question. But I will not forget this, for a very long time.
What was happening was in the portion of code where I loop through and create nodes 0-9 in my Linked List
for(int i = 0; i < 10; i++){
lists.add(i, queue);
}
I was adding a reference to the very same Queue. So regardless of the use of the other Queues/.clear() I was essentially pulling the reference to that original Queue on this line
thisQueue = lists.get(curVal);
While I made a few changes along the way, all that really had to be done was change the loop to
for(int i = 0; i < 10; i++){
queue = new LinkedList<>();
lists.add(i, queue);
}
and change
Queue<Integer> queue = new LinkedList<>();
To just
Queue<Integer> queue
I had thought of explicitly creating 10 Separate Queues and then using a switch in the code to decide which queue should be used. This did not seem very flexible and would have been very tedious. At the same time I realized that creating a new Queue for each iteration is very costly. I would be creating as many objects as are in the array, in this case 10, but that could be 100, 1000, 1000000. By using (correct me if I'm wrong) anonymous objects I was able to create only as many objects as needed (1 for each element in the linked lists).
Let's say we're giving a List of lists of some items, say Strings.
list 1: "a", "b", "c"
list 2: "d", "e", "f"
list 3: "1", "2", "3"
results: (a, d, 1), (a, d, 2), ... (c, f, 3)
(the real use case has nothing to do with strings and such, this is just a mock up)
I wrote a recursive method to do it, but I am not happy with it because it creates a lot of temporary sets that get tossed (yeah, I know object creation is cheap in java, usually fewer cpu instructions than a malloc in C (source: Java Concurrency in Action, p241), eden GC is cheap, blah blah blah. humor me :).
void combine(List<List<String>> itemLists, List<Set<String>> combinations, Set<String> partial) {
if (itemLists == null || itemLists.isEmpty()) return;
List<String> items = itemLists.get(0);
for (String s : items) {
Set<String> tmpSet = new HashSet<>(partial);
tmpSet.add(s);
if (itemLists.size() == 0) //termination test
combinations.add(tmpSet);
else
combine(itemLists.subList(1, itemLists.size()), combinations, tmpSet);
}
}
So, how would you go about this?
edit: To be clear, I do not want to create permutations. I want to create sets that are sizeof(list of lists) big.
What you're looking for is the "cartesian product."
If you're okay with using Sets instead of Lists, you can use Sets.cartesianProduct. There is still some garbage allocated as you iterate over the resulting Lists... but not nearly as much as other approaches.
(Note that as a common library method, it's been very exhaustively tested, so you can have a little more confidence in it than in pasting dozens of lines of code out of SO.)
FYI there has been a request for Lists.cartesianProduct as well, but I don't think anyone's working on it.
You want a list of all possible sets, containing exactly one value from each of the provided lists, assuming that the number of lists is variable and the size of those lists is also variable. Correct?
Something like this, then?
static List<Set<String>> combine(List<List<String>> itemLists)
{
// Calculate how many combinations we'll need to build
int remainingCombinations = itemLists.get(0).size();
for(int i=1; i<itemLists.size(); i++)
{
remainingCombinations *= itemLists.get(i).size();
}
List<Set<String>> allSets = new ArrayList<Set<String>>();
// Generate this combination
for (;remainingCombinations > 0; remainingCombinations --)
{
Set<String> currentSet = new HashSet<String>();
int positionInRow = remainingCombinations;
// Pick the required element from each list, and add it to the set.
for(int i=0; i<itemLists.size(); i++)
{
int sizeOfRow = itemLists.get(i).size();
currentSet.add(itemLists.get(i).get(positionInRow % sizeOfRow));
positionInRow /= sizeOfRow;
}
allSets.add(currentSet);
}
return allSets;
}
This is more efficient: Approach it the same way counting works (each "position" is one of your lists and each "digit" that can go in that position is an element of your list):
List<Set<String>> combine( List<List<String>> input ){
final int n = input.size();
int[] index = new int[n];
List<Set<Sting>> result = new List<>();
int position = 0;
while( position < n ){ // "overflow" check
// Add set to result.
Set<String> set = new HashSet<>();
for( int i=0; i<n; i++ )
set.add( input.get(i).get( index[i] ) );
result.add( set );
// Now the "hard" part: increment the index array
position = 0;
while( position < n ){
if( index[ position ] < input.get( position ).size() ){
index[position]++;
break;
}
else // carry
index[ position++ ] = 0;
}
}
return result;
}
(Not tested, might have some bugs, but the main idea is there).
In general, recursion is slower than iteration.