all possible combinations from a set of sets (n choose k) - c#

Given several sets such as {1,2,3,4},{1,2},{1},{1,2,3,},..., and a number k such that the combination that ought to be formed is exactly of length k, how would one go about generating a unique combination of length k if I can only choose one number from each set?
There are exactly k number of sets to choose from. I'm looking for the most efficient way to do this in c#.
Intuitively i tried(though i'm not sure it is correct) generating all possible combinations for each set then concatenating each i'th combination from each set with the corresponding i'th combination from subsequent sets to form a k long unique combination but I'm sure there is a general case for this type of question.
Can anyone please offer some tips or advise? Specifically which math or computer science topic this falls under and how these questions are typically solved?
in the snippet below a string in the array theSets would be for example "31"
this means that k = 2 (length of a string in theSets) and there are two sets one of which is {1,2,3} and {1}. with these sets, generate all the unique combinations and provide a count
private int combicount(string[] theSets)
{
int size = theSets[0].Length;
int count = 0;
List<HashSet<int>> l = new List<HashSet<int>>();
foreach (string s in theSets)
{
foreach (char c in s)
{
HashSet<int> h = new HashSet<int>();
for (int n = 1; n <= int.Parse(c.ToString()); n++)
{
h.Add(n);
}
l.Add(h);
}
//toDO - generate all unique combinations by picking exactly 1 from each set in l
//toDO - count the number of unique combinations generated
}
return count;
}

You can use LINQ to solve it like this:
private void CombineSets()
{
var mySets = new List<HashSet<int>>();
mySets.Add(new HashSet<int>() { 1, 2, 3, 4 });
mySets.Add(new HashSet<int>() { 1, 2 });
mySets.Add(new HashSet<int>() { 1 });
mySets.Add(new HashSet<int>() { 1, 2, 3 });
var result = new HashSet<int>();
while (mySets.Count > 0)
{
//get the smallest set
var set = mySets.OrderBy(x => x.Count).First();
//remove the set from the collection as we do not need it anymore (we got our value)
mySets.Remove(set);
//remove what is in the result from the set (as we only want unique values)
set.ExceptWith(result);
//then add the first value from the remaining values to our result set
result.Add(set.First());
}
}
To make this more efficient you could also sort the list before the while loop. This at least solves the first few lines of your question.
Hope this helps :)

Related

Ranking sums from first to third c#

