Returning the possible sums
class Solution {
public static List<Double> solve(double[] arr) {
return IntStream.range(0, 1 << arr.length).boxed()
.filter(n -> (n & (n - 1)) != 0)
.map(n -> IntStream.range(0, arr.length)
.filter(i -> ((n >> i) & 1) == 1)
.mapToDouble(i -> arr[i])
.sum())
.collect(Collectors.toList());
}
}
Usage
final double[] arr = { 15.00, 0.55, 25.00, 7.00 };
System.out.println(solve(arr));
// Outputs [15.55, 40.0, 25.55, 40.55, 22.0, 7.55, 22.55, 32.0, 47.0, 32.55, 47.55]
Returning the possible pairs
class Solution {
public static List<Double[]> solve(double[] arr) {
return IntStream.range(0, 1 << arr.length).boxed()
.filter(n -> (n & (n - 1)) != 0)
.map(n -> IntStream.range(0, arr.length).boxed()
.filter(i -> ((n >> i) & 1) == 1)
.map(i -> arr[i])
.toArray(Double[]::new))
.collect(Collectors.toList());
}
}
Usage
final double[] arr = { 15.00, 0.55, 25.00, 7.00 };
final List<Double[]> result = solve(arr);
for (Double[] resultArr : result) {
System.out.println(Arrays.toString(resultArr));
}
Output
[15.0, 0.55]
[15.0, 25.0]
[0.55, 25.0]
[15.0, 0.55, 25.0]
[15.0, 7.0]
[0.55, 7.0]
[15.0, 0.55, 7.0]
[25.0, 7.0]
[15.0, 25.0, 7.0]
[0.55, 25.0, 7.0]
[15.0, 0.55, 25.0, 7.0]
Explanation
Let's break it down using {15.00, 0.55, 25.00, 7.00}
as input array:
IntStream.range(0, 1 << arr.length)
creates a stream of values from 0 until 1 << arr.length
. The expression 1 << n
is the same as Math.pow(n, 2)
, so 1 << arr.length
will be equal to 16.
The stream now is IntStream({0, 1, 2, 3, ..., 12, 13, 14, 15})
.
boxed()
converts this IntStream
to Stream<Integer>
. This allows us to convert it to a List<Integer>
in the end.
The stream now is Stream<Integer>({0, 1, 2, 3, ..., 12, 13, 14, 15})
.
filter(n -> (n & (n - 1)) != 0)
removes all power-of-2 elements from the stream including 0 (explanation). Why is this? Let's take a look at the binary representation of the current stream:
{0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111, 1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111}
Now assign every bit to its respective array element: the number 0110
correspond to the elements {0.55, 25.00}
, the number 0001
correspond to the elements {7.00}
and the number 1111
correspond to all elements ({15.00, 0.55, 25.00, 7.00}
) since all bits are set.
Following your test cases, we cannot include the input numbers in the output array (for example, neither 15.0, 0.55, 25 or 7 should be in the output array). Therefore, we cannot include the numbers which contain a single bit (such as 0001
or 0100
). These numbers are powers of 2, therefore we must remove them.
The stream now is Stream<Integer>({3, 5, 6, 7, ..., 12, 13, 14, 15})
.
map(...)
maps the elements of this stream to another type. For each element n
(assuming n
is equal to 6),
IntStream.range(0, arr.length)
creates a stream from 0 until 4.
The substream now is IntStream({0, 1, 2, 3})
.
filter(i -> ((n >> i) & 1) == 1)
keeps only the indexes of the bits which are 1.
The substream now is IntStream({1, 2})
. Note that n
is equal to 6, whose binary representation is 0110
. Since the mid-two bits are set, the mid-two elements in the input array will be kept.
mapToDouble(i -> arr[i])
maps each bit to its respective element in the input array.
The substream now is IntStream({0.55, 25.00})
.
sum()
sums all elements from this stream.
The result is 25.55
.
collect(Collectors.toList())
will repeat step 4 for all element in this stream, returning a List<Double>
.
The result is [15.55, 40.0, 25.55, 40.55, 22.0, 7.55, 22.55, 32.0, 47.0, 32.55, 47.55]
.