Find all subsets with Enumerable.Zip or bitwise AND logic - c#

I wanted to find all subsets of an array of integers, so my first thought was to use the binary representation of numbers of size n bits, where 1 includes and 0 doesn't include.
For example:
int[] arr = { 3, 4, 5 };
going through numbers 0 to 7 gives me:
0,0,0
0,0,1
0,1,0
0,1,1
1,0,0
...etc
this maps to:
empty
5
4
4,5
3
...etc
To do the mapping I used Enumerable.Zip. The code is:
public static IEnumerable<byte> ToBinary(this int value, int size)
{
return ToBinaryStream(value, size).Reverse();
}
private static IEnumerable<byte> ToBinaryStream(int value, int size)
{
if (value < 0)
yield break;
do
{
yield return (byte)(value & 1);
--size;
}
while ((value = value >> 1) > 0 || size > 0);
}
int?[] arr = { 1,2,3,4 };
List<int[]> subsets = new List<int[]>();
for (int i = 0; i < (int)Math.Pow(2, (arr.Length)); i++)
{
var subset = i.ToBinary(arr.Length).Zip(arr, (value, array) => value == 1 ? array : null)
.Where(a => a != null).Cast<int>().ToArray();
subsets.Add(subset);
}
Seems to work great. Question is how can I use bitwise AND logic to do the same thing?
I want to map 1000 to the first element in the array, and 1001 to the first and last etc.

To check if an element x with index i should be included for a number num:
Shift 1 left by i
Bitwise AND the result with num
If the result is greater than 0, include x
Here's the code:
int[] arr = { 1,2,3,4 };
List<int[]> subsets = Enumerable
.Range(0, (int)Math.Pow(2, (arr.Length)))
.Select(num => arr.Where((x, i) => ((1 << i) & num) > 0).ToArray())
.ToList();
It treats the lowest bit in num as the first element in the array to avoid having to reverse things. Interesting idea by the way :)

Related

Get all split options to k chunks