I want to rank the number i added up from first to third but i cant think of a way to rank it properly since when there is a duplicate it will only show the number once and continues to the second highest
im new to the language and it would be great for someone to help me on this
Edit: Sorry i think there is a misunderstanding here my sums are in an array that is connected to the names in another array and im trying to sort it out with the same index value
Edit 2: Also i am stuck at c# 7.3 so i cant use some of the new codes
int first = Int32.MinValue;
int fs, nd, thr;
int temp = 0;
for (fs = 0; fs < hounds; fs++)
{
if (score_SUM[fs] > first)
{
first = score_SUM[fs];
temp = fs;
}
}
Console.WriteLine("\n" + "First:{1} {0}", first, houndname[temp]);
int second = Int32.MinValue;
for (nd = 0; nd < hounds; nd++)
{
if (score_SUM[nd] > second && score_SUM[nd] < first)
{
second = score_SUM[nd];
temp = nd;
}
}
Console.WriteLine("Second:{1} {0}", second, houndname[temp]);
int third = Int32.MinValue;
for (thr = 0; thr < hounds; thr++)
{
if (score_SUM[thr] > third && score_SUM[thr] < second)
{
third = score_SUM[thr];
temp = thr;
}
}
Console.WriteLine("Third:{1} {0}", third, houndname[temp]);
Console.ReadLine();
example
10 , 5 , 10 , 6, 1
The output will be like
10
6
5
But I expected
10
10
6
but i cant find a way to write a block a code for that
You're drastically over-engineering this.
If what you have is an array (or list/collection of some kind) of values then you can simply sort that array (descending in this case) and display the first three values.
For example, consider this list of values:
var hounds = new List<int> { 10, 5, 10, 6, 1 };
Then you can sort that list:
hounds = hounds.OrderByDescending(h => h).ToList();
And, either in a loop or by directly referencing the first three (if you know there will always be at least three), output them. For example:
Console.WriteLine("First:{0}", hounds[0]);
Console.WriteLine("Second:{0}", hounds[1]);
Console.WriteLine("Third:{0}", hounds[2]);
Regarding your edit...
my sums are in an array that is connected to the names in another array and im trying to sort it out with the same index value
You're doing it wrong.
Instead of trying to manually keep multiple arrays of values synchronized, maintain one array of meaningful objects. For example, consider how you define a "hound":
public class Hound
{
public int Value { get; set; }
public string Name { get; set; }
}
Create your list of hounds, not multiple lists of disconnected and unrelated values that you need to manually remember and keep synchronized. For example:
var hounds = new List<Hound>
{
new Hound { Value = 10, Name = "Fido" },
new Hound { Value = 5, Name = "Barney" },
new Hound { Value = 10, Name = "Jake" },
new Hound { Value = 6, Name = "Woof" },
new Hound { Value = 1, Name = "Dog" }
};
The rest of the process is the same. Sort the list:
hounds = hounds.OrderByDescending(h => h.Value);
And output the data:
Console.WriteLine("First:{1} {0}", hounds[0].Value, hounds[0].Name);
Console.WriteLine("Second:{1} {0}", hounds[1].Value, hounds[1].Name);
Console.WriteLine("Third:{1} {0}", hounds[2].Value, hounds[2].Name);
Overall, the main point here is that you don't need a ton of convoluted logic just to get the top 3 values in a list. Sorting is a common and well-established operation. All you need is the right data structure to be sorted.
Or, as usual, someone else has already said it better before...
"Smart data structures and dumb code works a lot better than the other way around."
Eric S. Raymond, The Cathedral & the Bazaar
Does this answer your question?
List<int> list = new() { 10, 5, 10, 6, 1 };
list.Sort((x, y) => y.CompareTo(x));
for (int i = 0; i < 3; i++)
{
Console.WriteLine(list[i]);
}
If you only want the three highest values you can also do this:
List<int> list = new() { 10, 5, 10, 6, 1 };
IEnumerable<int> highestValues = list.OrderByDescending(x => x).Take(3);
foreach (int value in highestValues)
{
Console.WriteLine(value);
}
Just change your < symbols to <= symbols. So your checks for the second and third ranks would look something like this:
// check for second
if (score_SUM[nd] > second && score_SUM[nd] <= first)
...
// check for third
if (score_SUM[thr] > third && score_SUM[thr] <= second)
...

Find the major element of the array

