Why is the stopwatch not timing my method correctly? - c#

I am trying to test the performance difference of my quicksort and mergesort methods, but for some reason the first cycle of the loop always show exactly 0.003 milliseconds and the rest 0 milliseconds.
public static void RunDiagnostics(int[] rangeOfLengthsToTest, int numOfTestsPerLength)
{
Stopwatch stopwatch = new Stopwatch();
int[] array;
double totalQuicksortTime, totalMergesortTime;
for (int i = rangeOfLengthsToTest[0]; i <= rangeOfLengthsToTest[1]; i++)
{
totalQuicksortTime = 0;
totalMergesortTime = 0;
for (int k = 0; k < numOfTestsPerLength; k++)
{
array = GetArray(i, new int[] { -9999, 9999 });
stopwatch.Start();
QuickSort((int[])array.Clone());
stopwatch.Stop();
totalQuicksortTime += stopwatch.ElapsedMilliseconds;
stopwatch.Restart();
MergeSort((int[])array.Clone());
stopwatch.Stop();
totalMergesortTime += stopwatch.ElapsedMilliseconds;
}
Console.WriteLine($"Quicksort took an average of {totalQuicksortTime / numOfTestsPerLength} milliseconds to sort arrays with a length of {i}.");
Console.WriteLine($"Mergesort took an average of {totalMergesortTime / numOfTestsPerLength} milliseconds to sort arrays with a length of {i}.");
Console.WriteLine();
}
}
Even when I print the elapsed milliseconds of each run instead of averaging them out, it still only times zeros.
edit: Here are my methods (sorry if they are a bit junk):
static void Main(string[] args)
{
RunDiagnostics(new int[] { 3, 12 }, 1000);
}
public static int[] GetArray(int length, int[] RangeOfNumbers)
{
Random rng = new Random();
int[] arr = new int[length];
for (int i = 0; i < arr.Length; i++)
arr[i] = rng.Next(RangeOfNumbers[0], RangeOfNumbers[1]);
return arr;
}
public static void QuickSort(int[] array)
{
QuickSort(0, array.Length - 1);
void QuickSort(int left, int right)
{
if (right > left)
{
if (right - left == 1)
{
if (array[left] > array[right])
Swap(left, right);
}
else
{
Swap(GetStartingPivotIndex(), right);
int pivot = Partition(left, right - 1);
QuickSort(left, pivot - 1);
QuickSort(pivot + 1, right);
}
}
int GetStartingPivotIndex()
{
if (array[left] > array[right])
{
if (array[left + (right - left) / 2] > array[right])
return right;
else
return left + (right - left) / 2;
}
else
{
if (array[left + (right - left) / 2] > array[left])
return left;
else
return left + (right - left) / 2;
}
}
int Partition(int low, int high)
{
while (low != high)
{
if (array[low] < array[right])
low++;
else if (array[high] > array[right])
high--;
else
Swap(low, high);
}
Swap(low, right);
return low;
}
void Swap(int index1, int Index2)
{
int temp = array[index1];
array[index1] = array[Index2];
array[Index2] = temp;
}
}
}
public static void MergeSort(int[] array)
{
MergeSort(array);
int[] MergeSort(int[] array)
{
int[] array1 = array.Take(array.Length / 2).ToArray();
int[] array2 = array.Skip(array.Length / 2).ToArray();
if (array1.Length > 1)
MergeSort(array1);
if (array2.Length > 1)
MergeSort(array2);
int c1 = 0;
int c2 = 0;
bool flag1 = false;
bool flag2 = false;
for (int i = 0; i < array.Length; i++)
{
if (flag1 && !flag2)
{
array[i] = array2[c2];
if (c2 == array2.Length - 1)
flag2 = true;
else c2++;
}
else if (flag2 && !flag1)
{
array[i] = array1[c1];
if (c1 == array1.Length - 1)
flag1 = true;
else c1++;
}
else if (!flag1 && !flag2)
{
if (array1[c1] < array2[c2])
{
array[i] = array1[c1];
if (c1 == array1.Length - 1)
flag1 = true;
else c1++;
}
else
{
array[i] = array2[c2];
if (c2 == array2.Length - 1)
flag2 = true;
else c2++;
}
}
}
return array;
}
}

