Ranking sums from first to third c# - 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)
...

Related

Most efficient way to remove element of certain value everywhere from List? C#

EDIT: Benchmarks for different techniques published at the bottom of this question.
I have a very large List<int> full of integers. I want to remove every occurrence of "3" from the List<int>. Which technique would be most efficient to do this? I would normally use the .Remove(3) extension until it returns false, but I fear that each call to .Remove(3) internally loops through the entire List<int> unnecessarily.
EDIT: It was recommended in the comments to try
TheList = TheList.Where(x => x != 3).ToList();
but I need to remove the elements without instantiating a new List.
var TheList = new List<int> { 5, 7, 8, 2, 8, 3, 1, 0, 6, 3, 9, 3, 5, 2, 7, 9, 3, 5, 5, 1, 0, 4, 5, 3, 5, 8, 2, 3 };
//technique 1
//this technique has the shortest amount of code,
//but I fear that every time the Remove() method is called,
//the entire list is internally looped over again starting at index 0
while (TheList.Remove(3)) { }
//technique 2
//this technique is an attempt to keep the keep the list from
//being looped over every time an element is removed
for (var i = 0; i < TheList.Count; i++)
{
if (TheList[i] == 3)
{
TheList.RemoveAt(i);
i--;
}
}
Are there any better ways to do this?
Benchmarks
I tested three techniques to remove 10,138 from an array with 100,000 elements: the two shown above, and one recommended by Serg in an answer. These are the results:
'while' loop: 179.6808ms
'for' loop: 65.5099ms
'RemoveAll' predicate: 0.5982ms
Benchmark Code:
var RNG = new Random();
//inclusive min and max random number
Func<int, int, int> RandomInt = delegate (int min, int max) { return RNG.Next(min - 1, max) + 1; };
var TheList = new List<int>();
var ThreeCount = 0;
for (var i = 0; i < 100000; i++)
{
var TheInteger = RandomInt(0, 9);
if (TheInteger == 3) { ThreeCount++; }
TheList.Add(TheInteger);
}
var Technique1List = TheList.ToList();
var Technique2List = TheList.ToList();
var Technique3List = TheList.ToList();
<div style="background-color:aquamarine;color:#000000;">Time to remove #ThreeCount items</div>
//technique 1
var Technique1Stopwatch = Stopwatch.StartNew();
while (Technique1List.Remove(3)) { }
var Technique1Time = Technique1Stopwatch.Elapsed.TotalMilliseconds;
<div style="background-color:#ffffff;color:#000000;">Technique 1: #(Technique1Time)ms ('while' loop)</div>
//technique 2
var Technique2Stopwatch = Stopwatch.StartNew();
for (var i = 0; i < Technique2List.Count; i++)
{
if (Technique2List[i] == 3)
{
Technique2List.RemoveAt(i);
i--;
}
}
var Technique2Time = Technique2Stopwatch.Elapsed.TotalMilliseconds;
<div style="background-color:#ffffff;color:#000000;">Technique 2: #(Technique2Time)ms ('for' loop)</div>
//technique 3
var Technique3Stopwatch = Stopwatch.StartNew();
var RemovedCount = Technique3List.RemoveAll(x => x == 3);
var Technique3Time = Technique3Stopwatch.Elapsed.TotalMilliseconds;
<div style="background-color:#ffffff;color:#000000;">Technique 3: #(Technique3Time)ms ('RemoveAll' predicate)</div>
You can just use List<T>.RemoveAll and pass your predicate - https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.removeall?view=net-6.0#System_Collections_Generic_List_1_RemoveAll_System_Predicate__0__ . This guaranteed to be linear complexity O(list.Count)
TheList.RemoveAll(x=>x==3);
Additionally, RemoveAll performs some GC-specific things internally, so I think in some cases this may provide some additional performance advantages against the simple hand-made loop implementation (but I'm unsure here).
If you want to do it all yourself, you can check out the implementation of RemoveAll here. Generally, it is just a while loop as in your question.
Additionally, as we can see from GitHub implementation (and as Jon Skeet mentioned in the comment) the remove operation causes the rest of list (all items after the first removed items) to be copied (shifted) on the free space, intorduced by deletion. So, if you have really huge list and/or want to remove something frequently, you may consider to switching to some other data structure, such as linked list.

Check times a value is next to the same value in an array

Lets say I have this array on C#:
int myList = {1,4,6,8,3,3,3,3,8,9,0}
I want to know if a value (lets say from 0-9) is next to itself in the list and how many times. In this case, the value 3 is next to itself and it has 4 repetitions. If I have a list {0,1,2,3,4,5,5,6,7} the value 5 is next to itself and has 2 repetitions.
Repetitions have a limit of 5. No value can be repeated more than 5 times. The far I went is making if statements, but I know there's a better way of doing it.
The standard of question is not that good but writing the answer
int lastValue = myList[0];
int times = 0;
foreach (int value in myList) {
if (lastValue == value) {
times++;
}
else if (times <= 1) {
lastValue = value;
times = 1;
}
else
break;
}
You only have to iterate on your list and keep a counter that will count only the consecutive duplicate integer.
If you want a neater solution, you might look at using an open source library called morelinq (by Jon Skeet and few others) on nuget. It has useful extension methods for LINQ.
One of them is called GroupAdjacent, which is applicable to your problem.
var testList = new[] { 1, 4, 6, 8, 3, 3, 3, 3, 8, 9, 0 };
var groups = testList.GroupAdjacent(t => t);
var groupsWithMoreThanOneMember = groups.Where(g => g.Count() > 1);

How to rewrite LINQ-based code as a for loop

I'm using the following code:
public class Card
{
public Card(Rank rank, Suit suit)
{
this.Rank = rank;
this.Suit = suit;
}
public Rank Rank { get; private set; }
public Suit Suit { get; private set; }
}
public enum Rank : byte
{
Ace = 1,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
Six = 6,
Seven = 7,
Eight = 8,
Nine = 9,
Ten = 10,
Jack = 11,
Queen = 12,
King = 13,
}
public enum Suit : byte
{
Club = 1, // Klavers
Diamond = 2, // Ruiten
Heart = 3, // Harten
Spades = 4 // Schoppen
}
public class Deck
{
private List<Card> deck = new List<Card>(52);
public Deck()
{
this.FillStack();
}
public void FillStack()
{
this.deck.Clear();
this.deck.AddRange(
Enumerable.Range(1,4)
.SelectMany( s =>
Enumerable.Range(1, 13)
.Select( n => new Card( (Rank)n , (Suit)s ) )
)
) ;
}
Can I replace the LINQ-based code in FillStack with equivalent code based on a for loop? The goal is to make the code easier to understand and extend for those who are less familiar with LINQ.
Yes, this looks like a good example of how not to use the LINQ helper methods. There is no advantage over a plain old and far more readable
for (int s = 1; s <= 4; s++)
for (int n = 1; n <= 13; n++)
this.deck.Add(new Card((Rank)n, (Suit)s));
The LINQ version could be made somewhat more readable though: here's how it could look when not attempting to stuff it all into a single expression.
var suits = Enumerable.Range(1, 4);
var ranks = Enumerable.Range(1, 13);
var cards =
from s in suits
from n in ranks
select new Card((Rank)n, (Suit)s);
this.deck.AddRange(cards);
This should be easier to understand, but it still means pretty much the same thing.
You could just say something like this
public void FillStack()
{
this.deck.Clear() ;
this.deck.AddRange(
Enumerable
.Range(0,52)
.Select( n => new Card( (Rank)(1+n%13), (Suit)(1+n/13) ) )
) ;
return ;
}
Or even
public void FillStack()
{
this.deck.Clear() ;
foreach ( Rank rank in Enum.GetValues(typeof(Rank)) )
{
foreach( Suit suit in Enum.GetValues(typeof(Suit)) )
{
Card card = new Card( rank , suit ) ;
this.deck.Add( card ) ;
}
}
return;
}
Your question is to replace the LINQ query with for loop so it's easier to understand. That's fair enough. But since you are learning it's worth to understand the query itself. Then you will be able to replace it yourself or, better yet, you will be comfortable enough with it to leave it there.
Let's break it down and try to explain it line by line:
[1] this.deck
[2] .AddRange(
[3] Enumerable.Range(1, 4)
[4] .SelectMany(s =>
[5] Enumerable.Range(1, 13)
[6] .Select(n =>
[7] new Card((Rank)n, (Suit)s))));
Start from the line #3 Enumerable.Range(1,4) - the method returns range of numbers from 1 to 4. So we have a collection 1, 2, 3, 4. In line #4 we access this collection item by item. So we actually iterate over the items. Each item is denoted by a variable s - you can use it to access the items. The same mechanism is repeated to create iteration from 1 to 13 (lines #5 and #6). The items in the second iteration are accessed by variable n.
Bigger picture - we iterate from 1 to 4 and inside that we iterate from 1 to 13. Now, in line #7 we create a Card and the numbers from both iterations - denoted as n and s - are used in the constructor of the class. Because we iterate through number from 1 to 4 and inside that from 1 to 13 we will get all the combinations: (1,1) (1,2) (1,3) (1,4) (2,1) (2,2) and so on. Each item from the combination will create a card. So it will result in a collection of cards. And now go back to line #2 AddRange method is responsible for adding this range of cards to the deck collection.
Hope this will help to understand the query and replace it with more imperative code.

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

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 :)

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

Categories