Array.Sort in with nontrivial comparison function - c#

Consider the following code from C# 5.0 in a Nutshell, p. 289:
int[] numbers = { 1, 2, 3, 4, 5 };
Array.Sort (numbers, (x, y) => x % 2 == y % 2 ? 0 : x % 2 == 1 ? -1 : 1);
which gives result {3, 5, 1, 2, 4}.
I tried this on a paper and got {1, 3, 5, 2, 4}.
Why computer sorting gave 3 > 5 > 1 ?

Most likely the topic is about the fact that Sort does not guarantee the order of elements which are equal. Unlike stable sort algorithms that preserve original order of equal elements "unstable sort" may swap them. Normally when you do sorting by hand you do "stable sort" version.
Array.Sort:
This implementation performs an unstable sort; that is, if two elements are equal, their order might not be preserved. In contrast, a stable sort preserves the order of elements that are equal.
The sort function used in sample makes 1 == 3, 1 == 5 so unstable sort is allowed to order this numbers in any way as long as they are in correct order compared to other ones: 1,3,5 (stable - same order as in source) or any sequence 3,1,5 (unstable sort).
I.e. OrderBy implements "stable sort" and you can see results in following sample using the same compare function:
void Main()
{
int[] numbers = { 1, 2, 3, 4, 5 };
var result = numbers.OrderBy(x=> x, new MyComparer()));
// 1, 3, 5, 2, 4
}
public class MyComparer : IComparer<int>
{
public int Compare( int x, int y)
{
return x % 2 == y % 2 ? 0 : x % 2 == 1 ? -1 : 1;
}
}

Although Array.Sort specifies
If the partition size is fewer than 16 elements, it uses an insertion sort algorithm.
it does not specifiy how it does this insertion sort or which flavor of insertion sort it uses. As already mentioned, it additionally specifies
This implementation performs an unstable sort
and as a result, the only thing Array.Sort promises about the order of the elements after returning is that they are sorted. This is true for {3, 5, 1, 2, 4}.
Consider that the algorithm used by Array.Sort would even be allowed to do something like this (Pseudocode):
if sequence = {1, 2, 3, 4, 5} then
sequence := {3, 5, 1, 2, 4}
end if
Sort(sequence);
This, of course, would be implementation defined behavior, and it could change in another version of the .NET framework.
Modifying your code to be
Array.Sort(numbers, (x, y) =>
{
Console.WriteLine(x + ", " + y);
return x % 2 == y % 2 ? 0 : x % 2 == 1 ? -1 : 1;
});
will give you the comparisons that are done by Array.Sort:
1, 3
1, 5
3, 5
1, 3
3, 5
2, 3
3, 4
3, 3
5, 3
5, 3
5, 5
5, 3
2, 4
2, 1
4, 2
1, 4
4, 4
4, 2
1, 2
1, 2
1, 1
1, 2
1, 1
And this, very likely, is not how you would do an insertion sort on paper.
The point is: Array.Sort promises to sort your sequence, but it does not promise how to do this.

That code is equivalent to:
static void Main(string[] args)
{
int[] numbers = { 1, 2, 3, 4, 5 };
Array.Sort(numbers, OnComparison);
}
private static int OnComparison(int x, int y)
{
if (x%2 == y%2) return 0;
if (x%2 == 1) return 1;
return -1;
}
I get:
{3, 5, 1, 2, 4}
The sorting operation goes like this:
0) {1,2,3,4,5}
1) {1,2,3,4,5}
2) {1,2,3,4,5}
3) {1,2,3,4,5}
4) {1,2,3,4,5}
5) {5,2,3,4,1}
6) {5,2,3,4,1}
7) {5,2,3,4,1}
8) {5,3,2,4,1}
9) {5,3,2,4,1}
10) {5,3,2,4,1}
11) {5,3,2,4,1}
12) {3,5,2,4,1}
13) {3,5,2,4,1}
14) {3,5,1,4,2}
15) {3,5,1,4,2}
16) {3,5,1,4,2}
17) {3,5,1,4,2}
18) {3,5,1,2,4}
19) {3,5,1,2,4}
20) {3,5,1,2,4}
21) {3,5,1,2,4}
22) {3,5,1,2,4}
23) Final: {3,5,1,2,4}
So in conclusion, it seems the "why" is because the 0 problem as everybody says, but I am not completelly sure yet.

Here you are not sort equal remainders how you got in order
try this:
Array.Sort(numbers, (x, y) => x % 2 == y % 2 ? x < y ? 1 : -1 : x % 2 == 1 ? -1 : 1);