This isn't a problem with the Stopwatch.
The very first time your sort code runs, it's taking much longer than subsequent times. This can happen due to JIT compilation, caching, and similar things that are completely outside of your control. It's taking long enough that the ElapsedMilliseconds value has a meaningful integer value (e.g. 3), and so when you divide it by 1000 you end up with a number in the thousands place (e.g. .003).
Every other time the sort code runs, it's taking less than a millisecond. So all the += operations are adding zero to the total. The sum of all those zeroes is zero.
Changing the += stopwatch.ElapsedMilliseconds to += stopwatch.Elapsed.TotalMilliseconds; will fix that particular problem, and give you results more like this:
Quicksort took an average of 0.003297300000000042 milliseconds to sort arrays with a length of 3.
Mergesort took an average of 0.0019986999999999453 milliseconds to sort arrays with a length of 3.
Quicksort took an average of 0.0013175999999999856 milliseconds to sort arrays with a length of 4.
Mergesort took an average of 0.001030500000000005 milliseconds to sort arrays with a length of 4.
Quicksort took an average of 0.001468300000000015 milliseconds to sort arrays with a length of 5.
Mergesort took an average of 0.0011402999999999956 milliseconds to sort arrays with a length of 5.
However, there are other issues to fix.
You're including the time spent cloning the array in your results.
Start() should be switched to Restart(): right now the nth run of QuickSort is including the n-1th run of Mergesort in its time.
The whole strategy of adding thousands of individual run times together still exposes you to rounding errors: they're just smaller with double than they are with int. You see all those 0000000s and 999999s in the results? A better strategy is typically to run the same sort a whole bunch of times and then see how much total time has passed.
In general, you're better off relying on a benchmarking framework rather than writing your own code. There are a lot of issues like these that you are unlikely to consider when writing your own.

As #JosephDaSilva pointed out, stopwatch.ElapsedMilliseconds returns an integer value which means it's going to round down to 0 if it was less then a milisecond. stopwatch.Elapsed.TotalMilliseconds should be used as it returns a floating-point value, which won't get rounded down.

Related

Print out the successful development of prime numbers from small to large

Given any natural number N> 1 (previously assigned). Print out the successful development of prime numbers from small to large.
Example:
9 --> 3 * 3
12 --> 2 * 2 * 3
My idea is find all GCD and add to list int, and write a function isPrimeNumber(int n), browse List< int > and check if isPrimeNumber().
But I can't solve problem print out the successful development of prime numbers from small to large
Here is what I tried
static void Main(string[] args)
{
Console.WriteLine("Enter n: ");
int n = Convert.ToInt32(Console.ReadLine());
List<int> arr = new List<int>();
for (int i = 1; i <= n; i++)
{
if (n % i == 0)
{
arr.Add(i);
}
}
/* I need Print out the successful development of prime numbers from small to large here */
}
static bool isPrimeNumber(int n)
{
if (n < 2)
{
return false;
}
for (int i = 2; i <= Math.Sqrt(n); i++)
{
if (n % i == 0)
{
return false;
}
}
return true;
}
As you posted your working solution for that, let me share a different implementation for that that is still simple to understand, but more efficient, because it only tests primes until it reaches the square root of n. After that, there will not be any other divisor, except the number n itself, if n is prime.
static IList<int> Factors(int num) {
var result = new List<int>();
// avoid scenarios like num=0, that would cause infinite loop
if (num <= 1) {
return result;
}
// testing with 2 outside the main loop, otherwise we would skip factor 3
// (2 * 2 > 3)
while (num % 2 == 0) {
result.Add(2);
num /= 2;
}
// only test primes until sqrt(num)
int i = 3;
while (i * i <= num) {
if (num % i == 0) {
result.Add(i);
num /= i;
} else {
i++;
}
}
// if not 1 here, num is prime
if (num > 1) {
result.Add(num);
}
return result;
}
I solved it
Here is code
static void lesson6()
{
Console.WriteLine("Enter n: ");
int n = Convert.ToInt32(Console.ReadLine());
int a = n;
List<int> arr = new List<int>();
for (int i = 2; i <= n; i++)
{
while (n % i == 0)
{
arr.Add(i);
n /= i;
}
}
Console.Write($"{a} = ");
int lastIndex = arr.Count - 1;
for (int i = 0; i < arr.Count; i++)
{
if (i == lastIndex)
{
Console.Write(arr[i]);
}
else
{
Console.Write(arr[i] + "*");
}
}
}
As pointed by derpirscher in the comment, there are several sources online with different approaches for integer factorization.
I recommend you to look for Trial Division algorithm, as it is the easier to understand, and is similar to your approach.
Based on the code you shared, there are some thing you should consider:
for (int i = 1; i <= n; i++)
{
if (n % i == 0)
{
arr.Add(i);
}
}
After finding that a prime is a divisor and appending to the list, you are going to the next number. However, a prime can figure many times in the factorization of a number. E.g: 12 -> { 2, 2, 3 }.
You need divide n by the prime and continue testing the until it is not a divisor anymore, then you can go test the next prime.
This way, your n is shrinking down each time you find a prime divisor, until it eventually become 1. Then you know you found all prime divisors.

