Note able to get Distinct int[] from List<int[]> - java-8

I am trying to get distinct List with unique int[], but I am still getting duplicates.
List<Integer> set1 = Arrays.asList(2, 3, 4, 5, 10, 4, 5, 6, 4);
List<Integer> count = new ArrayList<>();
List<int[]> combination = set1.stream().flatMap(i -> set1.stream().
flatMap(j -> set1.stream().map(k -> new int[] { i, j, k })))
.distinct().collect(Collectors.toList());
My logic to get required elements from the combined list of int[] is the numbers in array should be consecutive with difference of 1.
Ex: {2,3,4}
But in my final list the {2,3,4} is coming 3 times , it is obvious. I want to winnow the occurrences of 3 times {2,3,4} to 1. SO i wrote the below logic. Still there are many elements which are in consecutive form {4,5,6}, i want them also to be only once counted.
List<int[]> combination1 = combination.stream().distinct().collect(Collectors.toList());
combination1.stream().filter(i -> Collections.frequency(combination, i) == 1).forEach(s -> {
if (s[1] - s[0] == 1 && s[2] - s[1] == 1 && s[2] - s[0] == 2) {
count.add(1);
} else {
count.add(0);
}
});
System.out.println(count.stream().mapToInt(Integer::intValue).sum());
I am getting the unique elements as 15, but it should be 3.
My final List<int[]> should contain only : {2,3,4} , {3,4,5} , {4,5,6}
and hence count should be three.

Here's the first line of distinct()'s documentation:
Returns a stream consisting of the distinct elements (according to Object.equals(Object)) of this stream.
Now, because you're running it on a Stream<int[]>, int[].equals would have to return true when two int arrays have the same content. But it doesn't (see this). You can solve it by using a list before distinct(), and mapping afterwards (using Integer[] here, but a different mapping is needed for int[]):
List<Integer[]> combination = set1.stream()
.flatMap(i -> set1.stream()
.flatMap(j -> set1.stream().map(k -> Arrays.asList(i, j, k))))
.distinct()
.map(l -> l.toArray(new Integer[0]))
.collect(Collectors.toList());

As stated in this answer, int[] arrays do not have the require equality for distinct(). However, IntBuffer can wrap int[] arrays and provide the value based equality. So you can get distinct arrays using the sequence map(IntBuffer::wrap).distinct().map(IntBuffer::array), to wrap the arrays, eliminate duplicates and unwrap the arrays.
However, that problem was part of an entirely unnecessary step, as you weren’t focusing on the actual problem you want to solve.
To get all tuples of consecutive value, you can simply use
List<Integer> set1 = Arrays.asList(2, 3, 4, 5, 10, 4, 5, 6, 4);
int[] ascending = set1.stream().mapToInt(Integer::intValue).sorted().distinct().toArray();
List<int[]> combination = IntStream.rangeClosed(0, ascending.length - 3)
.mapToObj(ix -> Arrays.copyOfRange(ascending, ix, ix + 3))
.filter(a -> a[0] + 1 == a[1] && a[1] + 1 == a[2])
.collect(Collectors.toList());
combination.forEach(a -> System.out.println(Arrays.toString(a)));
Instead of creating 729 combinations of all values, to eliminate redundant combinations, followed by eliminating nonmatching combinations, it generates only the four possible ascending combinations in the first place. Then, it filters these, to accept sequences of consecutive numbers only.
So the last statement will print
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]

Although #ernest_k mention the basic problem for your solution but I think you can do it in simple way.
first create map from set1 elements.
Map<Integer,Integer> map = set1.stream()
.collect(Collectors
.toMap(Function.identity(),Function.identity(),(v1,v2)->v1));
{2=2, 3=3, 4=4, 5=5, 6=6, 10=10}
then by looping over map values you can create consecutive array.
List<Integer[]> combination = map.values()
.stream()
.filter(item -> map.containsKey(item + 1) && map.containsKey(item + 2))
.map(item -> new Integer[] {item, item + 1, item + 2})
.collect(Collectors.toList());
As #Holger commented using of set will do simpler.
Set<Integer> set = new HashSet<>(set1);
List<Integer[]> combination = set.stream()
.filter(item -> set.contains(item + 1) && set.contains(item + 2))
.map(item -> new Integer[] {item, item + 1, item + 2})
.collect(Collectors.toList());

Related

Failing to print a list in a sorted fashion without actually sorting the list