here are how i understand it ,
base on the code, you can change it as :
static void Main(string[] args)
{
int[] numbers = { 1, 2, 3, 4, 5 };
Array.Sort(numbers, OnComparison);
}
private static int OnComparison(int x, int y)
{
if (x%2 == y%2) return 0;
if (x%2 == 1) return -1;
return 1;
}
so arrary.sort, is sorting on condition (OnComparison,which by the return value), not compare with int[] numbers.
so 3 & 5 & 1, both return as -1
and as the definition of array.sort :
This implementation performs an unstable sort; that is, if two elements are equal, their order might not be preserved
that why , you got {3, 5, 1, 2, 4}

Related

using where to get every other number starting with the last

I have an array of numbers {3, 6, 1, 5, 5, 6} I am trying to get every other number starting with the last number.
The correct result would then be 6, 5, 6 the only way I have been able to get this to work is to use Reverse.
int[] digitList = {3, 6, 1, 5, 5,6};
var rev = digitList.Reverse().Where((x, i) => i % 2 == 0).Reverse().ToList();
// Correct results in 6,5,6
var l = digitList.Where((x, i) => i % 2 == 0).ToList();
// Incorrect results in 3,1,5
Is there a way of doing this without the Reverse? How can i tell Where() to start at the other end?
If the count is odd, then every other number from the start, if it's even then take every other number from the second one (or skip the first), that removes the need for a reverse operation. For example:
int[] digitList = { 3, 6, 1, 5, 5, 6 };
//Skip 1 if count is odd, otherwise skip zero
var skipCount = digitList.Count() % 2 == 0 ? 1 : 0;
var l = digitList
.Skip(skipCount)
.Where((x, i) => i % 2 == 0)
.ToList();
You have to check for odd/even length arrays; to amend your current code you
should change the == 0 condition:
int[] digitList = { 3, 6, 1, 5, 5, 6, 7, 8 };
var rev = digitList
.Where((x, i) => i % 2 != digitList.Length % 2) // not "== 0"
.ToList();

Sort int array using linq?

PROBLEM
Write a program to sort any given integer array of positive and negative numbers such that positive numbers follow negative numbers, however relative position of positive and negative numbers remains same. Like in the example below (1), (-7) occurs after (-5) hence in sorted array (-7) follows (-5).
The program should iterate given array only once and should not use temporary array.
Example 1.
Given Array: { 2, 4, -5, 0, -7, -2, 2, -5, 1, -9, -7, -1 }
Sorted Array: { -5, -7, -2, -5, -9, -7, -1, 2, 4, 0, 2, 1 }
Example 2.
Given Array: { -3, 7, 0, -2, -1, 3, -3, 9, 5, -5, 8}
Sorted Array: { -3, -2, -1, -3, -5, 7, 0, 3, 9, 5 , 8}
MY SOLUTION
class Program
{
public static void Main(string[] args)
{
var intArray = new int[] { 2, 4, -5, 0, -7, -2, 2, -5, 1, -9, -7, -1 };
var sorted_intArray = SortIntArray(intArray);
Console.WriteLine(String.Join(",", sorted_intArray));
intArray = new int[] { -3, -2, -1, -3, -5, 7, 0, 3, 9, 5, 8 };
sorted_intArray = SortIntArray(intArray);
Console.WriteLine(String.Join(",", sorted_intArray));
Console.ReadLine();
}
private static int[] SortIntArray(int[] intArray)
{
var sorted_intArray = intArray.GroupBy(_int => _int < 0 ? -1 : 1)// Create group of +ve and -ve numbers
.OrderBy(group => group.Key) // Bring group of -ve number first
.SelectMany(groupedItems => groupedItems).ToArray(); // Select num from both Group and convert to array
return sorted_intArray;
}
}
QUESTION
i) The problem statement says , numbers should be iterated only once and temporary array should not be used. I believe my solution using linq do not meet this requirement. How to solve mentioned problem with linq , if possible?
ii) What could be other possible and efficient ways to solve mentioned problem with or without linq? Solution using C/C++ is also fine.
According to this OrderBy is stable so:
enumerable.OrderBy(x => x >= 0);
I like these one:
For Order By Ascending
enumerable.OrderBy(x => x);
For Order By Descending
enumerable.OrderByDescending(x => x);
Linq is amazing:
private static int[] SortIntArrayWithDuplicates(IEnumerable<int> intArray)
{
var enumerable = intArray as int[] ?? intArray.ToArray();
return enumerable.Where(x => x < 0)
.Concat(enumerable.Where(x => x >= 0))
.ToArray();
}
private static int[] SortIntArrayWithoutDuplicates(IEnumerable<int> intArray)
{
var enumerable = intArray as int[] ?? intArray.ToArray();
return enumerable.Where(x => x < 0)
.Union(enumerable.Where(x => x >= 0))
.ToArray();
}
You don't need grouping at all. Linq-to-objects is "stable" (meainig it keeps the original order for "duplicate" objects) when using OrderBy, so you can just order on whether or not the number is less than zero:
private static int[] SortIntArray(int[] intArray)
{
var sorted_intArray = intArray.OrderBy(i => i < 0 ? 0 : 1).ToArray();
return sorted_intArray;
}
Output:
-5,-7,-2,-5,-9,-7,-1,2,4,0,2,1
-3,-2,-1,-3,-5,7,0,3,9,5,8
Note that if you change your return type to IEnumerable<int> then you can avoid the ToArray call and reduce your memory usage.
Whether or not you count a Linq ordering as "iterated once" is up to whoever gave you that requirement. I don't know of any sorting algorithm that can be done in one pass.
What could be other possible and efficient ways to solve mentioned problem with or without linq?
Well I can see how you'd do it in two passes by scanning all numbers, taking negative ones first, then scan again, taking all non-negative ones. You can simplify this with yield syntax:
private static IEnumerable<int> SortIntArray(int[] intArray)
{
foreach(var i in intArray)
if(i < 0) yield return i;
foreach(var i in intArray)
if(i >= 0) yield return i;
}

