How to iterate through list of lists on demand? - java

As the title indicates, I am looking for a solution on how to implement the getNextCombination method shown in the code snippet below.
public class Test {
private final List<List<String>> iterators;
/**
* #return null if no combination left
*/
public String[] getNextCombination() {
return null;
}
}
Assuming the attribute iterator would look like
[0]={1,2}
[1]={4,5}
[2]={7,8}
then the sequential call to the getNextCombination method should return something similar to this:
147
148
157
158
247
248
...
NULL
How can this be achieved as efficient as possible?
Thanks for any help.

The following generic lazy function to calculate all combinations do the job and works for any number of lists or any other structure
static Stream<int []> combinationsStream(final int... numElems) {
final int numCombinations = Arrays.stream(numElems) // the number of solutions is
.map(n -> n + 1) // possibles sizes
.reduce(1, (a, b) -> a * b); // product
final int[] queue = Arrays.stream(numElems).map(i -> -1).toArray(); // for every item take n elements
final int[] i = new int[]{0}; // volatile (final) integer
final Function<int [], int []> next = o -> { // compute the next combination
while (true) {
if (i[0] == numElems.length) { // if is the last position
i[0] -= 1; // we will back
return queue; // but it's a combination
}
queue[i[0]] += 1; // take one more of i[0]-th element
if (queue[i[0]] > numElems[i[0]]) // if no more of this
queue[i[0]--] = -1; // reset and go back
else
i[0] += 1; // if not, go forward
}
};
return Stream.iterate(null, next::apply) // iterate getting next over and over
.skip(1) // avoid the seed (null)
.limit(numCombinations); // limit the stream to combs number
}
since only work with indexes, you can use as follows
Let your list:
// let you list of list (from somewhere)
List<List<String>> iterators = asList(
asList("1", "2"),
asList("4", "5"),
asList("7", "8")
);
(lists could contains different number of elements)
then, to get a lazy stream with all combinations simply convert the list of list to an array of max indexes
// we get combinations in a lazy way simply with
// (you could set this stream in your private class field if you wish)
Stream<int []> lazyCombinations = combinationsStream(iterators.stream()
.mapToInt(g -> g.size() - 1).toArray());
in your case the call will be combinationsStream(1, 1, 1) since valid indexes are 0, 1 for each list.
To convert some index combination to your String output get the indexes and join
// to convert the indexes array to values (and concatenate all) you can write the function
Function<int [], String> toString = indexes -> IntStream
// for each list
.range(0, indexes.length)
// get the indexes[i]-th element
.mapToObj(i -> iterators.get(i).get(indexes[i]))
// and join
.collect(joining());
then, simply map the previous Stream<int []> to Stream<String> using that function
// now you can convert your indexes stream to your final combinations stream (all lazy)
Stream<String> lazySolutions = lazyCombinations.map(toString);
you can use the Stream in many ways (i.e. return in a Web Service lazily) but, if you wish take one by one you can convert to an iterator.
The itarator is lazy too
// if you need peek combinations on demand (not in a streaming way) convert stream to iterator
Iterator<String> lazyIterator = lazySolutions.iterator();
Then, you can take only one
// you can take one by one (e.g. inside a `getNextCombination`)
System.out.println("Only one: " + lazyIterator.next());
or as many as you consider
// or to the end
lazyIterator.forEachRemaining(System.out::println);
with output
Only one: 147
148
157
158
247
248
257
258

You can do something like this :
private final List<List<String>> iterators = Arrays.asList(Arrays.asList("1", "2"), Arrays.asList("4", "5"), Arrays.asList("7", "8"));
public void run() {
one("", 0, 0, iterators.size(), iterators.get(0).size()); // begin the iterate over
}
public void iterateOver(String before, int x, int y, int maxX, int maxY) {
List<String> secondList = iterators.get(x); // get Y value list
secondList.forEach((ss) -> {
if(x + 1 < maxX) { // go to next line
iterateOver(before + ss, x + 1, 0, maxX, iterators.get(x + 1).size());
} else if(y + 1 < maxY) {
System.out.println(before + ss); // print result
iterateOver(before + ss, x, y + 1, maxX, maxY); // go to next Y value
} else {
// this is finished
}
});
}
Output:
147
148
157
158
247
248
257
258
Warns: Arrays#asList list are not editable.
Also, I didn't make the full getNextCombination method because now you can use this code as you want to implement your prefered way to be at a combination, and go to next.