I am struggling to sort a list in a sorted fashion without actually "sorting" the list.
arr = [3, 2, 1, 4, 5]
count = 0
current = arr[0]
prev = -1
while count < len(arr):
for item in arr:
if current < item > prev:
current = item
prev = current
count = count + 1
print(current)
Output:
5
5
5
5
5
I don't want to sort the list. I am wondering is there a way to not sort the list and not change the original list and print the items in a sorted fashion?
It's pretty unclear what you're trying to do. If you want a sorted copy, you could make a list containing the indices of the the original objects ([0, 1, 2, ..., n]) and then sort these by comparing the original values at those indices, then map this sorted list back to the values from the first list.
But much simpler still is just to sort a shallow clone of the list.
If you read Javascript, here's a demonstration of that idea, using a simple range helper function to create the list of indices:
const arr = [8, 6, 7, 5, 3, 0, 9]
const range = (lo, hi) =>
[...Array (hi - lo)] .map((_, i) => lo + i)
const indexSort = (ns) =>
range (0, ns .length)
.sort ((i, j) => ns [i] - ns [j])
.map (x => ns [x])
console .log ('indexSort:', indexSort (arr))
console .log ('shallow clone:', [...arr] .sort ((a, b) => a - b))
console .log ('no mutation of original array:', arr)
.as-console-wrapper {max-height: 100% !important; top: 0}

Fast way of getting r-long combinations of set A that have at least one element from set B, which is a subset of A