How to find all instances of mirrored duplicates?

I have been searching for some time now for any answers on how to do this.
What I am trying to do is, take an array of numbers, e.g. {1, 3, 5, 6, 8, 7, 6 ,5, 3, 1} (but it will use user input) and find the duplicates of these numbers that are mirrored and return how many indexes are involved in just one instance of said array.
I know the basics of C# but can't grasp this task. No, this is not homework. This is my own project to further my knowledge.
I am not currently around the code I have for parts of this, but would really appreciate any help/advice anyone could give me.
int[] array = {1, 3, 5, 6, 8, 7, 6 ,5, 3, 1};
//holds left index of mirrored pair, you can easily find the right one
var mirroredIndexes = new List<int>();
var length = array.Length;
for (int i = 0; i < length / 2; i++)
{
if(array[i] == array[length - i - 1])
mirroredIndexes.Add(i);
}
mirroredIndexes.ForEach(Console.WriteLine);
Console.WriteLine ("total of {0} mirrored pairs ({1})",
mirroredIndexes.Count,
string.Join(", ", mirroredIndexes.Select(i => array[i])));
prints next indexes:
0
1
2
3
total of 4 mirrored pairs (1, 3, 5, 6)
I think this is what you are after. This will return a list of matching indices.
Eg. first == last, second == second to last, third == third to last
var matches = new List<Tuple<int, int>>();
var array = new [] { 0, 1, 2, 3, 4, 5, 3, 2, 1, 0 };
if (array.Length % 2 != 0)
throw new Exception("Array must have an even amount of elements");
for (int i = 0; i < array.Length / 2; i++)
{
if (array[i] == array[array.Length - 1 - i])
{
matches.Add(new Tuple<int, int>(i, array.Length - 1 - i));
}
}
var firstMatchingIndex1 = matches[0].Item1;
// This will be 0
var firstMatchingIndex2 = matches[0].Item2;
// This will be 9
You could go further, using a custom class, and capture the actual value that matched (eg. index1 is 1, index2 is 8 and the value was 1.

reorder List starting at given position

List:
List<int> list1 = new List<int>(){ 0, 1, 2, 3, 4, 5, 6 };
let's say we want to reorder it. The beginning should be at number "2"
// 2,3,4,5,6,0,1
or at number 5
// 5,6,0,1,2,3,4
how do you do it with C#?
the reason: Imagine that you have an index of a given number in the List (number 3, index 3). You want to get the second number from the right - it'll be 5.
Unfortunately, if the starting number is at the end of the List (numbers 5 and 6) - out of range exception will be thrown, because there's no 7 and 8!
The idea is to reorder the List!
We enter Nr. 5 - we get 0 (5,6,0).
We enter Nr. 6 - we get 1 (6,0,1), etc.
or maybe there is some other (read - better) way to solve this problem?
The better way to do it is to use the mod operator %. This gives you the remainder when you divide an int by another int. The way this works is something like this:
int nextIndex = (currentIndex + offset) % length;
So, if your current index is 5, your offset is 2 and your length is 6 then:
5 + 2 = 7
7 / 6 = 1 remainder 1 (or 7 mod 6 = 1)
therefore nextIndex = 1
A little Linq can do this pretty easily:
List<int> list1 = new List<int>(new[] { 0, 1, 2, 3, 4, 5, 6 });
var numToStart = 4;
//reorderedList will be {4,5,6,0,1,2,3}
var reorderedList = list1.Skip(numToStart).Concat(list1.Take(numToStart));
You don't need to reorder the list. You could get the number with the following function:
int GetNumber(List<int> list, int fromValue, int index)
{
return list[(list.IndexOf(fromValue) + index) % list.Count()];
}
You could call the function like this:
List<int> list1 = new List<int>(new[] { 0, 1, 2, 3, 4, 5, 6 });
int number = GetNumber(list1, 5, 2); // number = 0

How to get alternate numbers using Enumerable.Range?

If Start=0 and Count=10 then how to get the alternate values using Enumerable.Range()
the out put should be like { 0, 2, 4, 6, 8 }
and if Start=1 and Count=10 then { 1, 3, 5, 7, 9 }
The continuous value can be get like
var a = Enumerable.Range(0,10).ToList();
but how to get the alternate values?
Halving the number of items that Range should generate (its second parameter) and then doubling the resulting values will give both the correct number of items and ensure an increment of 2.
Enumerable.Range(0,5).Select(x => x * 2)
Enumerable.Range(0, 10).Where(i => i % 2 == 0); // { 0, 2, 4, 6, 8 }
Enumerable.Range(0, 10).Where(i => i % 2 != 0); // { 1, 3, 5, 7, 9 }
The count parameter in your code looks like an end point of the loop.
public static MyExt
{
public static IEnumerable<int> Range(int start, int end, Func<int, int> step)
{
//check parameters
while (start <= end)
{
yield return start;
start = step(start);
}
}
}
Usage: MyExt.Range(1, 10, x => x + 2) returns numbers between 1 to 10 with step 2
MyExt.Range(2, 1000, x => x * 2) returns numbers between 2 to 1000 with multiply 2 each time.
What you are after here does not exist in the BCL as far as I'm aware of, so you have to create your own static class like this to achieve the required functionality:
public static class MyEnumerable {
public static IEnumerable<int> AlternateRange(int start, int count) {
for (int i = start; i < start + count; i += 2) {
yield return i;
}
}
}
Then you can use it like this wherever you want to:
foreach (int i in MyEnumerable.AlternateRange(0, 10)) {
//your logic here
}
You can then also perform LINQ queries using this since it returns IEnumerable
So if you want you can also write the above like this if you want to exclude the number 6
foreach (int i in MyEnumerable.AlternateRange(0, 10).Where( j => j != 6)) {
//your logic here
}
I hope this is what you are after.
You can't have this as an extension method on the Enumerable class directly since that is a static class, and extension methods work on an object of a class, and not the class itself. That's why you have to create a new static class to hold this method if you want to mimic the Enumerable class.
This can be done more simply using Linq and by specifying the minimum, length, and step values:
Enumerable.Range(min, length).Where(i => (i - min) % step == 0);
Usage with 0 through 10 at a step size of 2:
var result = Enumerable.Range(0, 10).Where(i => (i - 10) % 2 == 0);
Output:
0, 2, 4, 6, 8
Usage with 1 through 10 at a step size of 2:
var result = Enumerable.Range(1, 10).Where(i => (i - 10) % 2 == 0);
Output:
1, 3, 5, 7, 9
You could go further and make a simple function to output it using a minimum, maximum, and step value:
public static IEnumerable<int> RangedEnumeration(int min, int max, int step)
{
return Enumerable.Range(min, max - min + 1).Where(i => (i - min) % step == 0);
}
The reason to set the range length to max - min + 1 is to ensure the max value is inclusive. If the max should be exclusive, remove the + 1.
Usage:
var Result = RangedEnumeration(0, 10, 2); // 0, 2, 4, 6, 8, 10
var Result = RangedEnumeration(1, 10, 2); // 1, 3, 5, 7, 9
var Result = RangedEnumeration(1000, 1500, 150); // 1000, 1150, 1300, 1450

Categories

Resources