Combination From Rows - java

I want to check the combination of values from data its exist or not. Below code works fine buts its looks inefficient. I am looking for some good solution of this problem.
public static void main(String args[]) {
Integer data[] = {
1, 2, 5, 1, 9, 3, 5, 3, 2
};
Integer combination[] = {
1, 3 ,2
};
System.out.println("Result: " + combinations(data, combination));
}
public static boolean combinations(Integer dataset[], Integer combination[]) {
boolean results[] = new boolean[combination.length];
Integer count = 0;
for (Integer comb : combination) {
for (Integer data : dataset) {
if (data.equals(comb)) {
results[count++] = true;
break;
}
}
}
for (Boolean result : results) {
if (!result) {
return false;
}
}
return true;
}
strong text

Looks like you just want to check if the combination is a subset of your data set...The order in which it appears in your data set is not important. Is that correct?
How big will be your data set? Is the data set created whenever it is required or is it maintained throughout?
If data set is large and is maintained throughout, it would be easier to search if you can maintain it sorted.
for (Integer comb : combination) {
if (Arrays.binarySearch(dataset, comb) < 0)
return false; //If any element is not found, return false
}
}
return true;
If you can keep the combination also sorted, you can further optimize it.
int loc = 0;
for (Integer comb : combination) {
loc = Arrays.binarySearch(dataset, loc, data.length, comb);
//Since both are sorted, u can be sure that next element will be
//after previous element match
if (loc < 0)
return false;
}
}
return true;
Even if you can't maintain a sorted array, one optimization u can do is to get rid of that boolean array and the for-loop at the bottom. Algorithm is as follows...
boolean isMatch = false;
for (Integer comb : combination) {
//We will be here in two condition -
// 1. first iteration or previous element was found in dataset.
// 2. Just set to false to check next element. Reset the flag
boolean isMatch = false;
for (Integer data : dataset) {
if (data.equals(comb)) {
//match found for the current combination element
isMatch = true;
break;
}
}
//This mean one of the combination element is not there. Break out.
if (!isMatch) break;
}
return isMatch;

Sort the dataset set which is complexity nlog(n). (Quick or merge sort maybe ? ) Then run binary search for every member of combination array over dataset. Binary search complexity log(n). When you do this for every member of combination array complexity will be nlog(n).
nlog(n) + nlog(n) = 2nlog(n) which is O(nlogn). This way your performance will be increase.
Code
public static boolean combinations(Integer dataset[], Integer combination[]) {
sort(dataset); // quick or insertion sort, nlogn
for (Integer comb : combination) { // n
if (!binarysearch(dataset, comb)) //logn
return false;
} //n*logn = nlogn
return true;} //2nlogn = O(nlogn)

You can change from array to ArrayList and use the containsAll method. not sure if it is efficient enough but will make your code shorter.
public static void main(String args[]) {
Integer data[] = {1, 2, 5, 1, 9, 3, 5, 3, 2};
Integer combination[] = {1,3 ,2};
System.out.println("Result: " + combinations(data, combination));
}
public static boolean combinations(Integer dataset[], Integer combination[]) {
List<Integer> data = Arrays.asList(dataset);
List<Integer> comb = Arrays.asList(combination);
return data.containsAll(comb);
}

Since your data consists of Integers which implements "Comparable", I would suggest you exploit that and construct a TreeSet and add all the data into it. You can then run a contains() to find if an element is present in the TreeSet or not.
TreeSet guarantees log(n) cost for each add, delete and contains.
Adding all elements(n) will cost you nlog(n).
Finding if 'm' elements are present will cost you mlog(n).

Related

Java - How to get list of elements best on certain condition