I'm stuck in this logic and I don't know how to solve it, I have the following question:
"Given a nums array of size n, return the majority element, that is, the element that appears the most times in your array."
And I have this code base:
`using System;
using System.Text.RegularExpressions;
public class Program
{
public static void Main(String[] args)
{
int n = int.Parse(Console.ReadLine());
int[] num = new int[n];
for (int i = 0; i < n; i++)
{
num[i] = int.Parse(Console.ReadLine());
}
Console.WriteLine(MajorityElement(num));
}
public static int MajorityElement(int[] nums)
{
int major = nums[0];
int count = 1;
for (int i = 0; i < nums.Length; i++)
{
if ( )
{
major = nums[i];
count++;
}
else
{
if (major == nums[i])
{
count++;
}
else
{
count--;
}
}
}
return major;
}
}`
But I can't think of what the logic of the first IF would be.
How would that logic be in this question? And how would I solve it?
A succinct but inefficient way to solve this is like so:
int major = nums.ToLookup(n => n).MaxBy(item => item.Count()).Key;
How does this work?
nums.ToLookup(n => n)
This uses Enumerable.ToLookup() to create a lookup table.
The lookup table will contain one entry for each unique number in the array.
Each entry will consist of a key (which is the number) and a list of all the numbers with the same value.
The n => n part selects the lookup key from each value. In this case they are the same, but usually it would be used to select some property from a class that you were creating a lookup for.
That is, given this list (the numbers can be in any order):
int[] nums = { 1, 2, 2, 3, 3, 3 };
Then the lookup table will have 3 elements (one for each unique number in the list) as follows:
[1] = {1}
[2] = {2, 2}
[3] = {3, 3, 3}
Note that the numbers in the square brackets are NOT indices - they are keys. They do not have to be integers; they could be strings, for example.
I think you will already be able to see how inefficient this really is! We shouldn't need to store a list of all the matching numbers just to obtain a count of them. Nevertheless, let's carry on with the explanation.
.MaxBy(item => item.Count())
This selects the maximum element of the lookup table according to each element's item.Count() which is the count of all the items for each element. In the example above, you can see that [1] has a count of 1, [2] has a count of 2 and [3] has a count of 3.
.Key
Once we've selected the maximum element in the lookup table according to the count, we just access the key for that element. Remember that the keys for the lookup tables are the integers that we've counted. The key of the element with the most items, therefore, is the number we're looking for.
A much more efficient approach: Use a Dictionary<Tkey, TValue>
We can use a dictionary to count the number of unique items. The dictionary keys will be the unique integer values in the list, and the dictionary value for each key will be the number of occurrences of that key.
var dict = new Dictionary<int, int>();
foreach (int value in nums)
{
if (dict.ContainsKey(value))
++dict[value];
else
dict[value] = 1;
}
int major = dict.MaxBy(kvp => kvp.Value).Key;
This is much easier to understand. It goes through each number in the input and if it is not already in the dictionary, it adds a value of 1 to the dictionary. (This is of course the initial count.) If the number is already in the dictionary, it instead increments the value - i.e. it increments the count of occurrences of that number.
Finally the code selects the dictionary element with the highest value (i.e. highest repeat count) and selects the key for that value (which will be the number that was repeated that many times).

How to improve my FindDuplicate classic algorithm

I have classic find duplicate algorithm like this:
int n = int.Parse(Console.ReadLine());
Console.WriteLine();
List<int> tempArr = new List<int>();
List<int> array = new List<int>();
for (int i = 0; i < n; i++)
{
Console.Write("input number {0}: ", i + 1);
tempArr.Add(int.Parse(Console.ReadLine()));
}
tempArr.Sort();
for (int i = 0; i < n; i++)
{
for (int j = i+1; j < n; j++)
{
if (tempArr[i] == tempArr[j])
{
array.Add(tempArr[i]);
}
}
}
Everything work's okay, but if i have just two duplicate numbers like (1,2,2,3,4,5) how can i add them both to List<int> **array** with one clean shot at the loop ?
Instead of lists you could use some kind of data structure that have a better search capability (hash tables or binary trees, for example). Even if you have just one duplicate, the problem is that you need to check if you have already added the element in the list, so the key operation in your algorithm is the search. The faster you perform the search, the faster the algorithm will be. Using binary search, which is the fastest way to search, you get O(nlogn) (you perform n searches of O(logn)).
An even better way to do this is to have some kind of array that has the same size as your input range and "tick" each value that you already have. This search runs in constant time, but gets inefficient if you have a large range of input.
You can use distinct:
array = tempArr.Distinct().ToList();
Distinct isn't in linear time, if that's what you're looking for ("one clean shot"). If you know more about the input you might be able to find a way to do this in linear time. For example, if you know if the integers you take are in a certain range.
To extract all the duplicates you can use Linq:
List<int> tempList = new List<int>() { 1, 2, 2, 3, 4, 5 };
// array == [2, 2]
List<int> array = tempList
.GroupBy(x => x)
.Where(x => x.Count() > 1)
.SelectMany(x => Enumerable.Repeat(x.Key, x.Count()))
.ToList();

How to merge 2 sorted listed into one shuffled list while keeping internal order in c#