Related

Find the positions that matches a condition on parent and child lists

Given a parent list with start and end times as numbers say (p1, p2):
1,5
2,2
4,10
Also another child list with their start and end times as (c1, c2):
2, 4
15,20
Find all the index positions from the parent and child list such that the below condition is satisfied:
p1 <= c1 <= c2 <= p2
For this example, the expected result is (0,0).
Explanation:
The valid combination is :
1 <= 2 <= 4 <= 5 that is position 0 from the parent list (1,5) matches with the condition for position 0 (2,4) of the child list.
So position 0 from the parent list and position 0 from the child list that is (0,0)
Constraints:
size of the parent and child list can be from 1 to 10^5
each element of this list can be from 1 to 10^9
Code that I tried:
static List<List<Integer>> process(List<List<Integer>> parent, List<List<Integer>> child) {
List<List<Integer>> answer = new ArrayList<>();
for(int i=0; i<parent.size(); i++) {
List<Integer> p = parent.get(i);
int p1 = p.get(0);
int p2 = p.get(1);
for(int j=0; j<child.size(); j++) {
List<Integer> c = child.get(j);
int c1 = c.get(0);
int c2 = c.get(1);
if((p1 <= c1) && (c1 <= c2) && (c2 <= p2)) {
answer.add(Arrays.asList(i, j));
}
}
}
return answer;
}
This code works for small inputs but fails for larger list sizes with time-out errors. What is the best approach to solve this problem?
Consider an alternative algorithm
The posted code is slow for large inputs,
because it checks all combinations of parents and children,
even for inputs where the number of answers will be a relatively small set.
I put an emphasis on the last point,
to highlight that when all children are within all parents,
then the answer must contain all pairings.
A more efficient solution is possible for inputs where the number of answers is significantly smaller than all possible pairings. (And without degrading the performance in case the answer is the complete set.)
Loop over the interesting positions from left to right. An interesting position is where a parent or child interval starts or ends.
If the position is a parent:
If this the start of the parent, add the parent to a linked hashset of started parents.
Otherwise it's the end of the parent. Remove this parent from the linked hashset.
If the position is the start of a child:
Loop over the linked hashset of started parents
If the parent was started before the child, add the index pair to the answers.
Break out of the loop, the remaining started parents were started after the child.
The key element that makes this fast is the following properties of a linked hashset:
Adding an item is O(1)
Removing an item is O(1)
The insertion order of items is preserved
The last point is especially important,
combined with the idea that we are looping over positions from left to right,
so we have the ordering that we need to eliminate parent-child pairs that won't be part of the answer.
The step of looping over interesting positions above is a bit tricky.
Here's one way to do it:
Define a new class to use for sorting, let's call it Tracker. It must have:
Position of an interesting index: the start or end of a parent or child
A flag to indicate if this position is a start or an end
A flag to indicate if this is a parent or a child
The original index in the parent or child list
Build a list of Tracker instances from the parent and child lists
For each parent, add two instances, one for the start and one for the end
For each child, add two instances, one for the start and one for the end
Sort the list, keeping in mind that the ordering is a bit tricky:
Must be ordered by position
When the position is the same, then:
The start of a parent must come before its own end
The start of a child must come before its own end
The start of a parent at some position X must come before the start of a child at the same position X
The end of a child at some position X must come before the end of a parent at the same position X
Evaluating the alternative algorithm
Given input with M parents and N children,
there are M * N possible combination of pairs.
To contrast the performance of the original and the suggested algorithms,
let's also consider a case where only a small subset of parents contain only a small subset of children,
that is, let's say that on average X parents contain Y children.
The original code will perform M * N comparisons, most of them will not be part of the answer.
The suggested alternative will perform an initial search step of 2 * (M + N) items, which is a log-linear operation: O(log (M + N)).
Then the main part of the algorithm performs linear logic,
generating the X * Y pairs with constant overhead: O(M + N).
The linked hashset makes this possible.
When X * Y is very close to M * N,
the overhead of the alternative algorithm may outweigh the benefits it brings.
However, the overhead grows log-linearly with M + N,
which is significantly smaller than M * N.
In other words, for large values of M and N and a uniformly random distribution of X and Y, the alternative algorithm will perform significantly better on average.
Ordering of the pairs in the answer
I want to point out that the question doesn't specify the ordering of pairs in the answers.
If a specific ordering is required,
it should be easy to modify the algorithm accordingly.
Alternative implementation
Here's an implementation of the ideas above,
and assuming that the pairs in the answer can be in any order.
List<List<Integer>> findPositions(List<List<Integer>> parent, List<List<Integer>> child) {
List<Tracker> items = new ArrayList<>();
// add the intervals with their original indexes from parent, and the parent flag set to true
for (int index = 0; index < parent.size(); index++) {
List<Integer> item = parent.get(index);
items.add(new Tracker(item.get(0), true, index, true));
items.add(new Tracker(item.get(1), false, index, true));
}
// add the intervals with their original indexes from child, and the parent flag set to false
for (int index = 0; index < child.size(); index++) {
List<Integer> item = child.get(index);
items.add(new Tracker(item.get(0), true, index, false));
items.add(new Tracker(item.get(1), false, index, false));
}
// sort the items by their position,
// parent start before child start,
// child end before parent end,
// start before end of child/parent
items.sort(Comparator.<Tracker>comparingInt(tracker -> tracker.position)
.thenComparing((a, b) -> {
if (a.isStart) {
if (b.isStart) return a.isParent ? -1 : 1;
return -1;
}
if (b.isStart) return 1;
return a.isParent ? 1 : -1;
}));
// prepare the list where we will store the answers
List<List<Integer>> answer = new ArrayList<>();
// track the parents that are started, in their insertion order
LinkedHashSet<Integer> startedParents = new LinkedHashSet<>();
// process the items one by one from left to right
for (Tracker item : items) {
if (item.isParent) {
if (item.isStart) startedParents.add(item.index);
else startedParents.remove(item.index);
} else {
if (!item.isStart) {
int childStart = child.get(item.index).get(0);
for (int parentIndex : startedParents) {
int parentStart = parent.get(parentIndex).get(0);
if (parentStart <= childStart) {
answer.add(Arrays.asList(parentIndex, item.index));
} else {
break;
}
}
}
}
}
return answer;
}
private static class Tracker {
final int position;
final boolean isStart;
final int index;
final boolean isParent;
Tracker(int position, boolean isStart, int index, boolean isParent) {
this.position = position;
this.isStart = isStart;
this.index = index;
this.isParent = isParent;
}
}
Lets consider each interval as an event. One idea would be to sort the parent and child list and then scan them from left to right. While doing this, we keep track of the "active events" from the parent list. For example, if the parent list has events e1 = (1, 5), e2 = (8, 11) and the child list has events e1' = (2, 6), e2' = (9, 10), a scan would look like this: start event e1 -> start event e1' -> end event e1 -> end event e1' -> start event e2 -> start event e2' -> end event e2' -> end event e2. While scanning, we keep track of the active events from the parent list by adding them to binary search tree, sorted by starting point. When we end an event ek' from the child list, we search for the starting point of ek' in the binary tree, and that way find all intervals, that have a smaller key. We can pair all of these up with the child Intervall and add it to the solution. The total time complexity is still O(n^2), since it is possible, that every child interval is in every parent interval. However, the complexity should be close to log(n)*n, if there is a very low amount of These pairs. I got part of the idea from the following link, so looking at this might help you to understand, what i am doing: Sub O(n^2) algorithm for counting nested intervals?
Firstly, here you can add a break in the if the condition:
if((p1 <= c1) && (c1 <= c2) && (c2 <= p2)) {
answer.add(i);
answer.add(j);
break;
}
Secondly, As this code has a time complexity of O(n^2) and tends to take time as your input increase to minimize it you can use some other data structures like trees where you get searching in O(log n) time.
RBinaryTree<Pair> tree = new RBinaryTree<>();
and in if condition...
tree.add(new Pair(i, j));
Create a Pare class like
private static class Pair {
int p;
int c;
Pair(int p, int c) {
this.p = p;
this.c = c;
}
}
Also, you can use some other approaches like divide and conquer by dividing to list into sublists.
It's my honor to share my thoughts. Maybe there are still some shortcomings that I haven't found, please correct them. This is for reference only.
First, process the parent list and child list, and add a third element to represent their input order. Then we need to write a Comparator
Comparator<List<Integer>> listComparator = (o1, o2) -> {
if (o1.get(0) < o2.get(0)) {
return -1;
} else if (o1.get(0) > o2.get(0)) {
return 1;
}
if (o1.get(1) < o2.get(1)) {
return -1;
} else if (o1.get(1) > o2.get(1)) {
return 1;
}
return 0;
}
and use list.stream ().sorted() to sort the elements in the list. At the same time, we can use list.stream().filter() to filter the illegal elements, so that we can get an ordered list; For the ordered list, we can search the parent list, find the elements that meet the size relationship in the child list, and record the index. In the subsequent element comparison of the parent list, we can directly start search from the record index.
Finally, the statistics results are sorted and output from small to large.
Here is the completion code:
static List<List<Integer>> process(List<List<Integer>> parent, List<List<Integer>> child) {
// The third element represents the original order number
int index = 0;
for (List<Integer> list : parent) {
list.add(index++);
}
index = 0;
for (List<Integer> list : child) {
list.add(index++);
}
Comparator<List<Integer>> listComparator = (o1, o2) -> {
if (o1.get(0) < o2.get(0)) {
return -1;
} else if (o1.get(0) > o2.get(0)) {
return 1;
}
if (o1.get(1) < o2.get(1)) {
return -1;
} else if (o1.get(1) > o2.get(1)) {
return 1;
}
return 0;
};
List<List<Integer>> parentSorted = parent.stream().filter(integers -> integers.get(0) <= integers.get(1)).sorted(listComparator).collect(Collectors.toList());
List<List<Integer>> childSorted = child.stream().filter(integers -> integers.get(0) <= integers.get(1)).sorted(listComparator).collect(Collectors.toList());
int childPointer = 0;
List<List<Integer>> answer = new ArrayList<>();
for (int i = 0; i < parentSorted.size(); i++) {
// Search the child list elements that meet the requirement that the parent list is greater than or equal to the ith element. The elements behind the parent list must be greater than or equal to the ith element. Therefore, for the following elements, you can directly search from the child list elements of the childPointer
if (parentSorted.get(i).get(0) <= childSorted.get(childPointer).get(0)) {
for (int j = childPointer; j < childSorted.size(); j++) {
if (parentSorted.get(i).get(0) <= childSorted.get(j).get(0)) {
if (childSorted.get(j).get(1) <= parentSorted.get(i).get(1)) {
answer.add(Arrays.asList(parentSorted.get(i).get(2), childSorted.get(j).get(2)));
} else {
break;
}
} else {
break;
}
}
} else {
// The child list pointer moves backward, and the parent list continues to judge the ith element
childPointer++;
i--;
}
}
return answer.stream().sorted(listComparator).collect(Collectors.toList());
}
Idea, it is similar to the balanced parenthesis ()()) is invalid and (())() is valid. Now we use (P1, -P1), (C1, -C1).... as as the symbols instead of (, ) where Pi is the start time for parent i and -Pi is the end time and similarly all the variables follow. We say Ci is balacned with Pi iff both Ci and -Ci are present between Pi and -Pi.
Some implementation detail, first sort all the numbers and make a stack and push the symbols from the start time (the first event), an example stack might lool like start: [P1, C3, P2, C2, C1, P3, -C2, -P1, -C3, -P3, -C1, -P2: top. Now maintain lists for all parents keeping track of the children between them and find the once that start and end in the scope of the parent i.e both Ci and -Ci are in list of Pi. Also the list closes when -Pi is read.
Hope this helps!
Usage of Streams API from Java 8 might be able to process more efficiently but not sure if it would help your context
static List<List<Integer>> process(List<List<Integer>> parent, List<List<Integer>> child) {
List<List<Integer>> answer = new ArrayList<>();
IntStream.range(0, parent.size()).forEach(parentIndex -> IntStream.range(0, child.size()).forEach(childIndex -> {
List<Integer> p = parent.get(parentIndex);
List<Integer> c = child.get(childIndex);
int p1 = p.get(0);
int p2 = p.get(1);
int c1 = c.get(0);
int c2 = c.get(1);
if((p1 <= c1) && (c1 <= c2) && (c2 <= p2)) {
answer.add(Arrays.asList(parentIndex, childIndex));
}
}));
return answer;
}
Following is another implementation using Streams API
static List<List<Integer>> process(List<List<Integer>> parent, List<List<Integer>> child) {
return
IntStream.range(0, parent.size()).mapToObj(parentIndex ->
IntStream.range(0, child.size()).filter(childIndex -> {
List<Integer> p = parent.get(parentIndex);
List<Integer> c = child.get(childIndex);
int p1 = p.get(0);
int p2 = p.get(1);
int c1 = c.get(0);
int c2 = c.get(1);
return ((p1 <= c1) && (c1 <= c2) && (c2 <= p2));
}).mapToObj(childIndex -> Arrays.asList(parentIndex, childIndex))
.flatMap(Collection::stream).collect(Collectors.toList())
).filter(value -> !value.isEmpty()).collect(Collectors.toList());
}

Sliding window of 10 seconds?

You are given an infinite stream of words (no spaces), each word also has an attached timestamp, starting at 0 and its in the format like 0, 1, 2, 3, 4, 5, ... , 6. We have APIs:
public class StreamClass {
public void consumeNextString(String next, int timeStamp);
public String getStrings(); // joins all strings into space seprated string using the below constraint
}
You are to implement both these functions. getStrings, specifically has the behavior that if you say had a stream like
one : 4
the: 5
hello : 12
the : 14
menlo: 15
If you got called getStrings now, it should print one hello the menlo instead of one the hello the menlo since the is duplicated at timestamp 11, 14 (current timestamp is 15). The oldest the at timestamp 5 got disregarded.
Later on, after the stream looks like:
one : 4
the: 5
hello : 12
the : 14
menlo: 15
big: 123
getStrings should print one the hello the menlo big because there are no duplicates in the last 10 second window (current timestamp is 123)
Work: I am thinking of an optimal way to do this, this is from an interview question.
The problem is, I dont see any good way of doing this other than just brute force, ie, storing every string then manually looking at the 10 second window to take out the oldest string, but surely there must be SOMETHING more optimal?
Well, here is a possible solution.
I used two Lists to hold the words and their timestamps.
The field lastTimeStamp is updated as each entry is consumed. It is used
to maintain the local window of seconds
when the last windows of time is entered, I simply iterate over the list of words removing the oldest duplicates.
after getString() is called, all lists are cleared to start the process anew.
This works for the supplied data and other data I have tested.
public class SlidingWindow10seconds {
public static void main(String[] args) {
StreamClass sc = new StreamClass();
sc.consumeNextString("one", 4);
sc.consumeNextString("the", 5);
sc.consumeNextString("hello", 12);
sc.consumeNextString("the", 14);
sc.consumeNextString("menlo", 15);
System.out.println(sc.getStrings());
sc.consumeNextString("one", 4);
sc.consumeNextString("the", 5);
sc.consumeNextString("hello", 12);
sc.consumeNextString("the", 14);
sc.consumeNextString("menlo", 15);
sc.consumeNextString("big", 123);
System.out.println(sc.getStrings());
}
Prints
one the hello menlo
one the hello the menlo big
class StreamClass {
int lastTimeStamp = 0;
final int windowSize = 10;
List<Integer> timeStamps = new ArrayList<>();
List<String> words = new ArrayList<>();
public void consumeNextString(String next, int timeStamp) {
words.add(next);
timeStamps.add(timeStamp);
lastTimeStamp = timeStamp;
}
public String getStrings() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < words.size(); i++) {
int ts = timeStamps.get(i);
// append all words if outside the window
if (ts < lastTimeStamp - windowSize) {
sb.append(words.get(i) + " ");
} else {
// Now iterate thru the list removing the oldest
// duplicates by adding in reverse order
Set<String> distinct = new LinkedHashSet<>();
for (int k = words.size()-1; k >= i; k--) {
distinct.add(words.get(k));
}
// and now reverse that using stack.
Stack<String> stk = new Stack<>();
stk.addAll(distinct);
while(!stk.isEmpty()) {
sb.append(stk.pop()+" ");
}
break;
}
}
sb.setLength(sb.length()-1);
words.clear();
timeStamps.clear();
return sb.toString();
}
}