Solution{
String question
String answer
public Solution(String question,String answer){
...
}
}
List<Solution> solutions = new ArrayList<>();
Arrays.asList(
new Solution("Index1","a"),
new Solution("Index2","b"),
new Solution("Index3","c"),
new Solution("Index4","d"),
new Solution("Index5","ae"),
new Solution("Index1","afg"),
new Solution("Index2","adfg"),
new Solution("Index1","ag"),
new Solution("Index2","a"),
new Solution("Index3","a"),
new Solution("Index4","a"),
new Solution("Index5","a"),
new Solution("Index1","arrr"),
new Solution("Index2","a"),
new Solution("Index3","a"));
I always want to get last two sets starting from Index1 which are
new Solution("Index1","ag"),
new Solution("Index2","a"),
new Solution("Index3","a"),
new Solution("Index4","a"),
new Solution("Index5","a"),
new Solution("Index1","arrr"),
new Solution("Index2","a"),
new Solution("Index3","a"))
but I am not sure what is the best way to do that. I can think of reversing the list and then have a counter on Index1 which starts with 0
then do while loop to add it in a list until counter reaches 2. Not sure if this is possible with streams.
I think the simplest way is to get the positions at which Index1 occurs in the list of solutions. These represent potential start position of the sublist. You can do this by using an IntStream over the indexes into the solutions list. Then, take the second-to-last start point as the starting of a sublist that goes to the end of the list.
List<Integer> starts = IntStream.range(0, solutions.size())
.filter(i -> solutions.get(i).getQuestion().equals("Index1"))
.boxed()
.collect(toList());
if (starts.size() < 2) {
// not sure what you want to do in this case
} else {
List<Solution> lastTwoSets = solutions.subList(starts.get(starts.size()-2), solutions.size());
lastTwoSets.forEach(System.out::println);
}
It occurs to me that using an int[] instead of List<Integer> makes things slightly more efficient as well as a bit more concise. The techique is otherwise essentially the same.
int[] starts = IntStream.range(0, solutions.size())
.filter(i -> solutions.get(i).question.equals("Index1"))
.toArray();
if (starts.length < 2) {
// not sure what you want to do in this case
} else {
List<Solution> lastTwoSets = solutions.subList(starts[starts.length-2], solutions.size());
lastTwoSets.forEach(System.out::println);
}
This is quite a bit simpler with a normal for-loop than with streams. You can loop through the list and keep track of the indexes of the last two occurrences of Index1. When you know the second to last index, you can use the subList method to get your final list.
To make it faster, you can iterate backwards from the end and find the first two occurrences. You can then stop when you hit Index1 the second time.
This example iterates through from the beginning:
int firstIndex = -1;
int secondIndex = -1;
for (int i = 0; i < solutions.size(); i++) {
if (solutions.get(i).getQuestion().equals("Index1")) {
firstIndex = secondIndex;
secondIndex = i;
}
}
if (firstIndex == -1) {
// There weren't two occurrences of "Index1", so I return the whole list.
return solutions;
}
return solutions.subList(firstIndex, solutions.size());
Note that the subList method returns a view over your original list. This means that it won't iterate over your list a second time when you call it.
It also means that if you mutate your original list, the changes will be reflected in the sublist.
This solution is a bit tricky because the filtering criteria is multi-part, but this can be accomplished by counting the number of times that a question index has been seen, at most adding only 2 solutions per question, and stopping once Index1 has been seen twice.
The filtering object for this criteria would be:
public class SolutionFilter implements Predicate<Solution> {
private final Map<String, Integer> counter = new HashMap<>();
#Override
public boolean test(Solution solution) {
Integer index1Count = counter.get("Index1");
if (index1Count != null && index1Count == 2) {
return false;
}
Integer count = counter.get(solution.getQuestion());
if (count == null) {
counter.put(solution.getQuestion(), 1);
return true;
}
else if (count == 1) {
counter.put(solution.getQuestion(), 2);
return true;
}
else {
return false;
}
}
}
To ensure that the solutions are added in reverse order, a loop is performed, starting at the end and progressing towards the beginning of the solutions list. Likewise, to ensure that the output list is not in reverse order, a Deque is used and the matching Solutions are added to the head of the Deque:
public static void main(final String[] args) {
List<Solution> solutions = Arrays.asList(
new Solution("Index1","a"),
new Solution("Index2","b"),
new Solution("Index3","c"),
new Solution("Index4","d"),
new Solution("Index5","ae"),
new Solution("Index1","afg"),
new Solution("Index2","adfg"),
new Solution("Index1","ag"),
new Solution("Index2","a"),
new Solution("Index3","a"),
new Solution("Index4","a"),
new Solution("Index5","a"),
new Solution("Index1","arrr"),
new Solution("Index2","a"),
new Solution("Index3","a")
);
SolutionFilter filter = new SolutionFilter();
Deque<Solution> filteredSolutions = new LinkedList<>();
for (int i = solutions.size() - 1; i > 0; i--) {
Solution solution = solutions.get(i);
if (filter.test(solution)) {
filteredSolutions.addFirst(solution);
}
}
System.out.println(filteredSolutions);
}
This results in the following output:
[{Index1: ag}, {Index2: a}, {Index3: a}, {Index4: a}, {Index5: a}, {Index1: arrr}, {Index2: a}, {Index3: a}]
This can be accomplished using a Stream, but it may be more complicated.

Print Tree with 4 nodes (simple forest) for checking a benchmark