How to save CPU cycles when searching for a value in a sorted list?

In CodinGame learning platform, one of the questions used as an example in a C# tutorial is this one:
The aim of this exercise is to check the presence of a number in an
array.
Specifications: The items are integers arranged in ascending order.
The array can contain up to 1 million items. The array is never null.
Implement the method boolean Answer.Exists(int[] ints, int k) so that
it returns true if k belongs to ints, otherwise the method should
return false.
Important note: Try to save CPU cycles if possible.
Example:
int[] ints = {-9, 14, 37, 102};
Answer.Exists(ints, 102) returns true.
Answer.Exists(ints, 36) returns false.
My proposal was to do that:
using System;
using System.IO;
public class Answer
{
public static bool Exists(int[] ints, int k)
{
foreach (var i in ints)
{
if (i == k)
{
return true;
}
if (i > k)
{
return false;
}
}
return false;
}
}
The result of the test was:
✔ The solution works with a 'small' array (200 pts) - Problem solving
✔ The solution works with an empty array (50 pts) - Reliability
✘ The solution works in a reasonable time with one million items (700 pts) - Problem solving
I don't get the last point. It appears that the code may be more optimal than the one I suggested.
How to optimize this piece of code? Is a binary search an actual solution (given that the values in the array are already ordered), or there is something simpler that I missed?
Yes, I think that binary search O(log(N)) complexity v. O(N) complexity is the solution:
public static bool Exists(int[] ints, int k) {
return Array.BinarySearch(ints, k) >= 0;
}
since Array.BinarySearch return non-negative value if the item (k) has been found:
https://msdn.microsoft.com/en-us/library/2cy9f6wb(v=vs.110).aspx
Return Value Type: System.Int32 The index of the specified value in
the specified array, if value is found; otherwise, a negative number.
Here is a fast method for an ordered array
public static class Answer
{
public static bool Exists( int[] ints, int k )
{
var lower = 0;
var upper = ints.Length - 1;
if ( k < ints[lower] || k > ints[upper] ) return false;
if ( k == ints[lower] ) return true;
if ( k == ints[upper] ) return true;
do
{
var middle = lower + ( upper - lower ) / 2;
if ( ints[middle] == k ) return true;
if ( lower == upper ) return false;
if ( k < ints[middle] )
upper = Math.Max( lower, middle - 1 );
else
lower = Math.Min( upper, middle + 1 );
} while ( true );
}
}
Takes around 50 ticks on my cpu (with 90.000.000 items in the array)
Sample on dotnetfiddle
class Answer
{
public static bool Exists(int[] ints, int k)
{
int index = Array.BinarySearch(ints, k);
if (index > -1)
{
return true;
}
else
{
return false;
}
}
static void Main(string[] args)
{
int[] ints = { -9, 14, 37, 102 };
Console.WriteLine(Answer.Exists(ints, 14)); // true
Console.WriteLine(Answer.Exists(ints, 4)); // false
}
}
Apparently, the task intends we use the default binary search method instead of implementing one. I was also somewhat surprised it is what it evaluates for in 3rd test. "The solution uses the standard library to perform the binary search (iterating on ints)"
Which kinda is confusing, they could have mentioned this in the code instead of giving some 15 - 20 minutes to solve. which is another reason for this confusion.
This is what I wrote for that question -> dividing array to half and search the half -> a more rudimentary way of implementing it...
int half = size/2;
if( k < ints[half])
{
for(int i=0; i < half; i++)
{
if( k == ints[i])
{
return true;
}
}
}
else
{
for(int i=half; i < size; i++)
{
if( k == ints[i])
{
return true;
}
}
}
public static bool Exists(int[] ints, int k)
{
var d = 0;
var f = ints.Length - 1;
if (d > f) return false;
if (k > ints[f] || k < ints[d]) return false;
if (k == ints[f] || k == ints[d]) return true;
return (BinarySearch(ints, k, d, f) > 0);
}
public static int BinarySearch(int[] V, int Key, int begin, int end)
{
if (begin > end)
return -1;
var MidellIndex = (begin + end) / 2;
if (Key == V[MidellIndex])
return MidellIndex;
else
{
if (Key > V[MidellIndex])
{
begin = MidellIndex + 1;
return BinarySearch(V, Key, begin, end);
}
else
{
end = MidellIndex - 1;
return BinarySearch(V, Key, begin, end);
}
}
}
I saw the all solutions, by the way I create and test the following recursive approach and get the complete points:
public static bool Exists(int[] ints, int k)
{
if (ints.Length > 0 && ints[0] <= k && k <= ints[ints.Length - 1])
{
if (ints[0] == k || ints[ints.Length - 1] == k) return true;
return SearchRecursive(ints, k, 0, ints.Length - 1) != -1;
}
return false;
}
private static int SearchRecursive(int[] array, int value, int first, int last)
{
int middle = (first + last) / 2;
if (array[middle] == value)
{
return middle;
}
else if (first >= last)
{
return -1;
}
else if (value < array[middle])
{
return SearchRecursive(array, value, first, middle - 1);
}
else
{
return SearchRecursive(array, value, middle + 1, last);
}
}
Yes, BinarySearch would be faster than most algorithms you can write manually. However, if the intent of the exercise is to learn how to write an algorithm, you are on the right track. Your algorithm, though, makes an unnecessary check with if (i > k) ... why do you need this?
Below is my general algorithm for simple requirements like this. The while loop like this is slightly more performant than a for-loop and out performs a foreach easily.
public class Answer
{
public static bool Exists(int[] ints, int k)
{
var i = 0;
var hasValue = false;
while(i < ints.Length && !hasValue)
{
hasValue = ints[i] == k;
++i;
}
return hasValue;
}
}
If you are trying to squeeze every ounce of speed out of it... consider that your array has 1..100 and you want to search for 78. Your algorithm needs to search and compare 78 items before you find the right one. How about instead you search the first item and its not there, so you jump to array size / 2 and find 50? Now you skipped 50 iterations. You know that 78 MUST be in the top half of the array, so you can again split it in half and jump to 75, etc. By continuously splitting the array in half, you do much fewer iterations then your brute force approach.