I need to split an array arr into k chunks where the union of all the chucks is arr and there in no same element in two chunks.
For example for
int[] arr = new int[] {1, 2, 3, 4, 5};
int k = 3;
I need to return all the possible splits:
[[1], [2], [3,4,5]]
[[1], [2,3], [4,5]]
[[1], [2,3,4], [5]]
[[1,2], [3], [4,5]]
[[1,2], [3,4], [5]]
[[1,2,3], [4], [5]]
How can I do that efficiently in C#?
You have a combinatoric problem: given an array of n item you should sample k subarrays or, put it differently, k - 1 splits from n - 1:
[A, B, C, D, E] n items, n - 1 possible splits
^ ^
| | k - 1 splits from n - 1 avaialable
| |
[A] [B, C] [D, E] k chunks
Note that we have standard combinatoric problem
k - 1 from n - 1 unordered without repetitions
Code for such sampling can be
private static IEnumerable<int[]> Samples(int take, int from) {
if (take > from || take <= 0 || from <= 0)
yield break;
int[] array = Enumerable.Range(0, take).ToArray();
for (bool agenda = true; agenda; ) {
agenda = false;
yield return array.ToArray();
for (int i = array.Length - 1; i >= 0; --i)
if (array[i] < from - take + i) {
agenda = true;
array[i] += 1;
for (int j = i + 1; j < array.Length; ++j)
array[j] = array[i] + j - i;
break;
}
}
}
Having this sampling routine, we can implement splitting into chunks:
private static IEnumerable<T[][]> MyChunks<T>(T[] array, int take) {
if (take > array.Length)
yield break;
foreach (var sample in Samples(take - 1, array.Length - 1)) {
T[][] result = new T[take][];
for (int i = 0, from = 0, to; i <= sample.Length; ++i, from = to) {
to = i < sample.Length ? sample[i] + 1 : array.Length;
result[i] = array
.Skip(from)
.Take(to - from)
.ToArray();
}
yield return result;
}
}
Demo:
var arr = new int[] { 1, 2, 3, 4, 5};
int k = 3;
string report = string.Join(Environment.NewLine, MyChunks(arr, k)
.Select(chunk => "[" + string.Join(", ", chunk
.Select(item => $"[{string.Join(",", item)}]")) + "]"));
Console.Write(report);
Output:
[[1], [2], [3,4,5]]
[[1], [2,3], [4,5]]
[[1], [2,3,4], [5]]
[[1,2], [3], [4,5]]
[[1,2], [3,4], [5]]
[[1,2,3], [4], [5]]
On way to solve such a problem is to divide and conquer. We could first solve how we compute the possible splits of an array if we only ever wanted to split into two sub arrays (k = 2).
I.e. our function, when given 1,2,3,4, should return 1|2,3,4, 1,2|3,4, and 1,2,3|4 where | marks the border between the left and right subarrays.
Note how the | starts at the left-most position (that still produces a non-empty split on the left) and gradually moves to the right, until no more non-empty split can be produced for the right.
A C# function that does this is shown below:
IEnumerable<(Memory<int>, Memory<int>)> PossibleSplits(
Memory<int> xs) {
for (var i = 1; i < xs.Length; i++)
yield return (xs[..i], xs[i..]);
}
var splits = PossibleSplits(new[] { 1, 2, 3, 4, 5 }.AsMemory());
As you can see it returns a sequence of left/right tuples.
I'm using Memory so no new arrays are allocated when splitting the input data.
One nice property of this function is that it returns an empty sequence when the input array's length is smaller than 2.
So how do we split to an abritrary number of splits, not only 2? One trick is to recursively split the left side of a split again until the left side becomes to small to be split.
To that end we have to modify the above function in several ways:
The return value must change from a sequence of Memory tuples to a sequence of Memory sequences.
i cannot always start at position 1. When splitting into an arbitrary number k of splits, our function must make sure that the left side always holds enough elements for it to be split again into at least k - 1 subarrays. Therefor i must start at k - 1.
Obviously the function needs to call itself to become recursive. And it must do so with one of the two subarrays (we choose left) and the predecssor of k. Decrementing k accounts for the other subarray that won't be split up any further, but will be returned as part of the result of course.
We must add an exit condition to break the recursive cycle. When k is below 2 then we cannot meaningfully split the array (regardless of its size) and just return the input array wrapped as a singleton sequence.
Below is a recursive version of the function above that does just that:
public static class Ext {
public static IEnumerable<IEnumerable<Memory<int>>> PossibleSplits(
this Memory<int> xs,
int k) {
if (k < 2) {
yield return Enumerable.Repeat(xs, 1);
yield break;
}
for (var i = k - 1; i < xs.Length; i++) {
var (left, right) = (xs[..i], xs[i..]);
foreach (var split in left.PossibleSplits(k - 1))
yield return split.Append(right);
}
}
}
var splits = new[] { 1, 2, 3, 4, 5 }
.AsMemory()
.PossibleSplits(k: 3);
It's an extension method, just because I like them.
You asked for an efficient solution, but efficiency is relative. This solution is efficient in terms of...
...memory because now new arrays are allocated (hush, we don't talk about all the enumerators created by this code);
...laziness, because only requested values will be computed;
...code size.
It's not the most efficient in terms of runtime speed, because of all the overhead enumerators introduce.

Extract elements from List of List in C#

I'm trying to solve the HackerRank excercise "Non-Divisible Subset"
https://www.hackerrank.com/challenges/non-divisible-subset/
Excercise track
The exercise track is about creating a program that will take in a list of integers and a number 'k', and will output the count of the maximum number of integers in the list that are not divisible by 'k' and are non-repeating.
My problem is that results differs from Expected output.
Can you detect any problems in my code? Probably it's a logic error but I'm stuck. Please help me.
With input k=9 and input list = 422346306, 940894801, 696810740, 862741861, 85835055, 313720373,
output should be 5 but my code get 6.
public static int nonDivisibleSubset(int k, List<int> s)
{
var x = GetPerm(s);
var y = x.Where(x => x.Value % k != 0).Select(x=>x.Key).ToList();
var a = y.SelectMany(x => x).ToHashSet();
return a.Count();
}
static Dictionary<List<int>,int> GetPerm (List<int> list)
{
Dictionary<List<int>,int> perm = new Dictionary<List<int>, int>();
for (int i = 0; i < list.Count; i++)
{
for (int j = i+1; j < list.Count; j++)
{
List<int> sumCouple = new List<int>();
sumCouple.Add(list[i]);
sumCouple.Add(list[j]);
perm.Add(sumCouple, sumCouple.Sum());
}
}
return perm;
}
As I can see the actual problem is quite different:
Given a set of distinct integers, print the size of a maximal subset of where the sum of any numbers in is not evenly divisible by k.
If we have a look at the example:
list = {422346306, 940894801, 696810740, 862741861, 85835055, 313720373}
k = 9
we can't take all 6 numbers since 940894801 + 313720373 is evenly divisible by k = 9. The required subset is all but last item: {422346306, 940894801, 696810740, 862741861, 85835055}
And the solution will be different as well:
public static int nonDivisibleSubset(int k, List<int> s)
{
Dictionary<int, int> remainders = s
.GroupBy(item => item % k)
.ToDictionary(group => group.Key, group => group.Count());
int result = 0;
foreach (var pair in remainders) {
if (pair.Key == 0 || pair.Key % (k / 2) == 0 && k % 2 == 0)
result += 1;
else if (!remainders.TryGetValue(k - pair.Key, out int count))
result += pair.Value;
else if (count < pair.Value)
result += pair.Value;
else if (count == pair.Value && pair.Key < k - pair.Key)
result += pair.Value;
}
return result;
}
The idea is to group all the numbers by their remainder when devided by k. Then we do the follow:
if remainder is 0 or k / 2 (for even k) we can take just one such number into the subset
if remainder is x we can add to subset either all such numbers or all the numbers which have remainder k - x.
Time complexity: O(n)
Space complexity: O(n)
Not an answer to the question - but there is at least one problem with your code - List's can't be used as a key for dictionary as is, because it does not override Equals/GetHashCode, so it will perform reference comparison. You can provide a custom equality comparer:
class PairListEqComparer : IEqualityComparer<List<int>>
{
public static PairListEqComparer Instance { get; } = new PairListEqComparer();
public bool Equals(List<int> x, List<int> y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null)) return false;
if (ReferenceEquals(y, null)) return false;
if (x.Count != 2 || y.Count != 2) return false; // or throw
return x[0] == y[0] && x[1] == y[1];
}
public int GetHashCode(List<int> obj) => HashCode.Combine(obj.Max(), obj.Min(), obj.Count);
}
And usage:
Dictionary<List<int>,int> perm = new Dictionary<List<int>, int>(PairListEqComparer.Instance);
Or consider using ordered value tuples (compiler will generate the needed methods). From that you can think about optimizations and better algorithm.
As for solution itself - valid brute-force approach would be to generate all permutations of all sizes, i.e. from 1 to s.Count and find the longest one which satisfies the condition (though I doubt that it will be efficient enough for hackerrank)