I implemented an experimental OOP language and now benchmark garbage collection using a Storage benchmark. Now I want to check/print the following benchmark for small depths (n=2, 3, 4,..).
The tree (forest with 4 subnode) is generated by the buildTreeDepth method. The code is as follows:
import java.util.Arrays;
public final class StorageSimple {
private int count;
private int seed = 74755;
public int randomNext() {
seed = ((seed * 1309) + 13849) & 65535;
return seed;
}
private Object buildTreeDepth(final int depth) {
count++;
if (depth == 1) {
return new Object[randomNext() % 10 + 1];
} else {
Object[] arr = new Object[4];
Arrays.setAll(arr, v -> buildTreeDepth(depth - 1));
return arr;
}
}
public Object benchmark() {
count = 0;
buildTreeDepth(7);
return count;
}
public boolean verifyResult(final Object result) {
return 5461 == (int) result;
}
public static void main(String[] args) {
StorageSimple store = new StorageSimple();
System.out.println("Result: " + store.verifyResult(store.benchmark()));
}
}
Is there a somewhat simple/straight forward way to print the tree generated by buildTreeDepth? Just the short trees of n=3, 4, 5.
As other has already suggested, you may choose some lib to do so. But if you just want a simple algo to test in command line, you may do the following, which I always use when printing tree in command line (write by handle, may have some bug. Believe you can get what this BFS algo works):
queue.add(root);
queue.add(empty);
int count = 1;
while (queue.size() != 1) {
Node poll = queue.poll();
if (poll == empty) {
count = 1;
queue.add(empty);
}
for (Node n : poll.getChildNodes()) {
n.setNodeName(poll.getNodeName(), count++);
queue.add(n);
}
System.out.println(poll.getNodeName());
}
Sample output:
1
1-1 1-2 1-3 1-4
1-1-1 1-1-2 1-1-3 1-2-1 1-2-2 1-3-1 1-3-2 1-4-1
...
And in your case you use array, which seems even easier to print.
Instead of using object arrays, use a List implementation like ArrayList. For an improved better result subclass ArrayList to also hold a 'level' value and add indentation to the toString() method.

String list get an item starting without loop