Java - Return random index of specific character in string

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.

Find Survivor when n people are sitting in a circle

Hi I am across this problem and trying to solve this
Take a second to imagine that you are in a room with 100 chairs arranged in a circle. These chairs are numbered sequentially from One to One Hundred.
At some point in time, the person in chair #1 will be told to leave the room. The person in chair #2 will be skipped, and the person in chair #3 will be told to leave. Next to go is person in chair #6. In other words, 1 person will be skipped initially, and then 2, 3, 4.. and so on. This pattern of skipping will keep going around the circle until there is only one person remaining.. the survivor. Note that the chair is removed when the person leaves the room.Write a program to figure out which chair the survivor is sitting in.
I made good progress but stuck with a issue, after the count reaches 100 and not sure how to iterate from here, can any one help me, this is my code
import java.util.ArrayList;
public class FindSurvivor {
public static void main(String[] args) {
System.out.println(getSurvivorNumber(10));
}
private static int getSurvivorNumber(int numChairs) {
// Handle bad input
if (numChairs < 1) {
return -1;
}
// Populate chair array list
ArrayList<Integer> chairs = new ArrayList<Integer>();
for (int i = 0; i < numChairs; i++) {
chairs.add(i + 1);
}
int chairIndex = 0;
int lr =0;
while (chairs.size() > 1) {
chairs.remove(lr);
chairIndex+=1;
System.out.println(lr+" lr, size "+chairs.size()+" index "+chairIndex);
if(lr==chairs.size()||lr==chairs.size()-1)
lr=0;
lr = lr+chairIndex;
printChair(chairs);
System.out.println();
}
return chairs.get(0);
}
public static void printChair(ArrayList<Integer> chairs){
for(int i : chairs){
System.out.print(i);
}
}
}
The answer is 31. Here are three different implementations
var lastSurvivor = function(skip, count, chairs) {
//base case checks to see if there is a lone survivor
if (chairs.length === 1)
return chairs[0];
//remove chairs when they are left/become dead
chairs.splice(skip, 1);
//increment the skip count so we know which chair
//to leave next.
skip = (skip + 1 + count) % chairs.length;
count++;
//recursive call
return lastSurvivor(skip, count, chairs);
};
/** TESTS *******************************************************************
----------------------------------------------------------------------------*/
var result = lastSurvivor(0, 0, chairs);
console.log('The lone survivor is located in chair #', result);
// The lone survivor is located in chair # 31
/** ALTERNATE IMPLEMENTATIONS ***********************************************
-----------------------------------------------------------------------------
/* Implemenation 2
-----------------*/
var lastSurvivor2 = function(chairs, skip) {
skip++;
if (chairs === 1)
return 1;
else
return ((lastSurvivor2(chairs - 1, skip) + skip - 1) % chairs) + 1;
};
/** Tests 2 *******************************************************************/
var result = lastSurvivor2(100, 0);
console.log('The lone survivor is located in chair #', result);
// The lone survivor is located in chair # 31
/* Implemenation 3
------------------*/
var chairs2 = [];
for (var i = 1; i <= 100; i++)
chairs2.push(i);
var lastSurvivor3 = function(chairs, skip) {
var count = 0;
while (chairs.length > 1) {
chairs.splice(skip, 1);
skip = (skip + 1 + count) % chairs.length;
count++;
}
return chairs[0];
};
/** Tests 3 *******************************************************************/
var result = lastSurvivor3(chairs2, 0);
console.log('The lone survivor is located in chair #', result);
// The lone survivor is located in chair # 31
I'm not sure what your removal pattern is but I'd probably implement this as a circular linked list where the 100th seat holder will connect back to the 1st seat holder. If you use an array, you will have to worry about re-organizing the seats after every removal.
There is elegant analytical solution:
Let's change numbering of people: #2 -> #1, #3 -> #2, ..., #1 -> #100 (in the end we just need to substract 1 to "fix" the result). Now first person remains instead or leaving. Suppose that there is only 64 people in circle. It's easy to see that after first elimination pass 32 people in circle will remain and numbering will start again from #1. So in the end only #1 will remain.
We have 100 people. After 36 people will leave the circle we will end up with 64 people - and we know how to solve this. For each person that leaves the room one person remains, so circle with 64 people will start from 1 + 2*36 = #73 (new #1). Because of changing indexes on first step final answer will be #72.
In general case res = 2*(N - closest_smaller_pow_2) = 2*N - closest_larger_pow_2. The code is trivial:
public static long remaining(long total) {
long pow2 = 1;
while (pow2 < total) {
pow2 *= 2;
}
return 2*total - pow2;
}
Also this algorithm has O(log(N)) complexity instead of O(N), so it's possible to calculate function for huge inputs (it can be easily adapted to use BigInteger instead of long).
First, let's assume the chairs are numbered from 0. We'll switch the numbering back at the end -- but usually things are simpler when items are enumerated from 0 rather than 1.
Now, if you've got n people and you start eliminating at chair x (x is 0 or 1) then in a single pass through you're going to eliminate half the people. Then you've got a problem of roughly half the size (possibly plus one), and if you solve that, you can construct the solution to the original problem by multiplying that sub-result by 2 and maybe adding one.
To code this, it's simply a matter of getting the 4 cases (n odd or even vs x 0 or 1) right. Here's a version that gets the 4 cases right by using bitwise trickery.
public static long j2(long n, long x) {
if (n == 1) return 0;
return j2(n/2 + (n&x), (n&1)^x) + 1-x;
}
A solution with chairs numbered from 1 and without the extra argument can now be written:
public static long remaining(long n) {
return 1 + j2(n, 0);
}
This runs in O(log n) time and uses O(log n) memory.
If your step is incremental you can you use the following code:
int cur = 0;
int step = 1;
while (chairs.size() > 1) {
chairs.remove(cur);
cur += ++step;
cur %= chairs.size();
}
return chairs.get(0);
If your step is fixed to 1 then based on explanation provided by #Jarlax you can solve the problem with one-line of code in O(log n) time:
//for long values
public static long remaining(long numChairs) {
return (numChairs << 1) - (long)Math.pow(2,Long.SIZE - Long.numberOfLeadingZeros(numChairs));
}
//for BigInteger values
public static BigInteger remaining(BigInteger numChairs) {
return numChairs.shiftLeft(1).subtract(new BigInteger("2").pow(numChairs.bitLength()));
}
However, if you stick with ArrayLists no extra variables are required to your code. Always remove the first element and remove-then-add the next at the end of the list. This is however O(n).
while (chairs.size() > 1) {
chairs.remove(0);
chairs.add(chairs.remove(0));
}
return chairs.get(0);

Most efficient way of combining elements of lists of items into sets of combinations?

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.

Categories