I want to generate a shuffled merged list that will keep the internal order of the lists.
For example:
list A: 11 22 33
list B: 6 7 8
valid result: 11 22 6 33 7 8
invalid result: 22 11 7 6 33 8
Just randomly select a list (e.g. generate a random number between 0 and 1, if < 0.5 list A, otherwise list B) and then take the element from that list and add it to you new list. Repeat until you have no elements left in each list.
Generate A.Length random integers in the interval [0, B.Length). Sort the random numbers, then iterate i from 0..A.Length adding A[i] to into position r[i]+i in B. The +i is because you're shifting the original values in B to the right as you insert values from A.
This will be as random as your RNG.
None of the answers provided in this page work if you need the outputs to be uniformly distributed.
To illustrate my examples, assume we are merging two lists A=[1,2,3], B=[a,b,c]
In the approach mentioned in most answers (i.e. merging two lists a la mergesort, but choosing a list head randomly each time), the output [1 a 2 b 3 c] is far less likely than [1 2 3 a b c]. Intuitively, this happens because when you run out of elements in a list, then the elements on the other list are appended at the end. Because of that, the probability for the first case is 0.5*0.5*0.5 = 0.5^3 = 0.125, but in the second case, there are more random random events, since a random head has to be picked 5 times instead of just 3, leaving us with a probability of 0.5^5 = 0.03125. An empirical evaluation also easily validates these results.
The answer suggested by #marcog is almost correct. However, there is an issue where the distribution of r is not uniform after sorting it. This happens because original lists [0,1,2], [2,1,0], [2,1,0] all get sorted into [0,1,2], making this sorted r more likely than, for example, [0,0,0] for which there is only one possibility.
There is a clever way of generating the list r in such a way that it is uniformly distributed, as seen in this Math StackExchange question: https://math.stackexchange.com/questions/3218854/randomly-generate-a-sorted-set-with-uniform-distribution
To summarize the answer to that question, you must sample |B| elements (uniformly at random, and without repetition) from the set {0,1,..|A|+|B|-1}, sort the result and then subtract its index to each element in this new list. The result is the list r that can be used in replacement at #marcog's answer.
Original Answer:
static IEnumerable<T> MergeShuffle<T>(IEnumerable<T> lista, IEnumerable<T> listb)
{
var first = lista.GetEnumerator();
var second = listb.GetEnumerator();
var rand = new Random();
bool exhaustedA = false;
bool exhaustedB = false;
while (!(exhaustedA && exhaustedB))
{
bool found = false;
if (!exhaustedB && (exhaustedA || rand.Next(0, 2) == 0))
{
exhaustedB = !(found = second.MoveNext());
if (found)
yield return second.Current;
}
if (!found && !exhaustedA)
{
exhaustedA = !(found = first.MoveNext());
if (found)
yield return first.Current;
}
}
}
Second answer based on marcog's answer
static IEnumerable<T> MergeShuffle<T>(IEnumerable<T> lista, IEnumerable<T> listb)
{
int total = lista.Count() + listb.Count();
var random = new Random();
var indexes = Enumerable.Range(0, total-1)
.OrderBy(_=>random.NextDouble())
.Take(lista.Count())
.OrderBy(x=>x)
.ToList();
var first = lista.GetEnumerator();
var second = listb.GetEnumerator();
for (int i = 0; i < total; i++)
if (indexes.Contains(i))
{
first.MoveNext();
yield return first.Current;
}
else
{
second.MoveNext();
yield return second.Current;
}
}
Rather than generating a list of indices, this can be done by adjusting the probabilities based on the number of elements left in each list. On each iteration, A will have A_size elements remaining, and B will have B_size elements remaining. Choose a random number R from 1..(A_size + B_size). If R <= A_size, then use an element from A as the next element in the output. Otherwise use an element from B.
int A[] = {11, 22, 33}, A_pos = 0, A_remaining = 3;
int B[] = {6, 7, 8}, B_pos = 0, B_remaining = 3;
while (A_remaining || B_remaining) {
int r = rand() % (A_remaining + B_remaining);
if (r < A_remaining) {
printf("%d ", A[A_pos++]);
A_remaining--;
} else {
printf("%d ", B[B_pos++]);
B_remaining--;
}
}
printf("\n");
As a list gets smaller, the probability an element gets chosen from it will decrease.
This can be scaled to multiple lists. For example, given lists A, B, and C with sizes A_size, B_size, and C_size, choose R in 1..(A_size+B_size+C_size). If R <= A_size, use an element from A. Otherwise, if R <= A_size+B_size use an element from B. Otherwise C.
Here is a solution that ensures a uniformly distributed output, and is easy to reason why. The idea is first to generate a list of tokens, where each token represent an element of a specific list, but not a specific element. For example for two lists having 3 elements each, we generate this list of tokens: 0, 0, 0, 1, 1, 1. Then we shuffle the tokens. Finally we yield an element for each token, selecting the next element from the corresponding original list.
public static IEnumerable<T> MergeShufflePreservingOrder<T>(
params IEnumerable<T>[] sources)
{
var random = new Random();
var queues = sources
.Select(source => new Queue<T>(source))
.ToArray();
var tokens = queues
.SelectMany((queue, i) => Enumerable.Repeat(i, queue.Count))
.ToArray();
Shuffle(tokens);
return tokens.Select(token => queues[token].Dequeue());
void Shuffle(int[] array)
{
for (int i = 0; i < array.Length; i++)
{
int j = random.Next(i, array.Length);
if (i == j) continue;
if (array[i] == array[j]) continue;
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
Usage example:
var list1 = "ABCDEFGHIJKL".ToCharArray();
var list2 = "abcd".ToCharArray();
var list3 = "#".ToCharArray();
var merged = MergeShufflePreservingOrder(list1, list2, list3);
Console.WriteLine(String.Join("", merged));
Output:
ABCDaEFGHIb#cJKLd
This might be easier, assuming you have a list of three values in order that match 3 values in another table.
You can also sequence with the identity using identity (1,2)
Create TABLE #tmp1 (ID int identity(1,1),firstvalue char(2),secondvalue char(2))
Create TABLE #tmp2 (ID int identity(1,1),firstvalue char(2),secondvalue char(2))
Insert into #tmp1(firstvalue,secondvalue) Select firstvalue,null secondvalue from firsttable
Insert into #tmp2(firstvalue,secondvalue) Select null firstvalue,secondvalue from secondtable
Select a.firstvalue,b.secondvalue from #tmp1 a join #tmp2 b on a.id=b.id
DROP TABLE #tmp1
DROP TABLE #tmp2

How can I count the unique numbers in an array without rearranging the array elements?

I am having trouble counting the unique values in an array, and I need to do so without rearranging the array elements.
How can I accomplish this?
If you have .NET 3.5 you can easily achieve this with LINQ via:
int numberOfElements = myArray.Distinct().Count();
Non LINQ:
List<int> uniqueValues = new List<int>();
for(int i = 0; i < myArray.Length; ++i)
{
if(!uniqueValues.Contains(myArray[i]))
uniqueValues.Add(myArray[i]);
}
int numberOfElements = uniqueValues.Count;
This is a far more efficient non LINQ implementation.
var array = new int[] { 1, 2, 3, 3, 3, 4 };
// .Net 3.0 - use Dictionary<int, bool>
// .Net 1.1 - use Hashtable
var set = new HashSet<int>();
foreach (var item in array) {
if (!set.Contains(item)) set.Add(item);
}
Console.WriteLine("There are {0} distinct values. ", set.Count);
O(n) running time max_value memory usage
boolean[] data = new boolean[maxValue];
for (int n : list) {
if (data[n]) counter++
else data[n] = true;
}
Should only the distinct values be counted or should each number in the array be counted (e.g. "number 5 is contained 3 times")?
The second requirement can be fulfilled with the starting steps of the counting sort algorithm.
It would be something like this:
build a set where the index/key is
the element to be counted
a key is connected to a variable which holds the number of occurences
of the key element
iterate the array
increment value of key(array[index])
Regards

Categories