I have a array list contains thousands of data.
For Example:
List<String> custNames = new ArrayList<String>();
custNames.add("John");
custNames.add("Tom");
custNames.add("Bart");
custNames.add("Tim");
custNames.add("Broad");
Now I want to get count of names only starting with 'T'. I used looping mechanism for my solution.
List<String> filterNames = new ArrayList<String>();
String nameStarts="T";
for(int i=0;i<custNames.size();i++)
{
if(custNames.get(i).toLowerCase().startsWith(nameStarts.toLowerCase()))
{
filterNames.add(custNames.get(i));
}
}
System.out.println(filterNames.size());
But I have very large collection of data in this custNames list.
Is there any different solution without using loop?
Thanks.
There is very good solution from Java 8 for your problem.
Try this,
long filterNameCount = custNames
.stream()
.parallel()
.filter((s) -> s.startsWith(nameStarts.toLowerCase()))
.count();
System.out.println(filterNameCount);
If you are open to using a third-party library, there are a few interesting options you could use with Eclipse Collections.
If you use the ArrayList as you have it above, you can use the LazyIterate utility as follows:
int count = LazyIterate.collect(custNames, String::toLowerCase)
.countWith(String::startsWith, nameStarts.toLowerCase());
Assert.assertEquals(2, count);
If you use the Eclipse Collections replacement for ArrayList, you can use the rich functional protocols available directly on MutableList:
MutableList<String> custNames =
Lists.mutable.with("John", "Tom", "Bart", "Tim", "Broad");
String nameStarts= "T";
int count = custNames.asLazy()
.collect(String::toLowerCase)
.countWith(String::startsWith, nameStarts.toLowerCase());
System.out.println(count);
Assert.assertEquals(2, count);
The serial API in Eclipse Collections is eager-by-default, which is why I called asLazy() first. The collect method would otherwise create another MutableList.
If you benchmark your code with your full set of data, the following parallel version of the code may be more performant:
MutableList<String> custNames =
Lists.mutable.with("John", "Tom", "Bart", "Tim", "Broad");
String nameStarts= "T";
int processors = Runtime.getRuntime().availableProcessors();
int batchSize = Math.max(1, custNames.size() / processors);
ExecutorService executor = Executors.newFixedThreadPool(processors);
int count = custNames.asParallel(executor, batchSize)
.collect(String::toLowerCase)
.countWith(String::startsWith, nameStarts.toLowerCase());
executor.shutdown();
Assert.assertEquals(2, count);
The asParallel() API in Eclipse Collections is lazy-by-default. The API forces you to pass in a an ExecutorService and an int batchSize. This gives you complete control over the parallelism.
You can also use the Stream API with all MutableCollections in Eclipse Collections because they extend java.util.Collection.
Note: I am a committer for Eclipse Collections.
You could also use a tree storage : it would very efficient for this kind of search. If you are stucked with a list the previous answered is a way to do.
remove all the items which dont start with "T" like this:
custNames.removeIf(p->!p.startsWith("T"));
you can make a copy out of your list and remove items not starting with "T".
First, you can shorten your initialization with Arrays.asList(T); Second, I would use a simple loop to build a table of counts once and then use that to determine the subsequent queries. Something like,
List<String> custNames = new ArrayList<String>(Arrays.asList("John", "Tom",
"Bart", "Tim", "Broad"));
int[] counts = new int[26];
for (String name : custNames) {
char ch = Character.toLowerCase(name.charAt(0));
counts[ch - 'a']++;
}
for (int i = 0; i < counts.length; i++) {
if (counts[i] > 0) {
System.out.printf("There are %d words that start with %c%n",
counts[i], (char) ('a' + i));
}
}
Which outputs
There are 2 words that start with b
There are 1 words that start with j
There are 2 words that start with t
Or, in the specific case - counts['t' - 'a'] is the count of words starting with t.
If you have more or less static list and perform search operation often you can sort your list or use TreeMap.
Also you don't need to create new list and get its size then. You can simply create a counter variable and increment it.
You can create your own sorting and finding implementation.
Consider the following:
public class ContainingArrayList<E> extends ArrayList<E> {
private Comparator<E> comparator;
public ContainingArrayList(Comparator<E> comparator) {
this.setComparator(comparator);
}
#Override
public boolean add(E e) {
// If the collection is empty or the new element is bigger than the last one, append it to the end of the collection
if(size() == 0 || comparator.compare(e, get(size()-1)) >= 0)
return super.add(e);
else {
for (int i = 0; i < size(); i++) {
int result = comparator.compare(e, get(i));
// If the new element is bigger than the current element, continue with the next element
if (result > 0) continue;
// If the new element is equal to the current element, no need to insert (you might insert of course)
if (result == 0) return false;
// Otherwise the new element is smaller than the current element, so insert it between the previous and the current element
super.add(i, e);
return true;
}
return super.add(e);
}
}
public E get(E containingElement) {
int start = 0;
int end = size()-1;
// If the element is the first one, return the first element
if(comparator.compare(containingElement, super.get(start)) == 0)
return super.get(start);
// If the element is the last one, return the last element
if(comparator.compare(containingElement, super.get(end)) == 0)
return super.get(end);
// Otherwise do a binary search
while(start != end) {
// Get the element between start and end positions
E mid = super.get(start + (end/2));
// Compare the two elements
int result = comparator.compare(containingElement, mid);
// If the middle element compared to the containing element is equal, return the middle element
if(result == 0) {
return mid;
}
// If the containing element is smaller than the middle, halve the end position
else if(result < 0) {
end = start + (end/2);
}
// If the containing element is bigger than the middle, set the start position to the middle position
else if(result > 0) {
start = start + (end/2);
}
}
return null;
}
public Comparator<E> getComparator() {
return comparator;
}
public void setComparator(Comparator<E> comparator) {
this.comparator = comparator;
}
}
The custom comparator is used to sort the elements and to find the element that starts with a specific character. This means that you can change the comparator implementation for your needs at any time or you can create a more dynamic finding solution.
Test:
public class SortFindTest {
public SortFindTest() {
ContainingArrayList<String> t = new ContainingArrayList<String>(new MyComparator());
t.add("John");
t.add("Tom");
t.add("Bart");
t.add("Tim");
t.add("Broad");
System.out.println(t.get("T"));
}
class MyComparator implements Comparator<String> {
#Override
public int compare(String o1, String o2) {
int o1c = o1.charAt(0);
int o2c = o2.charAt(0);
if(o1c == o2c)
return 0;
if(o1c > o2c)
return 1;
return -1;
}
}
public static void main(String[] args) {
new SortFindTest();
}
}
I'm not sure if this would be faster than Java 8 Stream API but it worth a try.
If the order in which the items are stored does not matter, you could store the names in a HashMap, where the first character of each name is the key, and an ArrayList of names with that first character are the values. And then all you need to do, assuming the HashMap is named customerList, is customerList.get("T").size().
Initializing HashList and Adding Customers
HashMap<Character, ArrayList<String>> customerList = new HashMap<Character, ArrayList<String>>();
int NUM_ALPHABETS = 26;
int ascii_char = 97;
for(int i = 0; i < NUM_ALPHABETS; i++){
char c = (char) ascii_char;
customerList.add(c, new ArrayList<String>());
ascii_char++;
}
customerList.get("t").add("Tony");
customerList.get("a").add("Alice");
customerList.get("b").add("Ben");
Getting Number of Customers Starting with "t"
int num_t = customerList.get("t").size();

Backtracking Recursion through a List of Strings

I'm trying to write a method that returns a sub lists and each containin the max value of strigns from a larger list.
I'm using iterator and backtrack recursion to achieve this, however - I'm not familiar with backtrack recursion. Any suggestions on how to make this work. Or if my code needs adjustments?
This is what I have so far:
private void printAnagrams(List<String> anagrams, int max, List<List<String>> listofLists) {
Iterator<String> iterate = anagrams.iterator();
String word = "";
while (iterate.hasNext()) {
for(int i = 0; i < anagrams.size(); i++) {
word = iterate.next();
listofLists.add(new ArrayList<>());
listofLists.get(i).add(word);
if (listofLists.size() == max) {
listofLists.add(new ArrayList<>());
// Continue new list
}
}
}
}
This is the method that calls the private method:
public void printAnagrams(String phrase, int max, List<List<String>> anagrams) {
if (anagrams == null || max < 0) {
throw new IllegalArgumentException();
} else if (max == 0) {
getWords(phrase);
} else
printAnagrams(phrase, max, anagrams);
}// End of printAnagram method
Sample output:
List:
[core, course, cs, cure, for, force, forces, four, of, off, offer, offers, or, our, ours, re, score, so, source, suffer, sure, us, use, user]
SubLists with max = 3:
[core, off, us]
[core, us, off]
[course, off]
[cure, off, so]
[cure, so, off]
[force, of, us]
[force, us, of]