Verify if a list (or a sublist of that list) of decimal values can equal a certain sum

Hi I have a List<decimal> containing values between ]0;1].
I want to check if a total (or subtotal) of these values can equal 1 (or almost).
I can also use Linq functions to filter or manipulate the list.
Desired results:
A list containing {0.7, 0.7, 0.7} should return false;
A list containing {0.7, 0.3, 0.7} should return true;
A list containing {0.777777, 0.2, 0.1} should return false;
A list containing {0.33333, 0.33333, 0.33333} should return true;
A list containing {0.4, 0.5, 0.6, 0.3} should return true.
Obviously, I'll want something with the lowest performance cost possible.
UPDATED -- now doesn't repetively sum
try this
bool isClose(IEnumerable<decimal> list, decimal epislon) {
return isClose(Enumerable.Empty<decimal>(),list,0,list.Sum(),epislon);
}
// Define other methods and classes here
bool isClose(IEnumerable<decimal> left,IEnumerable<decimal> right, decimal leftSum,decimal rightSum, decimal epsilon) {
if (leftSum>=1-epsilon && leftSum<=1+epsilon) return true;
if (leftSum>1+epsilon) return false;
if (leftSum+right.Sum()< 1-epsilon) return false;
if (!right.Any()) return false;
for (var i=0;i<right.Count();i++) {
var skip=right.Skip(i);
var newItem=skip.First();
if (isClose(left.Concat(skip.Take(1)),skip.Skip(1),leftSum+newItem,rightSum-newItem,epsilon)) return true;
}
return false;
}
isClose(new[] {0.7m,0.7m,0.7m},0.001m); // returns false
isClose(new[] {0.7m,0.3m,0.7m},0.001m); //returns true
isClose(new[] {0.777777m,0.2m,0.1m},0.001m); //returns false
isClose(new[] {0.33333m,0.33333m,0.33333m},0.001m); //returns true
EDIT 5th Test
isClose(new[] {0.4m, 0.5m, 0.6m, 0.3m},0.001m); //returns true
This is the subset sum problem, a special case of the knapsack problem. It's hard (NP-complete). The best known algorithms have exponential complexity. However there are approximate algorithms with polynomial complexity. These answer the question 'is there a subset that sums to 1±ε ?'
http://www.cs.dartmouth.edu/~ac/Teach/CS105-Winter05/Notes/nanda-scribe-3.pdf
http://en.wikipedia.org/wiki/Subset_sum_problem
Knuth's TAOCP (probably)
Here is a rather straightforward, niave, brute force approach. It won't be efficient, but hopefully it's easier to understand.
private const decimal threshold = .001M;
public static bool CloseEnough(decimal first, decimal second, decimal threshold)
{
return Math.Abs(first - second) < threshold;
}
private static bool SubsetSum(List<decimal> data, int desiredSum)
{
int numIteratons = (int)Math.Pow(2, data.Count);
for (int i = 1; i < numIteratons; i++)
{
decimal sum = 0;
int mask = 1;
for (int j = 0; j < data.Count && sum < desiredSum + threshold; j++)
{
if ((i & mask) > 0)
{
sum += data[j];
}
mask <<= 1;
}
if (CloseEnough(sum, desiredSum, threshold))
{
return true;
}
}
return false;
}
public static bool SubListAddsTo(this IEnumerable<decimal> source,
decimal target, decimal tolerance)
{
decimal lowtarget = target - tolerance;
decimal hightarget = target + tolerance;
HashSet<decimal> sums = new HashSet<decimal>();
sums.Add(0m);
List<decimal> newSums = new List<decimal>();
foreach(decimal item in source)
{
foreach(decimal oldSum in sums)
{
decimal sum = oldSum + item;
if (sum < lowtarget)
{
newSums.Add(sum);//keep trying
}
else if (sum < hightarget)
{
return true;
}
//else { do nothing, discard }
}
foreach (decimal newSum in newSums)
{
sums.Add(newSum);
}
newSums.Clear();
}
return false;
}
Tested by:
List<decimal> list1 = new List<decimal>(){0.7m, 0.7m, 0.7m};
List<decimal> list2 = new List<decimal>(){0.7m, 0.3m, 0.7m};
List<decimal> list3= new List<decimal>(){0.777777m, 0.2m, 0.1m};
List<decimal> list4 = new List<decimal>() { 0.33333m, 0.33333m, 0.33333m };
List<decimal> list5 = new List<decimal>() { 0.4m, 0.5m, 0.6m, 0.3m };
Console.WriteLine(list1.SubListAddsTo(1m, 0.001m)); //false
Console.WriteLine(list2.SubListAddsTo(1m, 0.001m)); //true
Console.WriteLine(list3.SubListAddsTo(1m, 0.001m)); //false
Console.WriteLine(list4.SubListAddsTo(1m, 0.001m)); //true
Console.WriteLine(list5.SubListAddsTo(1m, 0.001m)); //true
edited: my original code didn't allow for the approximation (0.9999 = 1).
This uses a bitmap of the number of variations to mask the values in the source array in order to brute force all of the variations.
This is about 7.5 times faster than the accepted answer when tested on all five test cases in a million count loop (about 41 seconds vs about 5.5 seconds). It is about twice as fast as David B's sln and about 15% faster than Servy's sln.
public static bool Test(decimal[] list, decimal epsilon)
{
var listLength = list.Length;
var variations = (int)(Math.Pow(2, listLength) - 1);
var bits = new bool[9];
for (var variation = variations; variation > 0; variation--)
{
decimal sum = 0;
bits[1] = (variation & 1) == 1;
bits[2] = (variation & 2) == 2;
bits[3] = (variation & 4) == 4;
bits[4] = (variation & 8) == 8;
bits[5] = (variation & 16) == 16;
bits[6] = (variation & 32) == 32;
bits[7] = (variation & 64) == 64;
bits[8] = (variation & 128) == 128;
for (var bit = listLength; bit > 0; bit--)
{
if (bits[bit])
{
sum += list[bit - 1];
}
}
if (Math.Abs(sum - 1) < epsilon)
{
return true;
}
}
return false;
}
edit: this NewTest version is 30% faster than the above version and is over ten times faster than the accepted solution. It removes building the array for coming up with the bitmask in favour of Servy's approach which is where the bulk of the improvement comes from. It also removes the Math.Abs call which gave marginal improvement.
private const decimal Epislon = 0.001m;
private const decimal Upper = 1 + Epislon;
private const decimal Lower = 1 - Epislon;
private static bool NewTest(decimal[] list)
{
var listLength = list.Length;
var variations = (int)(Math.Pow(2, listLength) - 1);
for (var variation = variations; variation > 0; variation--)
{
decimal sum = 0;
int mask = 1;
for (var bit = listLength; bit > 0; bit--)
{
if ((variation & mask) == mask)
{
sum += list[bit - 1];
}
mask <<= 1;
}
if (sum > Lower && sum < Upper)
{
return true;
}
}
return false;
}