For example, if A={0,1,2,3,4}, r=3 and B={1,4}, the result would be:
[0, 1, 2]
[0, 1, 3]
[0, 1, 4]
[0, 2, 4]
[0, 3, 4]
[1, 2, 3]
[1, 2, 4]
[1, 3, 4]
[2, 3, 4]
That's all the r-long combinations of A, excluding [0, 2, 3], because that one doesn't contain either 1 or 4.
The solution that I currently have is the following, using the fastest algorithm for getting normal combinations I know of, and just doing a simple check to see if combinations generated also contain an element of B (java):
int[] A = new int[]{0,1,2,3,4};
int[] B = new int[]{1,4};
int n = A.length;
int r = 3;
int[] picks = new int[r]; //Holds indexes of elements in A
for (int i = 0; i < picks.length; i++)
picks[i] = i;
int lastindex = picks.length - 1;
outer:
while (true) {
int at = lastindex;
while (true) {
picks[at] += 1;
if (picks[at] < n) {
int displacement = picks[at] - at; // at + displacement = picks[at], at + displacement + 1 = picks[at] + 1 ,...
// Make all picks elements after at y = picks[at] + x, so picks={0, 2, 4, 6, 18, 30} & at=3 --> picks={0, 2, 4, 5, 6, 7}
// (Note that this example will never take place in reality, because the 18 or the 30 would be increased instead, depending on what n is)
// Do the last one first, because that one is going to be the biggest,
picks[lastindex] = lastindex + displacement;
if (picks[lastindex] < n) { // and check if it doesn't overflow
for (int i = at + 1; i < lastindex; i++)
picks[i] = i + displacement;
int[] combination = new int[r];
for (int i = 0; i < r; i++)
combination[i] = A[picks[i]];
System.out.print(Arrays.toString(combination));
//^With this, all r-long combinations of A get printed
//Straightforward, bruteforce-ish way of checking if int[] combination
//contains any element from B
presence:
for (int p : combination) {
for (int b : B) {
if (p==b) {
System.out.print(" <-- Also contains an element from B");
break presence;
}
}
}
System.out.println();
break;
}
}
at--;
if (at < 0) {
//Moving this check to the start of the while loop will make this natively support pick 0 cases (5C0 for example),
//but reduce performance by I believe quite a bit. Probably better to special-case those (I haven't
// done that in this test tho)
break outer;
}
}
}
output:
[0, 1, 3] <-- Also contains an element from B
[0, 1, 4] <-- Also contains an element from B
[0, 2, 3]
[0, 2, 4] <-- Also contains an element from B
[0, 3, 4] <-- Also contains an element from B
[1, 2, 3] <-- Also contains an element from B
[1, 2, 4] <-- Also contains an element from B
[1, 3, 4] <-- Also contains an element from B
[2, 3, 4] <-- Also contains an element from B
As written in the comments, I believe this method to be very rudimentary. Can anyone think of a faster way to do this?
Assuming you have a int[][] FindCombinations(int[] set, int length) function that returns a list of all the length-long combinations of set, do the following (pseudo-code):
for i=1 to B.length
{
int bi = B[i];
A = A - bi; // remove bi from A
foreach C in FindCombinations(A, r-1)
{
output C+bi // output the union of C and {bi}
}
}
This way all combinations contain at least one element from B (and may also contain elements of B that have not yet been used) without much extra work. All other combinations are eliminated at no cost (the don't have to be found at all) and also the test that a combination contains an element from B for each combination is also eliminated.
Whether this algorithm is faster, greatly depends on how efficently you can add/remove elements from a set and the percentage of included vs excluded combinations (i.e. if you only end up excluding 1% of the total combinations it is probably not worth it)
Note that when getting the combinations to union with {b[i]} these may also contain an element B[j] where j>i. When you get to the point that you get the combinations to union with B[j] none of them will contain B[i], so all combinations are unique.

how to keep the unfiltered data in the collection in Java 8 Streaming API?

My Input Sequence is : [1,2,3,4,5]
Result should be : [1,12,3,14,5]
That is even numbers are incremented by 10, but odd values are left intact.
Here is what I tried:
public static List<Integer> incrementEvenNumbers(List<Integer> arrays){
List<Integer> temp =
arrays.stream()
.filter(x->x%2==0)
.map(i -> i+10)
.collect(Collectors.toList());
return temp;
}
when I call this method,
System.out.println(incrementEvenNumbers(Arrays.asList(1,2,3,4,5)));
I get [12, 14]. I am wondering how to include the values not filtered to seep in but the map should not be applied for it.
You can use a ternary operator with map, so that the function you apply is either the identity for odd values, or the one that increments the value by 10 for even values:
List<Integer> temp = arrays.stream()
.map(i -> i % 2 == 0 ? i+10 : i)
.collect(Collectors.toList());
The problem, as you saw, is that filter will remove the elements so when a terminal operation will be called, they will be filtered by the predicate.
Note that if you don't care modifying the list in place, you can use replaceAll directly, as you are doing a mapping from a type T to T.
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.replaceAll(i -> i % 2 == 0 ? i+10 : i); //[1, 12, 3, 14, 5]

Getting ALL permutations of ALL sublists of a list of integers

I've been having trouble with this problem. Basically, I have a list of integers, such as
list = [1, 2, 3]
I want to get all possible permutations of every subset. I know similar questions exist online, but I couldn't find one that does every permutation as well as every subset. In other words, I want:
function(list) =
[], [1], [2], [3],
[1, 2], [2, 1], [1, 3], [3,1], [2, 3], [3,2],
[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]
I understand the output will get extremely large even for a small input list size. Unfortunately, I just cannot figure out how to do such a problem.
Thank you!
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
public class Test {
private static boolean[] used;
private static int[] a;
private static void f(int curCount,int subsetSize,ArrayDeque<Integer> perm){
// System.out.println("in rec "+curCount+" "+subsetSize);
if(curCount < subsetSize){
for(int i=0;i<a.length;i++) {
if (!used[i]) { // try to add i-th elem of array as a next element of permutation if it's not busy
perm.add(a[i]);
used[i] = true; //mark i-th element as used for future recursion calls
f(curCount + 1, subsetSize,perm); // curCount+1 because we added elem to perm. subsetSize is const and it's needed just for recursion exit condition
used[i] = false; // "free" i-th element
perm.removeLast();
}
}
}
else{ //some permutation of array subset with size=subsetSize generated
for(Integer xx:perm) System.out.print(xx+" ");
System.out.println();
}
}
public static void main(String[] args){
a = new int[]{1,2,3};
used = new boolean[a.length];
Arrays.fill(used, false);
// second param is a subset size (all sizes from 1 to n)
// first param is number of "collected" numbers, when collected numbers==required subset size (firstparam==second param) exit from recursion (from some particular call-chain)
// third param is data structure for constructing permutation
for(int i=1;i<=a.length;i++)f(0,i,new ArrayDeque<Integer>());
} //end of main
} //end of class
output
1 2 3 1 2 1 3 2 1 2 3 3 1
3 2 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2
3 2 1
So what you are looking for is all the possible permutations of the Power Set.
This seems to go into some depth about strategies for doing this.
If your list in N elements long, you want to get all the combinations of N taken by M, where M is between 1 and N. For each of the combination you want to get all the permutations. You can probably figure out algorithms for combinations and permutations via google.
I ended up using a combination of these two functions. Not sure if it works as intended, but so far it has been working properly.
// Generates all permutations of a set. Thus, given an input like [1, 2, 3] it changes the null
// final_list input to be [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
static void heappermute(List<List<Integer>> waypoints, int n, List<List<List<Integer>>> final_list) {
int i;
if (n == 1) {
final_list.add(waypoints);
}
else {
for (i = 0; i < n; i++) {
heappermute(waypoints, n-1, final_list);
if (n % 2 == 1) {
swap(waypoints.get(0), waypoints.get(n-1));
}
else {
swap(waypoints.get(i), waypoints.get(n-1));
}
}
}
}
static void swap (List<Integer> x, List<Integer> y)
{
List<Integer> temp = new ArrayList<>();
temp = x;
x = y;
y = temp;
}
// Generates all subsets of a given set. Thus, given a list of waypoints, it will return a list of
// waypoint lists, each of which is a subset of the original list of waypoints.
// Ex: Input originalSet = {1, 2, 3}
// Output: = {}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}
// Code modified from http://stackoverflow.com/questions/4640034/calculating-all-of-the-subsets-of-a-set-of-numbers
public static List<List<List<Integer>>> powerSet(List<List<Integer>> originalSet) {
List<List<List<Integer>>> sets = new ArrayList<>();
if (originalSet.isEmpty()) {
sets.add(new ArrayList<List<Integer>>());
return sets;
}
List<List<Integer>> list = new ArrayList<List<Integer>>(originalSet);
List<Integer> head = list.get(0);
List<List<Integer>> rest = new ArrayList<List<Integer>>(list.subList(1, list.size()));
for (List<List<Integer>> set : powerSet(rest)) {
List<List<Integer>> newSet = new ArrayList<List<Integer>>();
newSet.add(head);
newSet.addAll(set);
sets.add(newSet);
sets.add(set);
}
return sets;
}

Find objects with the most correspondences to a reference object

Reference object: { 1, 5, 6, 9, 10, 11 }
Other objects:
A { 2, 4, 5, 6, 8, 10, 11 }
B { 5, 7, 9, 10 }
C { 2, 5, 6, 7, 9, 12 }
D { 1, 3, 4, 5, 6, 8, 9, 10 }
E { 6, 8 }
F { 1, 2, 3, 4, 7, 8, 9, 13, 15 }
... { ... }
Difficulty: It should be faster than O(n*m)
Result should be:
Array
(
[D] => 5
[A] => 4
[C] => 3
[B] => 3
[F] => 2
[E] => 1
)
Slow solution:
ref = array(1, 5, 6, 9, 10, 11);
foreach (A, B, C, D,.. AS row)
{
foreach (row AS col)
{
if ( exist(col, ref) )
{
result[row] += 1;
}
}
}
sort (result)
.. this is a solution, but its far to slow.
Is there another way like patter recognition, hopefully in O(log n)?
It is possible to save each object in an other notation, like for example:
ref = "15691011"
A = "2456811"
But I don't know if this helps.
If you have all data in your objects sorted, you can do this routine faster, by comparing not single values in the row, but whole row step by step.
foreach (A, B, C, D,.. AS row)
{
for (i = 0, j = 0; i < row.length && j < ref.length)
{
if (row[i] < ref[j]) i++;
elseif (row[i] > ref[j]) j++;
else {
result[row] += 1;
i++; j++;
}
}
}
In this case you pass you reference only once for each row, but this algorithm need all your data to be already sorted.
You could start with the largest sequence (it has the largest change to have many references).
When you find - for example - 4 refs, you can safely skip all sequences with less then 4 elements.
Another early exit is to abort checking a sequence, when the current sequence cannot surpass the current max. for example: Your current max is 6 elements. You are processing a list of size 7, but the first two elements are no reference. The highest reachable for this list is 5, which is lower than 6, abort the sequence.
Problem in both cases is that you can not construct a complete array of results.
Assumptions:
There are m lists apart from the reference object.
The lists are sorted initially.
There are no repetition of elements in any array.
Scan all the arrays and find out the maximum element in all the lists. You only need to check the last element in each list. Call it MAX.
For each of the m + 1 lists, make a corresponding Boolean array with MAX elements and initialize their values to zero.
Scan all the arrays and make the corresponding indices of arrays 1.
For example, the corresponding array for the example reference object { 1, 5, 6, 9, 10, 11 } shall look like:
{1,0,0,0,1,1,0,0,1,1,1,0,0,...}
Now for every pair-wise combination, you can just check the corresponding indices and increment the count if both are 1.
The above algorithm can be done in linear time complexity with regards to the total number of elements in the data.
You should use other techniques used in search engines. For each number, you have a list of object contained this number in sorted order. In your case
1 -> {D, F}
5 -> {A, B, C, D}
6 -> {A, C, D, E}
9 -> {B, C, D, F}
10 -> {A, B, D}
11 -> {A}
Merging this list you can count how your object is similar to objects in list
A -> 4
B -> 3
C -> 2
D -> 5
E -> 1
F -> 2
After sorting, you get needed result. If you need only top k elements, you should use a priority queue.

Resources