Most optimal way to combine unique groups of size k from n that meet specific requirements

I have been working on something the past few days that seems to be working as intended, however I am looking for ways to improve it. I have a set of n items, and I need to put together groups of these items that MUST meet ALL of the following requirements:
2 items from Category A
2 items from Category B
2 items from Category C
2 Items from Category D
1 item from Category E
I am currently using the following recursive method to put my groups together and the isValid() method is being used to determine if the group meets the criteria.
void getGroups(String[] arr, int len, int startPosition, String[] result) {
if(len == 0) {
Group group = new Group(Arrays.asList(result));
if(group.isValid()) {
validGroups.add(group);
group.printGroup();
}
return;
}
for(int i = startPosition; i <= arr.length - len; i++) {
result[result.length - len] = arr[i];
getGroups(arr, len - 1, i + 1, result);
}
}
I am able to see valid results get printed as the program runs, however the original size of items that I am working with can be well over 100 items. This means there is a very large number of total possible groups that will be iterated through and a lot of times the program never actually completes.
I know that there are currently a bunch of wasted iterations, for example if at some point I detect a group is invalid because it has 3 items from Category A, I should be able to move on. I am not sure if my current method with a few tweaks is the best way to go about this, or if I should separate the items into their respective groups first, and then from their put only valid combinations together. Any help would be appreciated. Thanks.
EDIT: I tried to make the method a bit more simpler than my actual method. My actual method takes in an array of Objects that I've created that contain their value along with their category. I guess for the example we can assume that each category is represented by a list of Strings that it contains. The method can be called like:
String[] items = {"test1", "test2", "test3", "test4", "test5", "test6", "test7",
"test8", "test9", "test10", "test11", "test12", "test13",
"test14", "test15", "test16", "test17", "test18"};
getGroups(items, 9, 0, new String[9]);
EDIT2:
List<String> catA = new ArrayList<String>();
catA.add("test1");
catA.add("test2");
catA.add("test3");
catA.add("test4");
List<String> catB = new ArrayList<String>();
catB.add("test5");
catB.add("test6");
catB.add("test7");
catB.add("test8");
List<String> catC = new ArrayList<String>();
catC.add("test9");
catC.add("test10");
catC.add("test11");
catC.add("test12");
List<String> catS = new ArrayList<String>();
catD.add("test13");
catD.add("test14");
catD.add("test15");
catD.add("test16");
List<String> catE = new ArrayList<String>();
catE.add("test17");
catE.add("test18");
Output:
{"test1", "test2", "test5", "test6", "test9", "test10", "test13", "test14", "test17"}
{"test1", "test2", "test5", "test6", "test9", "test10", "test13", "test14", "test18"}
{"test1", "test2", "test5", "test6", "test9", "test10", "test13", "test16", "test17"}
{"test1", "test2", "test5", "test6", "test9", "test10", "test13", "test15", "test17"}
{"test1", "test2", "test5", "test6", "test9", "test10", "test14", "test15", "test17"}
etc...
This seems to work.
I use a BitPattern iterator I wrote a while ago that walks all n-bit numbers containing just k set bits and uses that to select from your categories.
Note that much of this code is building the test data to reflect your requirements.
I hold a List of Iterables which are the BitPatterns. A list of Iterators which are the currently in-use Iterators from the BitPatterns (they must be renewed every time they complete) and a List of BigIntgers that are the current values to explode into selections from the data.
public class Test {
enum Category {
A(2), B(2), C(2), D(2), E(1);
public final int required;
Category(int required) {
this.required = required;
}
}
private static final Category[] categories = Category.values();
static class Categorised {
final String name;
final Category category;
Categorised(String name, Category category) {
this.name = name;
this.category = category;
}
#Override
public String toString() {
return category.name() + ":" + name;
}
}
static final List<Categorised> data = new ArrayList<>();
static {
data.add(new Categorised("A-1", Category.A));
data.add(new Categorised("A-2", Category.A));
data.add(new Categorised("A-3", Category.A));
data.add(new Categorised("B-1", Category.B));
data.add(new Categorised("B-2", Category.B));
data.add(new Categorised("B-3", Category.B));
data.add(new Categorised("C-1", Category.C));
data.add(new Categorised("C-2", Category.C));
data.add(new Categorised("C-3", Category.C));
data.add(new Categorised("D-1", Category.D));
data.add(new Categorised("D-2", Category.D));
data.add(new Categorised("D-3", Category.D));
data.add(new Categorised("E-1", Category.E));
data.add(new Categorised("E-2", Category.E));
data.add(new Categorised("E-3", Category.E));
}
// Categorise the data.
private Map<Category, List<Categorised>> categorise(List<Categorised> data) {
Map<Category, List<Categorised>> categorised = new EnumMap<>(Category.class);
for (Categorised d : data) {
List<Categorised> existing = categorised.get(d.category);
if (existing == null) {
existing = new ArrayList<>();
categorised.put(d.category, existing);
}
existing.add(d);
}
return categorised;
}
public void test() {
// Categorise the data.
Map<Category, List<Categorised>> categorised = categorise(data);
// Build my lists.
// A source of Iteratprs.
List<BitPattern> is = new ArrayList<>(categories.length);
// The Iterators.
List<Iterator<BigInteger>> its = new ArrayList<>(categories.length);
// The current it patterns to use to select.
List<BigInteger> next = new ArrayList<>(categories.length);
for (Category c : categories) {
int k = c.required;
List<Categorised> from = categorised.get(c);
// ToDo - Make sure there are enough.
int n = from.size();
// Make my iterable.
BitPattern p = new BitPattern(k, n);
is.add(p);
// Gather an Iterator.
Iterator<BigInteger> it = p.iterator();
// Store it.
its.add(it);
// Prime it.
next.add(it.next());
}
// Walk the lists.
boolean stepped;
do {
// Interpret the current numbers.
List<Categorised> candidates = new ArrayList<>();
for ( int c = 0; c < categories.length; c++ ) {
BigInteger b = next.get(c);
List<Categorised> category = categorised.get(categories[c]);
// Step through the bits in the number.
BitSet bs = BitSet.valueOf(b.toByteArray());
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
// Pull those entries from the categorised list.
candidates.add(category.get(i));
}
}
// Print it for now.
System.out.println(candidates);
// Step again.
stepped = step(is, its, next);
} while (stepped);
}
// Take one step.
private boolean step(List<BitPattern> is, List<Iterator<BigInteger>> its, List<BigInteger> next) {
boolean stepped = false;
// Step each one until we make one successful step.
for (int i = 0; i < is.size() && !stepped; i++) {
Iterator<BigInteger> it = its.get(i);
if (it.hasNext()) {
// Done here!
stepped = true;
} else {
// Exhausted - Reset it.
its.set(i, it = is.get(i).iterator());
}
// Pull that one.
next.set(i, it.next());
}
return stepped;
}
public static void main(String args[]) {
new Test().test();
}
}
This is the BitPattern iterator.
/**
* Iterates all bit patterns containing the specified number of bits.
*
* See "Compute the lexicographically next bit permutation"
* http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
*
* #author OldCurmudgeon
*/
public class BitPattern implements Iterable<BigInteger> {
// Useful stuff.
private static final BigInteger ONE = BigInteger.ONE;
private static final BigInteger TWO = ONE.add(ONE);
// How many bits to work with.
private final int bits;
// Value to stop at. 2^max_bits.
private final BigInteger stop;
// Should we invert the output.
private final boolean not;
// All patterns of that many bits up to the specified number of bits - invberting if required.
public BitPattern(int bits, int max, boolean not) {
this.bits = bits;
this.stop = TWO.pow(max);
this.not = not;
}
// All patterns of that many bits up to the specified number of bits.
public BitPattern(int bits, int max) {
this(bits, max, false);
}
#Override
public Iterator<BigInteger> iterator() {
return new BitPatternIterator();
}
/*
* From the link:
*
* Suppose we have a pattern of N bits set to 1 in an integer and
* we want the next permutation of N 1 bits in a lexicographical sense.
*
* For example, if N is 3 and the bit pattern is 00010011, the next patterns would be
* 00010101, 00010110, 00011001,
* 00011010, 00011100, 00100011,
* and so forth.
*
* The following is a fast way to compute the next permutation.
*/
private class BitPatternIterator implements Iterator<BigInteger> {
// Next to deliver - initially 2^n - 1
BigInteger next = TWO.pow(bits).subtract(ONE);
// The last one we delivered.
BigInteger last;
#Override
public boolean hasNext() {
if (next == null) {
// Next one!
// t gets v's least significant 0 bits set to 1
// unsigned int t = v | (v - 1);
BigInteger t = last.or(last.subtract(BigInteger.ONE));
// Silly optimisation.
BigInteger notT = t.not();
// Next set to 1 the most significant bit to change,
// set to 0 the least significant ones, and add the necessary 1 bits.
// w = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));
// The __builtin_ctz(v) GNU C compiler intrinsic for x86 CPUs returns the number of trailing zeros.
next = t.add(ONE).or(notT.and(notT.negate()).subtract(ONE).shiftRight(last.getLowestSetBit() + 1));
if (next.compareTo(stop) >= 0) {
// Dont go there.
next = null;
}
}
return next != null;
}
#Override
public BigInteger next() {
last = hasNext() ? next : null;
next = null;
return not ? last.not(): last;
}
#Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
#Override
public String toString () {
return next != null ? next.toString(2) : last != null ? last.toString(2): "";
}
}
}
I will not write code but will list a possible approach. I say possible because it will be running and storing all data in memory and is not the best in regard to algorithms. yet, it is an approach where you don't need to eliminate invalid options. I will use an example in order to make things more clear.
suppose you have categories A,B,C. Where K=2 for A,B and K=1 for C.
you also have the input items A1,B1,B2,A2,C1,A3
1- go over the items and divide them according to their category. so you prepare an array/list for each category that has all the items that belong to it.
so now you have arrays:
Category A = [A1,A2,A3] , Category B = [B1,B2] and Category C=[C1]
2- now after preparing the lists, prepare the various legal groups that you can have for picking K items from N items found in that list . here is a link that might help in doing that efficiently: How to iteratively generate k elements subsets from a set of size n in java?
now you have:
first group belonging to category A:
[A1,A2] , [A1,A3], [A2,A3] (3 elements)
second group belonging to category B:
[B1,B2] (1 element)
third group belonging to category C:
[C1] (1 element)
3- now, if you treat each such group as an item, the question transforms to how many different ways are there for picking exactly one element from each group. and that is supposed to be easier to program recursively and will not require eliminating options. and if the number of categories is constant, it will be nested loops over the sets of groups in second point above.
EDIT
the approach works well in eliminating the need to validate bad combinations.
yet, there will still be a problem in regard of time. Here is the code that I made to demonstrate. it makes a list of 100 items. then it does the steps mentioned.
Note that I commented out the code that prints the groups.
The calculation is very fast up to that point. I have added code that prints how many legal choices can be made from each group.
package tester;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
/**
*
* #author
*/
public class Tester {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
//generate 100 random items belonging to categories
Random rand=new Random();
List<Item> items=new ArrayList<>();
int a=1,b=1,c=1,d=1,e=1;
for (int i=0;i<100;i++){
int randomNumber=rand.nextInt(5)+1;
CATEGORY_TYPE categoryType=null;
int num=0;
switch (randomNumber) {
case 1:
categoryType=CATEGORY_TYPE.A;
num=a++;
break;
case 2:
categoryType=CATEGORY_TYPE.B;
num=b++;
break;
case 3:
categoryType=CATEGORY_TYPE.C;
num=c++;
break;
case 4:
categoryType=CATEGORY_TYPE.D;
num=d++;
break;
case 5:
categoryType=CATEGORY_TYPE.E;
num=e++;
break;
}
String dummyData="Item "+categoryType.toString()+num;
Item item=new Item(dummyData,categoryType);
items.add(item);
}
//arrange the items in lists by category
List<Item> categoryAItemsList=new ArrayList<>();
List<Item> categoryBItemsList=new ArrayList<>();
List<Item> categoryCItemsList=new ArrayList<>();
List<Item> categoryDItemsList=new ArrayList<>();
List<Item> categoryEItemsList=new ArrayList<>();
for (Item item:items){
if (item.getCategoryType()==CATEGORY_TYPE.A)
categoryAItemsList.add(item);
else if (item.getCategoryType()==CATEGORY_TYPE.B)
categoryBItemsList.add(item);
else if (item.getCategoryType()==CATEGORY_TYPE.C)
categoryCItemsList.add(item);
else if (item.getCategoryType()==CATEGORY_TYPE.D)
categoryDItemsList.add(item);
else if (item.getCategoryType()==CATEGORY_TYPE.E)
categoryEItemsList.add(item);
}
//now we want to construct lists of possible groups of choosing from each category
List<Item[]> subsetStoringListA=new ArrayList<>();
List<Item[]> subsetStoringListB=new ArrayList<>();
List<Item[]> subsetStoringListC=new ArrayList<>();
List<Item[]> subsetStoringListD=new ArrayList<>();
List<Item[]> subsetStoringListE=new ArrayList<>();
processSubsets(categoryAItemsList.toArray(new Item[0]),2,subsetStoringListA);
processSubsets(categoryBItemsList.toArray(new Item[0]),2,subsetStoringListB);
processSubsets(categoryCItemsList.toArray(new Item[0]),2,subsetStoringListC);
processSubsets(categoryDItemsList.toArray(new Item[0]),2,subsetStoringListD);
processSubsets(categoryEItemsList.toArray(new Item[0]),1,subsetStoringListE);
System.out.println(" A groups number: "+subsetStoringListA.size());
System.out.println(" B groups number: "+subsetStoringListB.size());
System.out.println(" C groups number: "+subsetStoringListC.size());
System.out.println(" D groups number: "+subsetStoringListD.size());
System.out.println(" E groups number: "+subsetStoringListE.size());
//now we just print all possible combinations of picking a single group from each list.
//the group is an array with valid choices
// for (Item[] subsetA:subsetStoringListA){
// for (Item[] subsetB:subsetStoringListB){
// for (Item[] subsetC:subsetStoringListC){
// for (Item[] subsetD:subsetStoringListD){
// for (Item[] subsetE:subsetStoringListE){
// print(subsetA);
// print(subsetB);
// print(subsetC);
// print(subsetD);
// print(subsetE);
// System.out.println("\n");
// }
//
// }
// }
// }
// }
}
static void print(Item[] arr){
for (Item item:arr)
System.out.print(item.getDumyData()+" ");
}
static void processSubsets(Item[] set, int k,List<Item[]> subsetStoringList) {
Item[] subset = new Item[k];
processLargerSubsets(set, subset, 0, 0,subsetStoringList);
}
static void processLargerSubsets(Item[] set, Item[] subset, int subsetSize, int nextIndex,List<Item[]> subsetStoringList) {
if (subsetSize == subset.length) { //here we have a subset we need to store a copy from it
subsetStoringList.add(Arrays.copyOf(subset, subset.length));
} else {
for (int j = nextIndex; j < set.length; j++) {
subset[subsetSize] = set[j];
processLargerSubsets(set, subset, subsetSize + 1, j + 1,subsetStoringList);
}
}
}
public static enum CATEGORY_TYPE {A,B,C,D,E}
private static class Item{
private CATEGORY_TYPE categoryType;
private String dumyData;
public Item(String dumyData,CATEGORY_TYPE categoryType) {
this.dumyData = dumyData; //maybe bad name but i mean the object can have many other fields etc
this.categoryType = categoryType;
}
/**
* #return the categoryType
*/
public CATEGORY_TYPE getCategoryType() {
return categoryType;
}
/**
* #return the dumyData
*/
public String getDumyData() {
return dumyData;
}
}
}
in a specific run, it gave the following:
A groups number: 210
B groups number: 153
C groups number: 210
D groups number: 210
E groups number: 19
that means , if we had to print all possible choices of a single element (and here an elemnt is an array containing k choices from a category) from each of these, you will have : 210*153*210*210*19 = 26,921,727,000
now listing/printing over 26 billion variations will take time no matter what and I don't see how it will be minimized.
try setting the total items to 20 and uncomment the printing code to see that everything is working correctly. And see if you really need to list the possible combinations. please remember that every combination here is legal and there are no wasted iterations in all the parts of the code.
one final note: I did not treat edge cases like when there are no items in a category to complete K. that you can easily put in the code according to the desired behaviour in that case.
So this seems to be a constraint satisfaction problem. So maybe try backtracking?
I believe the following works, but plug in your own data to guaranteee.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Launch {
public static void main(String[] args) {
// Formulate the constraints.
int[] constraints = { 2, 1, 0, 1 };
// Create all the items.
List<boolean[]> items = new ArrayList<boolean[]>();
boolean[] i1 = { true, false, true, false };
boolean[] i2 = { true, false, false, false };
boolean[] i3 = { false, true, false, true };
boolean[] i4 = { false, false, false, true };
items.add(i1);
items.add(i2);
items.add(i3);
items.add(i4);
// Solve!
backtrack(constraints, items);
}
/**
* Recursively generate possible solutions but backtrack as soon as the constraints fail.
*/
private static void backtrack(int[] constraints, List<boolean[]> items) {
// We start with no items belonging to any categories.
List<List<boolean[]>> memberships = new ArrayList<List<boolean[]>>();
for (int i = 0; i < constraints.length; i++) {
memberships.add(new ArrayList<boolean[]>());
}
backtrack(constraints, items, memberships);
}
/**
* Recursively generate possible solutions but backtrack as soon as the constraints fail.
*/
private static void backtrack(int[] constraints, List<boolean[]> items,
List<List<boolean[]>> memberships) {
if (items.isEmpty() && !acceptable(constraints, memberships)) {
return;
} else if (acceptable(constraints, memberships)) {
display(memberships);
} else {
for (boolean[] item : items) {
int catIdx = 0;
for (boolean belongs : item) {
if (belongs) {
// The item and category were chosen so let's update
// memberships.
List<List<boolean[]>> newMemberships = new ArrayList<List<boolean[]>>();
for (List<boolean[]> old : memberships) {
newMemberships.add(new ArrayList<boolean[]>(old));
}
newMemberships.get(catIdx).add(item);
// We've placed the item so let's remove it from the
// possibilities.
List<boolean[]> newItems = new ArrayList<boolean[]>(
items);
newItems.remove(item);
// Now solve the sub-problem.
backtrack(constraints, newItems, newMemberships);
}
catIdx++;
}
}
}
}
/**
* A nice way to display the membership tables.
*/
private static void display(List<List<boolean[]>> memberships) {
System.out.println("---");
for (List<boolean[]> category : memberships) {
for (boolean[] item : category) {
System.out.print(Arrays.toString(item) + " ");
}
System.out.println();
}
}
/**
* Returns whether or not a list of memberships are accepted by the
* constraints.
*
* #param constraints
* - The number of items required per category.
* #param memberships
* - The current items per category.
*/
private static boolean acceptable(int[] constraints,
List<List<boolean[]>> memberships) {
boolean acceptable = memberships.size() == constraints.length;
for (int i = 0; i < memberships.size(); i++) {
acceptable = acceptable
&& constraints[i] == memberships.get(i).size();
}
return acceptable;
}
}

Categories

Resources