Interview practice on array size i.e. I can't use arraylist, linked list, or any standard list class

I've become rusty at the brain teaser questions since I have been using fancy IDEs and C#. I have a solution for this question, but my real question is How should I deal with the array size? Assuming I can't use a list. This is written in C#.
The array size could be too small, or just be a waste of memory in its current state. I left out checks for negative coins, etc. so we could just focus on the algorithm. So I want to get rid of this line: double [] minList = new double[1000];
Here's the interview question, return the minimum number of coins that sum to a value (-1 if impossible:
public static Main(string [] args) {
double[] coins = {0.01, 0.05, 0.10, 0.25};
Console.WriteLine(MinNumberOfCoinsToSum(0.24, coins));
}
public static int MinNumberOfCoinsToSum(double sum, double [] coins)
{
double [] minList = new double[1000];
minList[0] = sum;
for (int i = 1; i < minList.Length; i++)
{
double buffer = minList[i-1];
for (int j = 0; j < coins.Length; j++)
{
if(minList[i-1] - coins[j] >= 0.0)
{
buffer = Math.Min(buffer, minList[i-1] - coins[j]);
}
}
minList[i] = Math.Min(minList[i-1], Math.Round(buffer, 2));
if (minList[i] == 0.0)
{
return i;
}
}
return -1;
}
Well Here's an answer taking in what you guys said (though it doesn't return -1 if not found):
private static int MinNumberOfCoinsToSumRecursiveDecimalVersion(decimal sum, decimal [] coins)
{
decimal buffer = sum;
for(int i = 0; i < coins.Length; i++)
{
if(sum - coins[i] >= 0)
{
buffer = Math.Min(buffer, sum - coins[i]);
}
}
if (buffer == sum && sum == 0m)
return 0;
if(buffer == sum && sum != 0m)
{
return Int32.MinValue;
}
int result = 1 + MinNumberOfCoinsToSumRecursiveDecimalVersion(Math.Min(buffer, sum), coins);
return result;
}
However my real is question is how do I deal with an array size when I do not know the size beforehand. Thanks btw for the decimal note... that would have been embarrassing at an interview.
public int MinNumberForCoinSum(int sum, decimal [] coins) {
// I assume there is a custom Util class, but it would be checking null, empty, or any other business requirement
foreach(var verification in VerificationUtil.GetArrayVerifications()) {
verification.Verify(coins);
}
if(sum < 0 ) { Throw new InvalidArgumentException()); }
return MinNumberOfCoinsToSumRecursiveDecimalVersion(sum, coins);
}
The classic solution to deal with unknown array sizes is to use an initial capacity and then start resizing when it gets full. The growth factor is typically 2. This is what most of the growing data structures in the .NET framework do (list, stack, queue etc.)
Forgive if I sound obnoxious: you are way off. The question has nothing to do with resizing arrays. Here is a solution.
public static int numberOfCoins(double[] coins, double sum){
int total = 0;
Arrays.sort(coins);
int amount = (int)(sum*100);//convert to int for precision
for(int i=coins.length-1; i>=0 && amount > 0; i--){
int coin = (int)(100*coins[i]);//convert to int for precision
total+=amount/coin;
amount%=coin;
}
if(amount>0)
return -1;
return total;
}//numberOfCoins
Test the code with
public static void main(String... args){
double[] den = {0.01,.10,.05,.25};
System.out.println(numberOfCoins(den,1.65));
}
EDIT TO HANDLE INT CASE:
In that case I recommend overloading the function. by adding below as well
public static int numberOfCoins(int[] coins, int sum){
int total = 0;
Arrays.sort(coins);
int amount = sum;
for(int i=coins.length-1; i>=0 && amount > 0; i--){
total+=amount/coins[i];
amount%=coins[i];
}
if(amount>0)
return -1;
return total;
}//numberOfCoins