find two strings in a string array that their length together equals to 6

What I have :
string[] array = new string[] { "Hello", "Please", "Help", "Me", "Thanks" };
Now I want filter this array to find two string that their length together equals to 6:
{ "Help", "Me" }.
Thanks.
Sorry for my bad English.
Can be done in a single LINQ query...
array = array.Where(o => 6 - o.Length >= 0 && array.Any(n => n.Length == 6 - o.Length)).ToArray();
You can sorts the array by length of the strings and starts searching from the first string (min = 0) and the last one (max = array.Length - 1).
If the sum of the lengths if less than 6, advance min index.
If the sum of the lengths if greater than 6, reduce max index.
If the sum of the lengths are equals to 6, then you have the words.
if min index reach max index then there are no words.
public string[] Words(string[] array)
{
var sorted = array.OrderBy(s => s.Length).ToArray();
int min = 0;
int max = sorted.Length - 1;
while (min < max)
{
int length = sorted[min].Length + sorted[max].Length;
if (length < 6) min++;
else if (length > 6) max--;
else return new string[] { sorted[min], sorted[max] };
}
return null;
}

Formula to produce 1 for positive integers and 0 otherwise

I have a function (f) the takes a number of items (n) and a number of columns (c) and returns the optimal layout as an array of items per column. I define optimal as being as square as possible. So f(4,4) would return [4,4,4,4], f(17,4) would return [5,4,4,4], and f(1,4) would return [1,0,0,0]. My function works correctly in all my tests, but I am looking to alter it. My desire to do this is not because I am looking increase performance. I just want to do this, because I am experimenting and want to learn different techniques.
Here is the code:
public static int[] f(int n, int c){
int[] a = new int[c];
if(c>0 && n>=0){
int opt = (n-(n%c))/c;
n = n - (opt*c);
for(int i = 0;i<a.Length;i++){
a[i] = opt;
if(n>0){
a[i]++;
n--;
}
}
}
return a;
}
The function works by first determining the optimal number of items per col:
int opt = (n-(n%c))/c;
So f(17,4) would yield 4, f(19,4) would also yield 4, and f(3,4) would yield 0. Then the reminder is calculated:
n = n - (opt*c);
I then loop through the array (of length c) and assign a[i] equal to the optimal value. Finally, if the reminder is greater than 0 I add 1 to a[i]. This equally distributes the reminder across the array. This is the part I would like to alter.
Instead of checking if(n>0) and adding 1 to the array is there a formula I could use that might look like:
a[i] = opt + n*?????;
So n*??? would always equal 1 if n is greater than 0 and 0 if n is 0 or less?
The simple answer to your question is to use an expression with the conditional operator:
a[i] = opt + (n > 0 ? 1 : 0);
(n > 0 ? 1 : 0) will be 1 if n is greater than 0, and 0 otherwise.
On that note, there is a clearer and more concise way to implement your algorithm.
Determine the total number of items that can be distributed evenly between the slots (call this average). This has the value n / c (using integer division).
Determine the remainder that would be left after those are evenly distributed (call this remainder). This has the value n % c.
Put the value average + 1 in the first remainder slots, and put average in the rest.
The implementation for this would be:
public static int[] Distribute(int total, int buckets)
{
if (total < 0) { throw new ArgumentException("cannot be less than 0", "total"); }
if (buckets < 1) { throw new ArgumentException("cannot be less than 1", "buckets"); }
var average = total / buckets;
var remainder = total % buckets;
var array = new int[buckets];
for (var i = 0; i < buckets; i++)
{
array[i] = average + (i < remainder ? 1 : 0);
}
return array;
}
And the obligatory Linq version:
public static int[] DistributeLinq(int total, int buckets)
{
if (total < 0) { throw new ArgumentException("cannot be less than 0", "total"); }
if (buckets < 1) { throw new ArgumentException("cannot be less than 1", "buckets"); }
var average = total / buckets;
var remainder = total % buckets;
return Enumerable.Range(1, buckets)
.Select(v => average + (v <= remainder ? 1 : 0))
.ToArray();
}
If you want to use a formula:
Math.Max(n - Math.Abs(n - 1), 0)
should do the trick.
Your code should look like:
a[i] = opt + Math.Max(n - Math.Abs(n - 1), 0)
Another option for a formula would be
Math.Max(Math.Sign(n), 0)
If you are looking for a mathematical formula, I'm not sure you're going to find it as the function is discontinuous at n = 0.
How about a simple function which outputs int on a bool expression?
int IsPositive(int number)
{
//if number is > 0 return integer one (1), else return integer zero (0)
return number > 0 ? 1 : 0;
}
You can then use this in your code as such:
a[i] = opt + IsPositive(n);
//opt + 1 if n > 0, opt + 0 if n <= 0
Update: per your comment, you can just move the evaluation inline:
a[i] = opt + (n > 0 ? 1 : 0);
As an aside: you should make #BradleyDotNET's comment one of your programming mottos.

