Im looking for some help with finding subsets of array.
int[] array = { 1,2,3,5,8,10,15,23};
I have to find all subsets of an array. If sum of the subsets elements equal to any number in array then my counter increment. For example: 1+2=3, 2+3=5, 5+8+10=23, 1+2+5=8, 2+3+8+10=23
public static void Main(string[] args)
{
int[] array = { 1, 2, 3, 5, 8, 10, 15, 23 };
int arrayLength = array.Count();
int sum = 0;
int subsetCount = 0;
for (int i = 0; i < arrayLength; i++)
{
for (int j = i + 1; j < arrayLength; j++)
{
sum = array[i] + array[j];
for (int m = j + 1; m < arrayLength; m++)
{
for (int k = 0; k < arrayLength; k++)
{
if (array[k] == sum)
{
subsetCount++;
}
}
sum = array[i] + array[j] + array[m];
}
}
}
Console.WriteLine(subsetCount);
Console.ReadLine();
}
I'm ok with 2-elements and 3-elements of subsets. But 4 and above I can't figured out how to solve it?
Any help would be greatly appreciated
You only need two loops to find the sum of all subsets. The outer loop is the starting point of subsets, and the inner loop is calculating the sums of all subsets from that starting point.
With the first index as starting points the subsets are 1+2, 1+2+3, 1+2+3+5 and so on. As you are only interested in the sum of the subsets you can just add one item after the other to get the sum of the subsets.
Then for each sum loop through the items to check for a match:
int[] array = { 1, 2, 3, 5, 8, 10, 15, 23 };
int subsetCount = 0;
for (int i = 0; i < array.Length; i++) {
int sum = array[i];
for (int j = i + 1; j < array.Length; j++) {
sum += array[j];
for (int k = 0; k < array.Length; k++) {
if (array[k] == sum) {
subsetCount++;
}
}
}
}
Console.WriteLine(subsetCount);
Edit:
I assumed that you meant continuous subsets, but from your examples it seems that you also want non-continuous subsets.
Lets's start with the correct solution:
23 = 15+8, 15+5+3, 15+5+2+1, 10+8+5, 10+8+3+2
15 = 10+5, 10+3+2, 8+5+2
10 = 8+2, 5+3+2
8 = 5+3, 5+2+1
5 = 3+2
3 = 2+1
That gives us 14 different subsets that sums up to an item in the set.
You can count the subsets recursively, only keeping track of the sum and number of items in subsets. You don't need the actual subsets, only to know the sum and that there are at least two items in the subset.
The subsets in a set is the first item combined with all subsets in the rest of the set, plus the subsets in the rest of the set. For example the subsets s() of [1,2,3] is 1,s([2,3]) and s([2,3]).
This gives you:
public static int CountSubsets(int[] arr, int start, int len, int sum) {
int cnt = 0;
if (start < arr.Length) {
if (len >= 1 && arr.Contains(sum + arr[start])) cnt++;
cnt += CountSubsets(arr, start + 1, len + 1, sum + arr[start]);
cnt += CountSubsets(arr, start + 1, len, sum);
}
return cnt;
}
And calling it:
int[] set = { 1, 2, 3, 5, 8, 10, 15, 23 };
Console.WriteLine(CountSubsets(set, 0, 0, 0));
Output:
14
This seems a lot like homework to me. So I will answer in that spirit (i.e. rather than write the code, point you in the right direction).
First, it's not really clear what you mean by "subset". Are you talking about contiguous runs of elements from the array? Or do you literally mean treating the array as an unordered set, from which you examine every possible subset?
The latter is significantly harder than the former. So I'm going to assume the former for the moment.
Then, it seems you really have two different problems:
Find all subsets of the array and sum each one
For a given sum, determine whether it's in the array
The latter is fairly straightforward. If you know these arrays will always be relatively short (and hopefully they will be, otherwise "find all subsets" may take awhile :) ), you can just do a linear search every time you have a new sum to look for.
Alternatively, a more semantically straightforward approach would be to create a HashSet<int> instance once using the members of the array, and then when you want to know if the sum is in the array, just check your set.
For example:
HashSet<int> setOfValues = new HashSet<int>(array);
Then you can just write setOfValues.Contains(sum) to check whether the value held by the variable sum is contained in the array.
As for the first problem, it seems to me that you really should only need two loops:
A loop to iterate on the index of the first element of a subset. I.e. you need to enumerate all subsets, first starting with all subsets that start with element 0, then with all subsets that start with element 1, and so on.
A loop to iterate on the length of the subset. I.e. having determined the starting index for your current group of subsets, now you want to find all the actual subsets: the first has length 1, the second has length 2, and so on up to the maximum possible length (i.e. the length of the array minus the current 0-based starting index value).
Considering for a moment the alternative possibility — that you are treating the array as an unordered set — then it seems to me an obvious, if brute-force approach, would be to generate the subsets recursively.
In that approach, you would again have a loop to enumerate the subset lengths, starting with 1, up to the total length of the original set. Then, given a length, you need to pick all possible subsets.
You can do this recursively:
Given a set, iterate over the elements of the current set (passed in to your recursive method). The current element is the new element of your current subset.
If your current subset is now the correct length, sum it and compare to the original set.
Otherwise, remove the current element from the current set and recurse.
The easiest implementation of the above will create new copies of the current set for each level of recursion, to pass to the method when it calls itself. This way you don't have to worry about one level of recursion interfering with previous levels.
Do note that this is going to be practical only for relatively small initial sets. It won't take very large initial arrays before your computer doesn't have enough time or memory to complete the search of all possible subsets.
First up, you need a method that will return all subsets.
Func<IEnumerable<int>, IEnumerable<IEnumerable<int>>> getAllSubsets = null;
getAllSubsets = xs =>
(xs == null || !xs.Any())
? Enumerable.Empty<IEnumerable<int>>()
: xs.Skip(1).Any()
? getAllSubsets(xs.Skip(1))
.SelectMany(ys => new [] { ys, xs.Take(1).Concat(ys) })
: new [] { Enumerable.Empty<int>(), xs.Take(1) };
So given getAllSubsets(new[] { 1, 2, 3 }) I get:
{ }
{ 1 }
{ 2 }
{ 1, 2 }
{ 3 }
{ 1, 3 }
{ 2, 3 }
{ 1, 2, 3 }
Now's it's easy to compute the result you want.
int[] array = { 1,2,3,5,8,10,15,23};
var result =
getAllSubsets(array)
.Where(ss => array.Contains(ss.Sum()))
.Count();
I get 22.
Using a bit of Linq:
int[] array = {1, 2, 3, 5, 8, 10, 15, 23};
var subsets = new List<IEnumerable<int>>();
int counter = 0;
for (int i = 0; i < array.Length; i++)
{
for (int j = 2; j < array.Length - i; j++)
{
if (array.Contains(array.Skip(i).Take(j).ToList().Sum()))
{
counter++;
}
}
}
Console.WriteLine("Number of subsets:" + counter);
Gives you:
Number of subsets:5
Related
I am trying to write code that finds the lowest and highest values stored in an array and then removes them from the array to compute an average.
Currently I have written code to produce the average of all numbers in the array but I need to change that once I figure out how to remove Highest and lowest value.
Code I have:
private void HighAndLow()
{
try
{
int[] HighAndLowGrade;
int[] highest = HighAndLowGrade.Max();
int lowest = HighAndLowGrade.Min();
}
catch
{
MessageBox.Show("HighAndLow Method failed");
}
}
//find average without highest and lowest values
private void ComputeMean()
{
double total = 0;
for (int index = 2; index < 9; index ++)
{
total += double.Parse(lineContent[index]);
}
averageTestScore = total / 7;
}
This should work from what I have tested so far.
int[] numberArray = new int[] {1,2,5,9,5,2};
double answer = 0;
var ignoreList = new List<decimal>() {
numberArray.Max(),
numberArray.Min()
};
var cleanList = numberArray.Where(x => !ignoreList.Contains(x));
answer = cleanList.Any() ? cleanList.Average() : 0;
This only requires one iteration through the collection:
public double ComputeAdjustedMean(IEnumerable<int> items)
{
int total = 0;
int count = 0;
int min = int.MaxValue;
int max = int.MinValue;
foreach(int item in items)
{
count++;
total += item;
if (item < min) min = item;
if (item > max) max = item;
}
if (count <= 2) // not enough items
{
// do something here
}
return (total - (min + max)) / (double)(count - 2);
}
Try this using bubble sorting algorithm-
static void Main(string[] args)
{
int[] array = { 12, 6, 34, 23, 89 };
int temp;
for (int i = 0; i <= array.Length - 2; i++)
{
if (array[i] > array[i + 1])
{
temp = array[i];
array[i] = array[i + 1];
array[i + 1] = temp;
}
}
array = array.Skip(1).SkipLast(1).ToArray();
Console.WriteLine((array.Sum()) / (array.Length));
Console.Read();
}
If you have an array of values then you can do this neat LINQ query:
var average_skip_min_and_max =
values
.OrderBy(x => x)
.Skip(1)
.Take(values.Length - 2)
.Average();
I really don't get people when they encounter this kind of questions, they became insanely eager to provide a direct answer. This question is obviously a homework assignment. I'm not saying we don't help OPs but we need to lead them to solutions.
Dear OP,
Do not use the LINQ, yet. I think your instructor is meaning you to learn the sorting algorithms and memory operations. Do some research about them, say, Buble Sort, to sort the array you have. Then it'll be in front of you how to implement and use. After then, you should use the framework provided methods like LINQ's Min() / Max() extension methods.
The approach to your problem is could be like this:
Sort the array ascending.
Get the first element which is now the minimum valued element.
Reallocate a new array but 1 element shorter
Copy your entire array with the current ordered state to newly allocated array but skip the first element when copying, start with next element.
Get the minimum again, but this time search in the newly allocated array and check with the previous minimum
If they are equal go the 3rd operation, if you need to eliminate the repeating minimums ( [1, 1, 2, 3 ...] ), which I think you need to.
If they are not equal, then it means you've found the minimum element of your array and removed all occurences
Now if you repeat the approach to finding the maximum valued element you are done with the elimination process.
I solved a task on Hackerrank.com, where the problem was like this:
You have an Array. This Array contains numbers.
Now you enter two numbers:
The first one describes a sum
The second one describes the amount of indexes (sequence length) you add together
In the end you get the amount of sequences whose sum is your defined number
For example:
Your array is [ 1, 2, 3, 4], your sum is 3 and your sequence length is 2.
Now you take the first two indexes and output the sum: [1, 2] = 3.
This is equal to your sum, so now you have found one sequence.
The next sequence is [ 2, 3 ] = 5. This is not equal to 3, so your sequence counter stays 1.
The last sequence is [3, 4] = 7. This is also not equal to 3 and in the end, you found one sequence.
I wrote this code for that:
static int GetSequences(List<int> s, int d, int m)
{
//m = segment-length
//d = sum
int count = 0;
int j = 0;
int k = 0;
do
{
try
{
List<int> temp = new List<int>();
for (int i = 0; i < m; i++)
{
temp.Add(s[i + k]);
}
if (temp.Sum() == d)
{
count++;
}
j++;
k++;
}
catch (ArgumentOutOfRangeException)
{
break;
}
} while (true);
return count;
}
As I didn't know how often I have to count
(For example a 6-Length-Array with a sequence-length of 3 has 4 sequences (1,2,3 | 2,3,4 | 3,4,5 | 4,5,6)),
I am stopping the while loop when the index is out of range. but I'm not sure if this solution is okay. Not just with program speed, but also with code cleanliness. Is this code acceptable, or is it better to use a for loop, which loops for example exactly 4 times for a 6-length array with 3-Length sequences?
It's not recommended, no. Exceptions should be reserved for stuff that isn't supposed to happen, not flow control or validation.
What you want is to use conditional logic (if statements) and the break keyword.
Also, codereview.stackexchange.com is better suited for these kinds of questions.
It would be better to fix your code so that it doesn't routinely throw exceptions:
You sum each of these segments:
0 1 2 3 start = 0
| | summing indexes: 0, 1
+--+
0 1 2 3 start = 1
| | summing indexes: 1, 2
+--+
0 1 2 3 start = 2
| | summing indexes: 2, 3
+--+
The bracket starts at the index start, and has a size of m. The length of s is given by s.Count. Therefore we want to keep going until start + m == s.Count.
(I always find it's useful to draw these things out, and put sample numbers in, in order to make sure you've got the maths right. In the sample above, you can see that we stop when start (2) + m (2) == the array size (4))
static int GetSequences(List<int> s, int d, int m)
{
//m = segment-length
//d = sum
int count = 0;
for (int start = 0; start + m <= s.Count; start++)
{
List<int> temp = new List<int>();
for (int i = 0; i < m; i++)
{
temp.Add(s[start + i]);
}
if (temp.Sum() == d)
{
count++;
}
}
return count;
}
However, you can improve your code a bit:
Use meaningful variable names
Don't create a new temporary list each time, just to sum it
Check your inputs
static int GetSequences(List<int> numbers, int targetSum, int segmentLength)
{
if (numbers == null)
throw new ArgumentNullException(nameof(numbers));
if (segmentLength > numbers.Count)
throw new ArgumentException("segmentLength must be <= numbers.Count");
int count = 0;
for (int start = 0; start + segmentLength <= numbers.Count; start++)
{
int sum = 0;
for (int i = 0; i < segmentLength; i++)
{
sum += numbers[start + i];
}
if (sum == targetSum)
{
count++;
}
}
}
Usually except for switch/case there is often no real reason to use break.
Also an exception MUST be as the name says exceptional, so it MUST NOT be a part of your logic.
As said Jeppe you can use the methods and attributes the framework provides you to do as you like.
Here s.Count seems to be the way to go.
int[] arr = new[] { 1, 2, 1, 2 };
// Sum and len are given by the task.
// 'last' is the last index where we should stop iterating.
int sum = 3, len = 2, last = arr.Length - len;
// One of the overloads of Where accepts index, i.e. the position of element.
// 1) We check that we don't go after our stop-index (last).
// 2) Avoid exception by using '&&'.
// 3) We use C# 8 Range (..) to get the slice of the numbers we need:
// we start from the current position (index) till then index,
// calculated as current index + length given by the task.
// 4) Sum all the numbers in the slice (Sum()) and compare it with the target sum,
// given by the task (== sum).
// 5) The count of all matches (Count()) is the sought amount of sequences.
int count = arr.Where((z, index) => index <= last && arr[index..(index+len)].Sum() == sum).Count();
I tried a demo test on Codility for finding the equilibrium index(es) of an array. I'm not sure if the test was to find the equilibrium indexes of an array or what. I googled around and found the following example:
The equilibrium index of a sequence is an index such that the sum of elements at lower indexes is equal to the sum of elements at higher indexes. For example, in a sequence A:
A[0]=-7 A[1]=1 A[2]=5 A[3]=2 A[4]=-4 A[5]=3 A[6]=0
3 is an equilibrium index, because:
A[0] + A[1] + A[2] = A[4] + A[5] +A[6]
6 is also an equilibrium index, because:
A[0] + A[1] + A[2] + A[3] + A[4] + A[5] = 0
Based on this information I see the test array contains 7 elements. It looks like the middle element A[3]=2 is being ignored. Is it because it is between the first 3 elements and the last 3 elements? How is the equilibrium index of 6 arrived at in this example?
Here is the method that was used to compute this:
int equi(int arr[], int n) {
if (n==0) return -1;
long long sum = 0;
int i;
for(i=0;i<n;i++) sum+=(long long) arr[i];
long long sum_left = 0;
for(i=0;i<n;i++) {
long long sum_right = sum - sum_left - (long long) arr[i];
if (sum_left == sum_right) return i;
sum_left += (long long) arr[i];
}
return -1;
}
When I took the Codility demo test I used the method (below) with the for loops initially beginning at 0. I received a "wrong answer" message for the test case of 1, 5, 2, 1, 4, 0 and that Codility was looking for a result of 11.
I modified the two for loops in my method and started the first loop at i = 1 and the second loop at i = 2 until it yielded a result of 11, which Codility was satisfied with. I basically just tweaked the method until Codility was happy (I was shooting for 11 because Codility specified that was the answer they were looking for), but I don't really know why Codility was happy or what was the significance of my tweaks -- just hit and miss:
...
int[] B = new int[] { 1, 5, 2, 1, 4, 0 };
Console.WriteLine(solution3(B));
}
public int solution3(int[] A)
{
long rsum = 0;
for (int i = 1; i < A.Count(); i++)
rsum += A[i];
long lsum = A[0];
int min = (int)Math.Abs(lsum - rsum);
for (int i = 2; i < A.Count() - 1; i++)
{
lsum += A[i];
rsum -= A[i];
int diff = (int)Math.Abs(lsum - rsum);
if (diff >= min)
min = diff;
}
return min;
}
Why did this tweak (of hit and miss) satisfy the Codility test? What is an equilibrium index actually? How is it arrived at? Note: if there are steps between step A and step E (which I would have to intuit the in between steps) what are steps B, C, and D?
What is an equilibrium index and how is it determined?
Think of your array like a board with weights of different sizes on it (negative weights can be thought of as helium balloons attached to the board). Each weight is numbered from left to right. Your task is to place a fulcrum under the board at any of the numbered positions to make the board balance. (We will treat the board itself as weightless, and assume distance of the weights from the center doesn't matter.) Whichever positions make the board balance are the equilibrium indexes. The weight at the fulcrum doesn't "count" because it's not on either side; it is in the middle.
Here is a picture of the first example:
Here, 3 is an equilibrium index, because the sum of the weights to the left of the fulcrum equal the sum of the weights to the right of the fulcrum (-7 + 5 + 1 = -1 = -4 + 3 + 0).
Here is the second example:
We see that 6 is also an equilibrium index for the same reason. All the elements to the left of the fulcrum add up to zero. As there are no elements to the right of the fulcrum, that sum is also zero.
The basic algorithm to find the equilibrium indexes is this:
Loop over the array and add up all the elements. Call this sum total.
Initialize a variable left_sum to zero.
Initialize a variable right_sum to total.
Now loop over the array a second time. For each array item:
Subtract the value of the current item from right_sum.
Compare the left_sum to the right_sum. If they are equal, then the index of the current item is an equilibrium index.
Add the value of the current item to left_sum.
And that's all there is to it. Does it make sense now?
What's going on with my algorithm?
As for why the "Codility demo test" was expecting a result of 11 for an array containing the elements { 1, 5, 2, 1, 4, 0 }, I cannot say. You never said in your question what the demo problem actually was. If the problem was to find the first equilibrium index in that array, then the answer is that there is none for that array. Stepping through the above algorithm, here are the left and right sums for each array index:
Index Left Right
----- ---- -----
0 0 12
1 1 7
2 6 5
3 8 4
4 9 0
5 13 0
As you can see, there is no index at which the left sum equals the right sum. An expected result of 11 does not even make sense anyway because there are only six elements. So if there were an equilibrium index for this array, it would have to between 0 and 5 inclusive. So I'm guessing the problem posed must have been something else. You would have to include the problem statement in your question before I could even begin to guess why your algorithm is or isn't correct.
Well, based on what I understand about an equilibrium index of an array I came up with the following algorithm which I tested on another array which has equilibrium indexes as index 3 and index 6, but I also tested this algorithm on the original array which returned a -1 (nothing to do with an 11). Here is the sample:
private void button6_Click_1(object sender, EventArgs e)
{
//int[] arr = new int[7] { -7, 1, 5, 2, -4, 3, 0 }; //--new array
int[] arr = new int[6] { 1, 5, 2, 1, 4, 0 }; //--original array
List<int> lst = new List<int>();
int p = arr.Length;
int i = 0;
int q = 0;
int t = 0;
do
{
t = equilibrium(arr, arr.Length, q);
if (lst.IndexOf(t) == -1)
lst.Add(t);
q = t;
i++;
} while (i < p);
foreach (var m in lst)
Console.WriteLine(m);
}
private int equilibrium( int[] arr, int n, int q1)
{
int i, j;
int leftsum, rightsum;
for (i = 0; i < n; ++i)
{
leftsum = 0;
rightsum = 0;
for (j = 0; j < i; j++)
leftsum += arr[j];
for (j = i + 1; j < n; j++)
rightsum += arr[j];
if (leftsum == rightsum && q1 != i)
return i;
}
return -1;
}
This algorithm makes sense to me, but when I tried it on Codility with an array of int[] arr = new int[] { 2, 2, 1, 0, 1 }; I got a "wrong Answer -- expected 3" In my test app my algorithm returned a 1 which is what it returned on Codility. Where did they come up with 3 for this array? Thus, I do not understand the equilibrium index of an array. Any suggestions appreciated.
I want a faster function to find the Nth biggest number of an Int array in C#. This function takes N and Array and returns index of that number.
Here is what i have already. It simply sorts the array and then returns the index of that number. It works perfectly but I'm not sure if this is the fastest way. it seems logical to be an algorithm without complete sorting.
static int myFunction(int[] array, int N){
int[] indexes = new int[array.Length];
for (int i = 0; i < indexes.Length; i++)
indexes[i] = i;
for (int i = 0; i < array.Length; i++)
{
for (int j = i + 1; j < array.Length; j++)
{
if (array[i] < array[j])
{
int m = array[j];
array[j] = array[i];
array[i] = m;
m = indexes[j];
indexes[j] = indexes[i];
indexes[i] = m;
}
}
}
return indexes[N];
}
some results :
myFunction(new int[] { 1, 3, 2, 0, 10 }, 0); //returns 4 (index of 10)
myFunction(new int[] { 1, 3, 2, 0, 10 }, 1); //returns 1 (index of 3)
myFunction(new int[] { 1, 3, 2, 0, 10 }, 2); //returns 2 (index of 2)
Randomized quickselect algorithm works in average case complexity O(n). Practically it's very rare to be O(n^2). It uses quicksort's partition function
If your array has a size of a zillion numbers and you need the fifth largest number then you are sorting a lot of numbers that you won't need.
Wouldn't it be faster to keep an ascending sorted sequence of length n (linked list?), and for every element check if it is larger than the first one (which is the smallest in the ascending order
If smaller: skip to the next element in your large array
If larger: remove the smallest one from your sorted array which is the first element and insert the larger element in the proper place, keep the array sorted.
After having scanned your complete array, the first element in your sorted sequence is the one you are looking for.
Most comparisons are only with the first element of your sorted array. You'll have to change the array N-times, one time for the N largest numbers. A change of the array is to remove the first element (the smallest) and find the place where to insert the new element to keep the array sorted
Correction: my statement that the array has to be changed N-time is incorrect. This can be seen most easily when offering an array sorted in ascending order: every compared number will be larger than the smallest in the N-size array, and thus cause a replace
This would be the implementation of #HaraldDutch's answer.
int get(int[] array, int n)
{
var comparer = Comparer<int>.Create((x, y) => array[x].CompareTo(array[y])); //compare the array entries, not the indices
var highestIndices = new SortedSet<int>(comparer);
for (var i = 0; i < array.Length; i++)
{
var entry = array[i];
if (highestIndices.Count < n) highestIndices.Add(i);
else if (array[highestIndices.Min] < entry)
{
highestIndices.Remove(highestIndices.Min);
highestIndices.Add(i);
}
}
return highestIndices.Min;
}
You'd have to pass in 1 instead of 0 though.
you need to use Selection algorithm
https://en.wikipedia.org/wiki/Selection_algorithm
here nice slides: https://c3p0demo.googlecode.com/svn/trunk/scalaDemo/script/Order_statistics.ppt
generally algorithm:
Select(A,n,i):
Divide input into ⌈n/5⌉ groups of size 5.
/* Partition on median-of-medians */
medians = array of each group’s median.
pivot = Select(medians, ⌈n/5⌉, ⌈n/10⌉)
Left Array L and Right Array G = partition(A, pivot)
/* Find ith element in L, pivot, or G */
k = |L| + 1
If i = k, return pivot
If i < k, return Select(L, k-1, i)
If i > k, return Select(G, n-k, i-k)
You could create a heap of size N which has the largest number as its first element (as opposed to the smallest one usually given). Then you walk through your integer array, and whenever you have an element smaller than the largest member of the heap, you insert it into the heap. If that makes the heap exceed a size of N, you remove the largest member in it.
That should be one of the cheapest ways to do this. Specific "nth largest of m" algorithms may beat it, but probably not asymptotically.
Your sorting algorithm is by far not the fastest. You should google for "Quicksort" for a much, much faster algorithm.
And after you have implemented Quicksort, you would then think about whether you really needed to sort the complete array. Say you want to find the 20 largest out of 10,000 numbers, why would you sort the remaining 9,980 numbers? You can easily modify Quicksort so that it will find the N largest numbers but mostly ignore the rest.
Maybe this could help someone. finding the nth largest number in an int array.
int[] arr = new int[] { 3, 2, 1, 5 };
Array.Sort(arr);
int elemCount = 0;
int? thirdLargestNumber = null;
foreach (var elem in arr)
{
var temp = arr.Skip(elemCount).ToArray();
if (temp.Length == 3) //replace `3` with variable.
{
thirdLargestNumber = temp[0];
break;
}
elemCount++;
}
Console.WriteLine($"Third largest number = {thirdLargestNumber}");
I have an integer array int[] number = { 3,4,2,5,1};
The minimum number of steps to sort it should be 2. But I am getting 4.
static void Main(string[] args)
{
int[] number = { 3,4,2,5,1};
int result = get_order(number);
Console.ReadKey();
}
public static int get_order(int[] input1)
{
input1 = input1.OrderByDescending(o => o).ToArray();
bool flag = true;
int temp;
int numLength = input1.Length;
int passes = 0;
for (int i = 1; (i <= (numLength - 1)) && flag; i++)
{
flag = false;
for (int j = 0; j < (numLength - 1); j++)
{
if (input1[j + 1] > input1[j])
{
temp = input1[j];
input1[j] = input1[j + 1];
input1[j + 1] = temp;
flag = true;
}
}
passes++;
}
return passes+1;
}
What is the problem and what changes i need to do in my code?
Edit
implement #Patashu, algorithm,
public static int get_order(int[] input1)
{
var sorterArray = input1.OrderByDescending(o => o).ToArray();
var unsortedArray = input1;
int temp1;
int swap = 0;
int arrayLength = sorterArray.Length;
for (int i = 0; i < arrayLength; i++)
{
if (sorterArray[i] != unsortedArray[i])
{
temp1 = unsortedArray[i];
unsortedArray[i] = sorterArray[i];
for (int j = i + 1; j < arrayLength; j++)
{
if (unsortedArray[j] == sorterArray[i])
{
unsortedArray[j] = temp1;
swap++;
break;
}
}
}
}
return swap;
}
The problem with your algorithm is that it only attempts swapping adjacent elements.
3,4,2,5,1 is best sorted by swapping 3 with 5, which is an unadjacent swap, and then 2 with 3.
So, I suggest that you will find a better algorithm by doing the following:
1) First, sort the array into descending order using the built in sorting function of C#.
2) Now, you can use this sorted array as a comparison - iterate through the array from left to right. Every time you see an element in the unsorted array that is != to the element in the same space in the sorted array, look deeper into the unsorted array for the value the sorted array has there, and do one swap.
e.g.
3,4,2,5,1
Sort using Sort -> 5,4,3,2,1 is our sorted array
3 is != 5 - look in unsorted array for 5 - found it, swap them.
Unsorted is now 5,4,2,3,1
4 == 4
2 is != 3 - look in unsorted array for 3 - found it, swap them.
Unsorted is now 5,4,3,2,1
2 == 2
1 == 1
We're at the end of the unsorted array and we did two swaps.
EDIT: In your algorithm implementation, it looks almost right except
instead of
unsortedArray[j] = sorterArray[i];
unsortedArray[i] = temp1;
you had it backwards, you want
unsortedArray[j] = temp1;
unsortedArray[i] = sorterArray[i];
Since you're asking why you're getting 4 steps, and not how to calculate the passes, the correct way to do this is to simply step through your code. In your case the code is simple enough to step through on a piece of paper, in the debugger, or with added debug statements.
Original: 3, 4, 2, 5, 1
Pass: 1: 4, 3, 5, 2, 1
Pass: 2: 4, 5, 3, 2, 1
Pass: 3: 5, 4, 3, 2, 1
Pass: 4: 5, 4, 3, 2, 1
Basically what you see is that each iteration you sort one number into the correct position. At the end of pass one 2 is in the correct position. Then 3, 4, 5.
Ah! But this is only 3 passes you say. But you're actually incrementing passes regardless of flag, which shows you that you actually did one extra step where the array is sorted (in reverse order) but you didn't know this so you had to go through and double check (this was pass 4).
To improve performance, you do not need to start checking the array from the beginning.
Better than the last equal element.
static int MinimumSwaps(int[] arr)
{
int result = 0;
int temp;
int counter = 0;
for (int i = 0; i < arr.Length; ++i)
{
if (arr[i] - 1 == i)
{
//once all sorted then
if(counter==arr.Length)break;
counter++;
continue;
}
temp = arr[arr[i]-1];
arr[arr[i] - 1] = arr[i];
arr[i] = temp;
result++;//swapped
i = counter ;//needs to start from the last equal element
}
return result;
}
At the start:
{ 3,4,2,5,1}; // passes = 0
Round 1 reuslt:
{ 4,3,2,5,1};
{ 4,3,5,2,1}; // passes = 1
Round 2 reuslt:
{ 4,5,3,2,1}; // passes = 2
Round 3 reuslt:
{ 5,4,3,2,1}; // passes = 3 and flag is set to true
Round 4 reuslt:
{ 5,4,3,2,1}; // same result and passes is incremented to be 4
You fail to mention that the array is supposed to be sorted in descending order, which is usually not the default expected behavior (at least in "C" / C++). To turn:
3, 4, 2, 5, 1
into:
1, 2, 3, 4, 5
one indeed needs 4 (non-adjacent) swaps. However, to turn it into:
5, 4, 3, 2, 1
only two swaps suffice. The following algorithm finds the number of swaps in O(m) of swap operations where m is number of swaps, which is always strictly less than the number of items in the array, n (alternately the complexity is O(m + n) of loop iterations):
int n = 5;
size_t P[] = {3, 4, 2, 5, 1};
for(int i = 0; i < n; ++ i)
-- P[i];
// need zero-based indices (yours are 1-based)
for(int i = 0; i < n; ++ i)
P[i] = 4 - P[i];
// reverse order?
size_t count = 0;
for(int i = 0; i < n; ++ i) {
for(; P[i] != i; ++ count) // could be permuted multiple times
std::swap(P[P[i]], P[i]); // look where the number at hand should be
}
// count number of permutations
This indeed finds two swaps. Note that the permutation is destroyed in the process.
The test case for this algorithm can be found here (tested with Visual Studio 2008).
Here is the solution for your question :)
static int MinimumSwaps(int[] arr)
{
int result = 0;
int temp;
int counter = 0;
for (int i = 0; i < arr.Length; ++i)
{
if (arr[i] - 1 == i)
{
//once all sorted then
if(counter==arr.Length)break;
counter++;
continue;
}
temp = arr[arr[i]-1];
arr[arr[i] - 1] = arr[i];
arr[i] = temp;
result++;//swapped
i = 0;//needs to start from the beginning after every swap
counter = 0;//clearing the sorted array counter
}
return result;
}