Is there a good radixsort-implementation for floats in C#

I have a datastructure with a field of the float-type. A collection of these structures needs to be sorted by the value of the float. Is there a radix-sort implementation for this.
If there isn't, is there a fast way to access the exponent, the sign and the mantissa.
Because if you sort the floats first on mantissa, exponent, and on exponent the last time. You sort floats in O(n).
Update:
I was quite interested in this topic, so I sat down and implemented it (using this very fast and memory conservative implementation). I also read this one (thanks celion) and found out that you even dont have to split the floats into mantissa and exponent to sort it. You just have to take the bits one-to-one and perform an int sort. You just have to care about the negative values, that have to be inversely put in front of the positive ones at the end of the algorithm (I made that in one step with the last iteration of the algorithm to save some cpu time).
So heres my float radixsort:
public static float[] RadixSort(this float[] array)
{
// temporary array and the array of converted floats to ints
int[] t = new int[array.Length];
int[] a = new int[array.Length];
for (int i = 0; i < array.Length; i++)
a[i] = BitConverter.ToInt32(BitConverter.GetBytes(array[i]), 0);
// set the group length to 1, 2, 4, 8 or 16
// and see which one is quicker
int groupLength = 4;
int bitLength = 32;
// counting and prefix arrays
// (dimension is 2^r, the number of possible values of a r-bit number)
int[] count = new int[1 << groupLength];
int[] pref = new int[1 << groupLength];
int groups = bitLength / groupLength;
int mask = (1 << groupLength) - 1;
int negatives = 0, positives = 0;
for (int c = 0, shift = 0; c < groups; c++, shift += groupLength)
{
// reset count array
for (int j = 0; j < count.Length; j++)
count[j] = 0;
// counting elements of the c-th group
for (int i = 0; i < a.Length; i++)
{
count[(a[i] >> shift) & mask]++;
// additionally count all negative
// values in first round
if (c == 0 && a[i] < 0)
negatives++;
}
if (c == 0) positives = a.Length - negatives;
// calculating prefixes
pref[0] = 0;
for (int i = 1; i < count.Length; i++)
pref[i] = pref[i - 1] + count[i - 1];
// from a[] to t[] elements ordered by c-th group
for (int i = 0; i < a.Length; i++){
// Get the right index to sort the number in
int index = pref[(a[i] >> shift) & mask]++;
if (c == groups - 1)
{
// We're in the last (most significant) group, if the
// number is negative, order them inversely in front
// of the array, pushing positive ones back.
if (a[i] < 0)
index = positives - (index - negatives) - 1;
else
index += negatives;
}
t[index] = a[i];
}
// a[]=t[] and start again until the last group
t.CopyTo(a, 0);
}
// Convert back the ints to the float array
float[] ret = new float[a.Length];
for (int i = 0; i < a.Length; i++)
ret[i] = BitConverter.ToSingle(BitConverter.GetBytes(a[i]), 0);
return ret;
}
It is slightly slower than an int radix sort, because of the array copying at the beginning and end of the function, where the floats are bitwise copied to ints and back. The whole function nevertheless is again O(n). In any case much faster than sorting 3 times in a row like you proposed. I dont see much room for optimizations anymore, but if anyone does: feel free to tell me.
To sort descending change this line at the very end:
ret[i] = BitConverter.ToSingle(BitConverter.GetBytes(a[i]), 0);
to this:
ret[a.Length - i - 1] = BitConverter.ToSingle(BitConverter.GetBytes(a[i]), 0);
Measuring:
I set up some short test, containing all special cases of floats (NaN, +/-Inf, Min/Max value, 0) and random numbers. It sorts exactly the same order as Linq or Array.Sort sorts floats:
NaN -> -Inf -> Min -> Negative Nums -> 0 -> Positive Nums -> Max -> +Inf
So i ran a test with a huge array of 10M numbers:
float[] test = new float[10000000];
Random rnd = new Random();
for (int i = 0; i < test.Length; i++)
{
byte[] buffer = new byte[4];
rnd.NextBytes(buffer);
float rndfloat = BitConverter.ToSingle(buffer, 0);
switch(i){
case 0: { test[i] = float.MaxValue; break; }
case 1: { test[i] = float.MinValue; break; }
case 2: { test[i] = float.NaN; break; }
case 3: { test[i] = float.NegativeInfinity; break; }
case 4: { test[i] = float.PositiveInfinity; break; }
case 5: { test[i] = 0f; break; }
default: { test[i] = test[i] = rndfloat; break; }
}
}
And stopped the time of the different sorting algorithms:
Stopwatch sw = new Stopwatch();
sw.Start();
float[] sorted1 = test.RadixSort();
sw.Stop();
Console.WriteLine(string.Format("RadixSort: {0}", sw.Elapsed));
sw.Reset();
sw.Start();
float[] sorted2 = test.OrderBy(x => x).ToArray();
sw.Stop();
Console.WriteLine(string.Format("Linq OrderBy: {0}", sw.Elapsed));
sw.Reset();
sw.Start();
Array.Sort(test);
float[] sorted3 = test;
sw.Stop();
Console.WriteLine(string.Format("Array.Sort: {0}", sw.Elapsed));
And the output was (update: now ran with release build, not debug):
RadixSort: 00:00:03.9902332
Linq OrderBy: 00:00:17.4983272
Array.Sort: 00:00:03.1536785
roughly more than four times as fast as Linq. That is not bad. But still not yet that fast as Array.Sort, but also not that much worse. But i was really surprised by this one: I expected it to be slightly slower than Linq on very small arrays. But then I ran a test with just 20 elements:
RadixSort: 00:00:00.0012944
Linq OrderBy: 00:00:00.0072271
Array.Sort: 00:00:00.0002979
and even this time my Radixsort is quicker than Linq, but way slower than array sort. :)
Update 2:
I made some more measurements and found out some interesting things: longer group length constants mean less iterations and more memory usage. If you use a group length of 16 bits (only 2 iterations), you have a huge memory overhead when sorting small arrays, but you can beat Array.Sort if it comes to arrays larger than about 100k elements, even if not very much. The charts axes are both logarithmized:
(source: daubmeier.de)
There's a nice explanation of how to perform radix sort on floats here:
http://www.codercorner.com/RadixSortRevisited.htm
If all your values are positive, you can get away with using the binary representation; the link explains how to handle negative values.
By doing some fancy casting and swapping arrays instead of copying this version is 2x faster for 10M numbers as Philip Daubmeiers original with grouplength set to 8. It is 3x faster as Array.Sort for that arraysize.
static public void RadixSortFloat(this float[] array, int arrayLen = -1)
{
// Some use cases have an array that is longer as the filled part which we want to sort
if (arrayLen < 0) arrayLen = array.Length;
// Cast our original array as long
Span<float> asFloat = array;
Span<int> a = MemoryMarshal.Cast<float, int>(asFloat);
// Create a temp array
Span<int> t = new Span<int>(new int[arrayLen]);
// set the group length to 1, 2, 4, 8 or 16 and see which one is quicker
int groupLength = 8;
int bitLength = 32;
// counting and prefix arrays
// (dimension is 2^r, the number of possible values of a r-bit number)
var dim = 1 << groupLength;
int groups = bitLength / groupLength;
if (groups % 2 != 0) throw new Exception("groups must be even so data is in original array at end");
var count = new int[dim];
var pref = new int[dim];
int mask = (dim) - 1;
int negatives = 0, positives = 0;
// counting elements of the 1st group incuding negative/positive
for (int i = 0; i < arrayLen; i++)
{
if (a[i] < 0) negatives++;
count[(a[i] >> 0) & mask]++;
}
positives = arrayLen - negatives;
int c;
int shift;
for (c = 0, shift = 0; c < groups - 1; c++, shift += groupLength)
{
CalcPrefixes();
var nextShift = shift + groupLength;
//
for (var i = 0; i < arrayLen; i++)
{
var ai = a[i];
// Get the right index to sort the number in
int index = pref[( ai >> shift) & mask]++;
count[( ai>> nextShift) & mask]++;
t[index] = ai;
}
// swap the arrays and start again until the last group
var temp = a;
a = t;
t = temp;
}
// Last round
CalcPrefixes();
for (var i = 0; i < arrayLen; i++)
{
var ai = a[i];
// Get the right index to sort the number in
int index = pref[( ai >> shift) & mask]++;
// We're in the last (most significant) group, if the
// number is negative, order them inversely in front
// of the array, pushing positive ones back.
if ( ai < 0) index = positives - (index - negatives) - 1; else index += negatives;
//
t[index] = ai;
}
void CalcPrefixes()
{
pref[0] = 0;
for (int i = 1; i < dim; i++)
{
pref[i] = pref[i - 1] + count[i - 1];
count[i - 1] = 0;
}
}
}
You can use an unsafe block to memcpy or alias a float * to a uint * to extract the bits.
I think your best bet if the values aren't too close and there's a reasonable precision requirement, you can just use the actual float digits before and after the decimal point to do the sorting.
For example, you can just use the first 4 decimals (be they 0 or not) to do the sorting.

Categories

Resources