PermCheck codility. O(N) time complexity

Hi i have this solution for PermCheck codility. Here is the link which includes the question: https://codility.com/demo/results/demo73YNCU-8FK/
I got 100% for it but i got a time complexity of O(N * log(N)) or O(N).
How could i make this code O(N)? Could you also give a brief description of what makes code O(N)? Thankyou.
Code here for shortcut:
Array.Sort(A);
if(A[0] == 1 && A.Length == 1) return 1;
if(A[0] != 1) return 0;
int n = 0;
for(int i = 0; i < A.Length; i++)
{
if(i < A.Length - 1)
{
if(A[i+1] == A[i] + 1)
{
n += 1;
}
else
{
return 0;
}
}
}
return 1;
Create a bool array which has the same size as the input, N, and leave all elements as the default false value. Loop through every element X of the input. If X is greater than N then return false. If array[N-1] is true, return false. Otherwise set array[N-1] to true. Repeat. This is O(N).
Explanation: First, if you have a permutation, then you need elements 1..N, but if any element is larger than N, then surely some elements are missing. Second, if an element occurs twice, it's a problem, that's why we create the bool array to remember already seen elements.
My python solution (doesn't need additional libraries, looks pythonic and not difficult to understand by others):
def solution(A): return 1 if len(set(A)) == len(A) == sorted(A)[-1] else 0
It can be simplified to:
def solution(A):
if len(set(A)) == len(A):
if len(A) == sorted(A)[-1]:
return 1
return 0
I checked two moments:
1: Length of set from array (set can't have duplicates) is equals to length of initial array
Arrays passed (no duplicates):
[1, 3, 2, 4], [4, 3, 2, 5]
Arrays failed:
[1, 1, 2, 4], [5, 6, 6, 1]
2: Length of initial array is equals to last element in sorted array
Arrays passed:
[1, 2, 3, 4]: Length is equal to last element => return 1
Arrays failed:
[2, 3, 4, 5]: Length is not equal to last element => return 0
This code doesn't work if A contains 0 or negative elements, but according to task details: each element of array A is an integer within the range [1..1,000,000,000].
Codility shows 100% and O(N) or O(N * log(N))
Tried to make it O(N) (suggested by fejesjoco):
def solution(A):
array_length = len(A)
seen_values = [False] * array_length
for x in A:
if x > array_length:
return 0
else:
if seen_values[x - 1]:
return 0
else:
seen_values[x - 1] = True
return 1
Codility result 100% Detected time complexity: O(N) or O(N * log(N))
Both codes shows equal result and time complexity. Maybe codility can't evaluate it right?
Here is my 100/100 answer:
same Idea as #fejesjoco
https://codility.com/demo/results/demoNR485D-33P/
You can change int to long to get performance.
public int solution(int[] A)
{
// idea: add to set,dictionary. Count the size and compare to N.
// dedupe data when needed.
var set = new HashSet<int>();
var max = int.MinValue;
foreach (var item in A)
{
if (set.Contains(item)) return 0;
set.Add(item);
if (item > max) max = item;
}
return set.Count == max ? 1 : 0;
}
This is my solution that scored 100% in correctness and performance.
def solution(A):
arraylength = len(A)
if (arraylength > 100000):
raise ValueError("Out of bound range")
arr = sorted(A)
for i in range(arraylength):
if (arr[i] != i+1):
return 0
return 1
I know that this questions is asked long time ago but it is still active in codility.
Possible solution:
public int solution(int[] A)
{
return (Enumerable.Range(1, A.Length).Except(A).Count() == 0) ? 1 : 0;
}
Following the suggestion by fejesjoco,
Boolean[] bln = new Boolean[A.Length];
for (int i = 0; i < A.Length; i++)
{
if (A[i] > A.Length) // more than array length
return 0;
if (bln[A[i]-1]) // same value twice
return 0;
bln[A[i]-1] = true;
}
return 1;
This was my solution, it scored 100%. Do not forget to add "using System.Linq".
int distinctLength = A.Distinct().ToArray().Length;
// The array length should equal to the distinct array length which is also the maximum value in array A.
if (A.Length == distinctLength && distinctLength == A.Max()) { return 1; }
return 0;
Swift Solution 100%
public func solution(_ A : inout [Int]) -> Int {
// write your code in Swift 4.2.1 (Linux)
let sorted = A.sorted()
for (index, value) in sorted.enumerated() {
if index+1 != value {
return 0
}
}
return 1
}

Categories

Resources