So I have a list of items. Each item on the list has a property called notional. Now, the list is already sorted. What I need to do is, develop a function that sets the type of list to one of the following:
Bullet - notional is the same for every item
Amortizing - notional decreases over the course of the schedule (might stay the same from element to element but it should never go up, and should end lower)
Accreting - notional increases over the course of the schedule (might stay the same from element to element but it should never go down, and should end higher)
Rollercoaster - notional goes up and down (could end the same, higher, or lower, but shouldn't be the same for each element and shouldn't be classfied as the other types)
What would this method look like and what would be the most efficient way to go through the list and figure this out?
Thanks!
This would be a straightforward way to do it:
bool hasGoneUp = false;
bool hasGoneDown = false;
T previous = null; // T is the type of objects in the list; assuming ref type
foreach(var item in list)
{
if (previous == null) {
previous = item;
continue;
}
hasGoneUp = hasGoneUp || item.notional > previous.notional;
hasGoneDown = hasGoneDown || item.notional < previous.notional;
if(hasGoneUp && hasGoneDown) {
return Trend.Rollercoaster;
}
previous = item;
}
if (!hasGoneUp && !hasGoneDown) {
return Trend.Bullet;
}
// Exactly one of hasGoneUp and hasGoneDown is true by this point
return hasGoneUp ? Trend.Accreting : Trend.Amortizing;
Let trendOut = Bullet
Loop from First Item to Last item
2.1. If previous notional < next notional
2.1.a. If trendOut = Amortizing return RollerCoaster
2.1.b. Else set trendOut = Accreting
2.2. if Previous Notional > next notional
2.2.a. If trendOut = Accreting return RollerCoaster
2.2.b. Else set trendOut = Amortizing
return trendOut.
You could probably do something as simple as this
var changeList = new List<Integer>
for(i = 0; i < yourList.Count() - 1; i++)
{
changeList.Add(yourList.Item(i + 1) - yourList.Item(i));
}
//Determine if the nature of the list
var positiveChangeCount = changeList.Where(x => x < 0);
var negativeChangeCount = changeList.Where(x => X > 0);
if (positiveChangeCount = yourList.Count)
{
Accreting;
}
elseif (negativeChangeCount = yourList.Count)
{
Amortizing;
}
elseif (negativeChangeCount + PositiveChangeCount = 0)
{
Bullet;
}
else
{
Rollercoaster;
}
I usually start of by optimizing for simplicity first and then performance. Hence, I would start by making a second list of N-1 elements, whose {elements} are differences between the {notionals} of the first list.
Hence, for the second list, I would expect the following for the list of your needs
Bullet - ALL elements are 0
Amortising - ALL elements stay 0 or negative
Accreting - ALL elements stay 0 or positive
Rollercoaster - Elements oscillate between negative & positive
You can probably optimize it an do it in one pass. Basically, this is a discrete differentiation over your data.
bool OnlyGreaterOrEqual=true;
bool OnlyLessOrEqual=true;
foreach(int i=1;i<itemList.Count;i++){
if(itemList[i].notional>itemList[i-1].notional){
OnlyLessOrEqual=false;
}else if(itemList[i].notional<itemList[i-1].notional){
OnlyGreaterOrEqual=false;
}
}
if(OnlyGreaterOrEqual && OnlyLessOrEqual){
return "Bullet";
}else if(OnlyGreaterOrEqual){
return "Accreting":
}else if(OnlyLessOrEqual){
return "Amortizing";
}else{
return "RollerCoast";
}
This is basically a Linq implementation of Danish's answer. It'll require (worst case) 3 passes through the list, but because they are so small it won't really matter from a performance point of view. (I wrote it to work on a list of ints so you'll have to modify it easily to work with your types).
var tmp = values
.Skip(1)
.Zip( values, (first, second) => first - second )
.ToList();
var up = tmp.Any( t => t > 0 );
var down = tmp.Any( t => t < 0 );
if( up && down )
// Rollercoaster
else if( up )
// Accreting
else if( down )
// Amortizing
else
// Bullet
You could also (ab)use the Aggregate operator and Tuple to do it as one query. However, this will fail if the collection is empty and is a bit weird to use in production code.
var result = values.Skip(1).Aggregate(
Tuple.Create<int, bool, bool>( values.First(), false, false ),
( last, current ) => {
return Tuple.Create(
current,
last.Item2 || (current - last.Item1) > 0,
last.Item3 || (current - last.Item1) < 0 );
});
result will be a tuple that contains:
the last element of the collection (which is of no use)
Item2 will contain a boolean indicating whether any element was bigger than the previous element
Item3 will contain a boolean indicating whether any element was smaller than the previous element
The same switch statement as above can be used to decide which pattern your data follows.
Related
I'm stuck only partially passing the below problem.
Given a sequence of integers, check whether it is possible to obtain a strictly increasing sequence by erasing no more than one element from it.
Example
sequence = [1, 3, 2, 1]
almostIncreasingSequence(sequence) = false
sequence = [1, 3, 2]
almostIncreasingSequence(sequence) = true
My code that is only passing some examples:
bool almostIncreasingSequence(int[] sequence) {
int seqIncreasing = 0;
if (sequence.Length == 1) return true;
for (int i = 0;i < sequence.Length-2;i++)
{
if ((sequence[i] == sequence[++i]+1)||(sequence[i] == sequence[++i]))
{
seqIncreasing++;
}
}
return ((seqIncreasing == sequence.Length) || (--seqIncreasing == sequence.Length));
}
Failed Examples:
Input:
sequence: [1, 3, 2]
Output:
false
Expected Output:
true
Input:
sequence: [10, 1, 2, 3, 4, 5]
Output:
false
Expected Output:
true
Input:
sequence: [0, -2, 5, 6]
Output:
false
Expected Output:
true
Input:
sequence: [1, 1]
Output:
false
Expected Output:
true
The LINQ-based answer is fine, and expresses the basic problem well. It's easy to read and understand, and solves the problem directly. However, it does have the problem that it requires generating a new sequence for each element in the original. As the sequences get longer, this becomes dramatically more costly and eventually, intractable.
It doesn't help that it requires the use of Skip() and Take(), which themselves add to the overhead of handling the original sequence.
A different approach is to scan the sequence once, but keep track of whether a deletion has already been attempted and when finding an out-of-sequence element, to a) immediately return false if a deletion was already found, and b) don't include the deleted element in the determination of the sequence.
The code you tried almost accomplishes this. Here's a version that works:
static bool almostIncreasingSequence(int[] sequence)
{
bool foundOne = false;
for (int i = -1, j = 0, k = 1; k < sequence.Length; k++)
{
bool deleteCurrent = false;
if (sequence[j] >= sequence[k])
{
if (foundOne)
{
return false;
}
foundOne = true;
if (k > 1 && sequence[i] >= sequence[k])
{
deleteCurrent = true;
}
}
if (!foundOne)
{
i = j;
}
if (!deleteCurrent)
{
j = k;
}
}
return true;
}
Note: I originally thought your attempt could be fixed with a minor change. But ultimately, it turned out that it had to be essentially the same as the generic implementation I wrote (especially once I fixed that one too…see below). The only material difference is really just whether one uses an array or a generic IEnumerable<T>.
For grins, I wrote another approach that is in the vein of the LINQ-based solution, in that it works on any sequence, not just arrays. I also made it generic (albeit with the constraint that the type implements IComparable<T>). That looks like this:
static bool almostIncreasingSequence<T>(IEnumerable<T> sequence) where T : IComparable<T>
{
bool foundOne = false;
int i = 0;
T previous = default(T), previousPrevious = default(T);
foreach (T t in sequence)
{
bool deleteCurrent = false;
if (i > 0)
{
if (previous.CompareTo(t) >= 0)
{
if (foundOne)
{
return false;
}
// So, which one do we delete? If the element before the previous
// one is in sequence with the current element, delete the previous
// element. If it's out of sequence with the current element, delete
// the current element. If we don't have a previous previous element,
// delete the previous one.
if (i > 1 && previousPrevious.CompareTo(t) >= 0)
{
deleteCurrent = true;
}
foundOne = true;
}
}
if (!foundOne)
{
previousPrevious = previous;
}
if (!deleteCurrent)
{
previous = t;
}
i++;
}
return true;
}
Of course, if you're willing to copy the original sequence into a temporary array, if it's not already one, then you could easily make the array-based version generic, which would make the code a lot simpler but still generic. It just depends on what your priorities are.
Addendum:
The basic performance difference between the LINQ method and a linear method (such as mine above) is obvious, but I was curious and wanted to quantify this difference. So I ran some tests, using randomly generated sequences, to get a rough idea of the difference.
I performed two versions of the tests: the first, I ran a loop with 1000 trials, where the sequences could be anywhere between 10 and 100 elements long; and the second, with 10,000 trials and sequences between 100 and 1000 elements long. I performed the second version, because on my laptop the entire test of 1000 trials with shorter sequences completed in less than 1/20th of a second, too short a time for me to have confidence in the validity of the result.
With that first version, the code spent about 1ms calling the linear method of the check, and about 30ms calling the LINQ method, for a 30x difference in speed. Increasing the number of trials to 10,000 confirmed the result; the times scaled almost exactly 10x for each method, keeping a difference of 30x.
With the second version, the difference was closer to 400x. The linear version took about 0.07 seconds, while the LINQ version took 30 seconds.
As expected, the longer the sequence, the worse the disparity. For very short sequences, not only is the code unlikely to ever spend much time in the sequence-checking logic, the discrepancy between the linear and LINQ methods is going to be relatively small. But as the sequences get longer, the discrepancy will trend to very poor performance for the LINQ version while the linear version remains an excellent performer.
The LINQ version is very readable and concise. So in a situation where the inputs are always going to be relatively short (on the order of a dozen or two elements at the most), I'd go with the LINQ version. But if I expected to execute this test routinely with data that was any longer than that, I would avoid the LINQ and stick with the much more efficient linear approach.
A note on the randomly-generated sequences: I wrote the code to generate a monotonically increasing sequence of non-negative numbers, of the desired length, and then inserted between 0 and 2 (inclusive) new elements having a value of int.MinValue or int.MaxValue (also randomly selected, for each insertion). In this way, a third of the tests involved sequences that were trivially valid, a third involved sequences that required finding the correct single element to remove, and a third were not valid (i.e. did not meet the requirement that it could be made monotonically increasing by deleting at most one element).
UPDATE: Fixed a bug related to the way I was generating subsequences using Except. The obvious issue was that the subsequences generated when the original sequence contained duplicate items could be wrong; all positions of duplicate items could be potentially removed.
This problem seems deceptively simple but you can easily get bogged down in loops with ifs and elses that will never get it exactly right.
The best way to solve this is to take a step back and understand what the condition you are asking for really means. An almost strictly increasing sequence is one such that, of all possible subsequences created be removing one single item, at least one must be strictly increasing.
Ok, that seems to be sound reasoning, and its easy to implement, so lets do it:
First, a trivial method that tells us if a given sequence is strictly increasing:
private static bool IsStrictlyIncreasing<T>(this IEnumerable<T> sequence)
where T : IComparable<T>
{
using (var e = sequence.GetEnumerator())
{
if (!e.MoveNext())
return true;
var previous = e.Current;
while (e.MoveNext())
{
if (e.Current.CompareTo(previous) <= 0)
return false;
previous = e.Current;
}
return true;
}
}
Now we need a helper method to generate all possible subsequences removing one item (as stated above, simply using Except will not cut it if T has value equality semantics):
private static IEnumerable<IEnumerable<T>> GenerateSubsequences<T>(
this IEnumerable<T> sequence)
=> Enumerable.Range(0, sequence.Count())
.Select(i => sequence.Take(i)
.Concat(sequence.Skip(i + 1)))
And now, we simply need to check all subsequences and find at least one that is strictly increasing:
public static bool IsAlmostStrictlyIncreasing<T>(this IEnumerable<T> sequence)
where T : IComparable<T>
=> sequence.GenerateSubsequences()
.Any(s => s.IsStrictlyIncreasing());
That should do it.
Having solved that CodeSignal challenge using C# myself, I can tell you how I approached it.
First, a helper method to handle the logic of deciding when to remove an element from a sequence:
private static bool removeElement(IEnumerable<int> sequence, int i) {
// This method handles the logic for determining whether to remove an element from a sequence of integers.
// Initialize the return variable and declare some useful element aliases.
bool removeElement = false;
int c = sequence.ElementAt(i), p = sequence.ElementAtOrDefault(i - 1), n = sequence.ElementAtOrDefault(i + 1);
// Remove the first element if and only if it is greater than or equal to the next element.
if (i == 0) removeElement = (c >= n);
// Remove the last element if and only if it is less than or equal to the previous element.
else if (i == (sequence.Count() - 1)) removeElement = (c <= p);
// Removal logic for an element somewhere in the middle of the sequence:
else {
// If the current element is greater than the previous element...
// ...and the current element is less than the next element, then do not remove the current element.
if (c > p && c < n) removeElement = false;
// If the current element is greater than or equal to the next element, then it might need to be removed.
else if (c > p && c >= n) {
removeElement = true;
// Handle edge case for test 19.
// If the current element is the next-to-last element...
// ...and the only reason it's being considered for removal is because it is less than the last element...
// ...then skip it and remove the last element instead.
if (i == (sequence.Count() - 2)) removeElement = false;
// Handle edge case for test 16.
// If the current element occurs before the next-to-last element...
if (i < (sequence.Count() - 2))
// ...and both the current and next elements are less than the following element...
// ...then skip the current element and remove the next one instead.
if (n < sequence.ElementAt(i + 2) && c < sequence.ElementAt(i + 2)) removeElement = false;
// Otherwise, remove the current element.
} else removeElement = true;
}
return removeElement;
}
Then I wrote two versions of the main method: one using LINQ, and one without.
LINQ version:
bool almostIncreasingSequence(int[] sequence) {
// Eliminate the most trivial cases first.
if (sequence.Length <= 2) return true;
else if (sequence.SequenceEqual(sequence.Distinct().OrderBy(x => x))) return true;
else {
// Get the index of the first element that should be removed from the sequence.
int index = Enumerable.Range(0, sequence.Length).First(x => removeElement(sequence, x));
// Remove that element from the sequence.
sequence = sequence.Where((x, i) => i != index).ToArray();
}
// Return whether or not the remaining sequence is strictly increasing.
return sequence.SequenceEqual(sequence.Distinct().OrderBy(x => x));
}
Non-LINQ version:
bool almostIncreasingSequence(int[] sequence) {
// Eliminate the most trivial cases.
if (sequence.Length <= 2) return true;
// Make a copy of the input array in the form of a List collection.
var initSequence = new List<int>(sequence);
// Iterate through the List.
for (int i = 0; i < initSequence.Count; i++) {
// If the current element needs to be removed from the List, remove it.
if (removeElement(initSequence, i)) {
initSequence.RemoveAt(i);
// Now the entire sequence after the first removal must be strictly increasing.
// If this is not the case, return false.
for (int j = i; j < initSequence.Count; j++) {
if (removeElement(initSequence, j)) return false;
}
break;
}
}
return true;
}
Both variations pass all of the provided test cases:
38/38 tests passed.
Sample tests: 19/19
Hidden tests: 19/19
Score: 300/300
Here is my version. It has similarities with Peter Duniho's first solution.
static bool AlmostIncreasingSequence(int[] sequence)
{
int problemIndex = -1;
for (int i = 0; i < sequence.Length - 1; i++)
{
if (sequence[i] < sequence[i + 1])
continue; // The elements i and i + 1 are in order
if (problemIndex != -1)
return false; // The sequence has more than one problems, so it cannot be fixed
problemIndex = i; // This is the first problem found so far
}
if (problemIndex == -1)
return true; // The sequence has no problems
if (problemIndex == 0)
return true; // The sequence can be fixed by removing the first element
if (problemIndex == sequence.Length - 2)
return true; // The sequence can be fixed by removing the last element
if (sequence[problemIndex - 1] < sequence[problemIndex + 1])
return true; // The sequence can be fixed by removing the (problemIndex) element
if (sequence[problemIndex] < sequence[problemIndex + 2])
return true; // The sequence can be fixed by removing the (problemIndex + 1) element
return false; // The sequence cannot be fixed
}
I have applied a recursive method:
public bool IsAlmostIncreasingSequence(int[] sequence)
{
if (sequence.Length <= 2)
return true;
return IsAlmostIncreasingSequenceRecursive(sequence, 0);
}
private bool IsAlmostIncreasingSequenceRecursive(int[] sequence, int seed)
{
int count = seed;
if (count > 1) //condition met: not almost
return false;
for (int i = 1; i < sequence.Length; i++)
{
if (sequence[i] <= sequence[i - 1])
{
if (i >= 2 && sequence[i - 2] >= sequence[i])
sequence = RemoveAt(sequence, i);
else
sequence = RemoveAt(sequence, i - 1);
return IsAlmostIncreasingSequenceRecursive(sequence, ++count);
}
}
return true;
}
private static int[] RemoveAt(int[] sequence, int index)
{
for (int i = index; i < sequence.Length - 1; i++)
sequence[i] = sequence[i + 1];
Array.Resize(ref sequence, sequence.Length - 1);
return sequence;
}
Well I have seen many solutions but things were made complicated a bit so here is my short and precise solution for that particular c# code problem.
bool solution(int[] sequence) {
//if there is just one item return true
if (sequence.Length <= 2) return true;
//create list for sequence comparison, C# beauty
List<int> newList = new List<int>();
if (sequence.Length > 0)
{
newList = new List<int>(sequence);
}
//just check if array is already valid sequence
if (sequence.SequenceEqual(newList.Distinct().OrderBy(x => x))) return true;
//count occurance of no sequence
int noSecCount = 0;
//for checking Gap
int lastGap = 0, thisGap = 0;
for (int n = 0; n < sequence.Count() - 1; n++)
{
thisGap = sequence[n + 1] - sequence[n];
//if current value is less then next one continue as array is in sequence by this point
//if not less then next one we have a situation here to further digging
if (!(sequence[n] < sequence[n + 1]))
{
noSecCount++;
//if we found more than one occurance of no sequence numbers, this array is not in sequence
if (noSecCount > 1) return false;
switch (n)
{
case 0: //First item at index 0
lastGap = thisGap;
newList = new List<int>(sequence);
newList.RemoveAt(n);
if (newList.SequenceEqual(newList.Distinct().OrderBy(x => x))) return true;
break;
default: //any other item above index 0
//just remove current item and check the sequence
newList = new List<int>(sequence);
newList.RemoveAt(n);
if (newList.SequenceEqual(newList.Distinct().OrderBy(x => x))) return true;
//remove the next item and check the sequencce
newList = new List<int>(sequence);
newList.RemoveAt(n + 1);
if (newList.SequenceEqual(newList.Distinct().OrderBy(x => x))) return true;
//if we reach here we need to check if gap between previous comparison and current one is same? if not we should quick as we find more then
//one out of sequence values.
if (thisGap != lastGap) return false;
lastGap = thisGap;
break;
}
}
}
//if we reach here and there is only one item which is out of sequence, we can remove it and get the sequence
return noSecCount == 1;
}
Thanks for the help strangers! I was able to get all my tests to pass first by removing all the increment/decrement operators for simplicity and simplifying my logic. If the iterator element is greater than or equal to the next element, increment my erasedElements variable. If that variable is 1, we know we've only removed one element and satisfied the increasing sequence.
bool almostIncreasingSequence(int[] sequence) {
int erasedElements = 0;
for (int i = 0; i < sequence.Length-1; i++)
{
if(sequence[i] >= sequence[i+1])
{
erasedElements += 1;
}
}
Console.Write(erasedElements);
return (erasedElements == 1);
}
All of the following sequences passed:
[1, 3, 2, 1]
[1, 3, 2]
[1, 4, 10, 4, 2]
[10, 1, 2, 3, 4, 5]
[1, 1, 1, 2, 3]
[0, -2, 5, 6]
[1, 1]
I want the console to writeLine false for each ally that does not have a peculiar item(afaerie_charm), and .25 for each ally that is true in the code:
public bool aFaerie_Charm()
{
var allylist = new List<Obj_AI_Hero>();
allylist.AddRange(HeroManager.Allies);
bool z = false;
for (int i = 0; i < allylist.Count; i++) //allylist.count = 4
{
z = Items.HasItem((int)ItemId.Faerie_Charm, allylist[i]);
}
return z;
}
In the above code, z is true if any of the allies in the list(4 allies) have 'faerie_charm' and false otherwise.
Now, in this part of the code: allylist[1] (ally 2) has the peculiar item, so bool returns true, then faerie = .25;, and a2mrs() returns .25, but here's the problem:
the code found that one of the allies has the item and .25, but i have no idea which ally has it, it's not returning 'ally 2 has item, and it's value is .25', that's the problem. Need advice or help please.
public double a2mrs()
{
double faerie = 0;
if (aFaerie_Charm() == true)
{
faerie = .25;
}
return faerie;
}
static void Main(String[] args)
{
Console.WriteLine(a2mrs())
}
output: .25
problem: from the output .25, i only know one of the allies has the item, i dont know which one has the item.
summary: how do i re-work the code so that it prints something like this:
'ally 1 = false'
'ally 2 = .25'
'ally 3 = false'
'ally 4 = false' ?
Your first method returns whether or not the last person in the party has a faerie charm, not whether anyone does. You'll probably need to refactor your methods so that they return the information for each ally. You could do this by changing your first method to
public List<bool> aFaerie_Charm()
{
var allylist = new List<Obj_AI_Hero>();
allylist.AddRange(HeroManager.Allies);
List<bool> z = new List<bool>();
for (int i = 0; i < allylist.Count; i++) //allylist.count = 4
{
z.Add(Items.HasItem((int)ItemId.Faerie_Charm, allylist[i]));
}
return z;
}
Then the print:
public void printHasFaerieCharm()
{
List<bool> charms = aFaerieCharm();
for (int i = 0; i < charms.Count; ++i)
{
Console.WriteLine("Ally " + (i + 1) + ": " + (charms[i] ? ".25" : "false"));
}
}
However, I would recommend you adjust the first method so it takes a parameter of the item you want to check for and uses that instead, that way you can reuse it when you want the same information for other items. You could do something similar with the second method to change what it prints when the item is found (and which item to find), or even add that information to the item's class (so it would be called as something like printWhoHasItem(ItemId.FaerieCharm) and look something like "ally i: " + ItemId.Message).
Well this code is wrong in every aspect. You do realize that result of aFaerie_Charm() is always the result of check for last ally from array?
First of all can't mix booleans with doubles on that stage of code. You need to decide on one of them (based on your use case) and interpret it when it's needed.
You need either return Dictionary from aFaerie_Charm() and in that dictionary you'll add KeyValuePair for each ally in your array, where Key is name of that ally and Boolean is either true or false. Or alternatively you can make aFaerie_Charm accept input parameter of ally and return boolean for that particular ally.
And at the end, you loop through that Dictionary and interpret that boolean anyway you want for display purpose.
In Linq you'll do something like that:
// Create dictionary, keys are names of allies, values says if that particular ally has "charm" or not
Dictionary<String, Boolean> allyData = (from x in HeroManager.Allies select new { Name = x.Name, HasCharm = Items.HasItem((int)ItemId.Faerie_Charm, x) }).ToDictionary(y => y.Name, z => z.HasCharm);
And now displaying:
// Loop through dictionary
foreach (KeyValuePair<String, Boolean> a in allyData)
{
// Display ally's name and if that ally has "charm" then print ".25" and print "false" otherwise
Console.WriteLine("%s = %s", a.Key, (a.Value ? ".25" : "false"));
}
You can add one more line to your method like this:
public bool aFaerie_Charm()
{
var allylist = new List<Obj_AI_Hero>();
allylist.AddRange(HeroManager.Allies);
bool z = false;
for (int i = 0; i < allylist.Count; i++) //allylist.count = 4
{
z = Items.HasItem((int)ItemId.Faerie_Charm, allylist[i]);
//You got 'z' now print a line
Console.WriteLine("Ally " + (i + 1) + ": " + z ? ".25" : "false");
}
return z;
}
Well, You can do this simple thing
In aFaerie_Charm method instead of bool return int
On that aFaerie_Charm method in the if block if HasItem returns true then you return index (i) value or else return -1.
Now in your a2mrs method check it is >0 or less than 0
So if > 0 then you have the index value so you can use that.
However in aFaerie_Charm method guess you need one break statement in for loop. Please check
The task is to keep an array of objects untouched if input is null and, otherwise, remove the elements that are on positions specified by the input. I've got it working but I'm vastly dissatisfied with the code quality.
List<Stuff> stuff = new List<Stuff>{ new Stuff(1), new Stuff(2), new Stuff(3) };
String input = "5";
if(input == null)
return stuff;
int mask = Int32.Parse(input);
for (int i = stuff.Count - 1; i >= 0; i--)
if ((mask & (int)Math.Pow(2, i)) == 0)
stuff.RemoveAt(i);
return stuff;
The actual obtaining input and the fact that e.g. String.Empty will cause problems need not to be regarded. Let's assume that those are handled.
How can I make the code more efficient?
How can I make the syntax more compact and graspable?
Instead of the backwards running loop, you could use Linq with the following statement.
stuff = stuff.Where( (iStuff, idx) => (mask & (int)Math.Pow(2, idx)) != 0 );
Or even cooler using bitwise shit.
stuff = stuff.Where((_, index) => (mask >> index & 1) == 1);
It uses an overload of Where which can access the position in the sequence, as documented here. For a similar task, there is also an overload of Select which gives access to the index, as documented here.
Untested, but you could make an extension method that iterates the collection and filters, returning matching elements as it goes. Repeatedly bit-shifting the mask and checking the 0th bit seems the easiest to follow - for me at least.
static IEnumerable<T> TakeMaskedItemsByIndex(this IEnumerable<T> collection, ulong mask)
{
foreach (T item in collection)
{
if((mask & 1) == 1)
yield return item;
mask = mask >> 1;
}
}
I'm calculating intersection of 2 sets of sorted numbers in a time-critical part of my application. This calculation is the biggest bottleneck of the whole application so I need to speed it up.
I've tried a bunch of simple options and am currently using this:
foreach (var index in firstSet)
{
if (secondSet.BinarySearch(index) < 0)
continue;
//do stuff
}
Both firstSet and secondSet are of type List.
I've also tried using LINQ:
var intersection = firstSet.Where(t => secondSet.BinarySearch(t) >= 0).ToList();
and then looping through intersection.
But as both of these sets are sorted I feel there's a better way to do it. Note that I can't remove items from sets to make them smaller. Both sets usually consist of about 50 items each.
Please help me guys as I don't have a lot of time to get this thing done. Thanks.
NOTE: I'm doing this about 5.3 million times. So every microsecond counts.
If you have two sets which are both sorted, you can implement a faster intersection than anything provided out of the box with LINQ.
Basically, keep two IEnumerator<T> cursors open, one for each set. At any point, advance whichever has the smaller value. If they match at any point, advance them both, and so on until you reach the end of either iterator.
The nice thing about this is that you only need to iterate over each set once, and you can do it in O(1) memory.
Here's a sample implementation - untested, but it does compile :) It assumes that both of the incoming sequences are duplicate-free and sorted, both according to the comparer provided (pass in Comparer<T>.Default):
(There's more text at the end of the answer!)
static IEnumerable<T> IntersectSorted<T>(this IEnumerable<T> sequence1,
IEnumerable<T> sequence2,
IComparer<T> comparer)
{
using (var cursor1 = sequence1.GetEnumerator())
using (var cursor2 = sequence2.GetEnumerator())
{
if (!cursor1.MoveNext() || !cursor2.MoveNext())
{
yield break;
}
var value1 = cursor1.Current;
var value2 = cursor2.Current;
while (true)
{
int comparison = comparer.Compare(value1, value2);
if (comparison < 0)
{
if (!cursor1.MoveNext())
{
yield break;
}
value1 = cursor1.Current;
}
else if (comparison > 0)
{
if (!cursor2.MoveNext())
{
yield break;
}
value2 = cursor2.Current;
}
else
{
yield return value1;
if (!cursor1.MoveNext() || !cursor2.MoveNext())
{
yield break;
}
value1 = cursor1.Current;
value2 = cursor2.Current;
}
}
}
}
EDIT: As noted in comments, in some cases you may have one input which is much larger than the other, in which case you could potentially save a lot of time using a binary search for each element from the smaller set within the larger set. This requires random access to the larger set, however (it's just a prerequisite of binary search). You can even make it slightly better than a naive binary search by using the match from the previous result to give a lower bound to the binary search. So suppose you were looking for values 1000, 2000 and 3000 in a set with every integer from 0 to 19,999. In the first iteration, you'd need to look across the whole set - your starting lower/upper indexes would be 0 and 19,999 respectively. After you'd found a match at index 1000, however, the next step (where you're looking for 2000) can start with a lower index of 2000. As you progress, the range in which you need to search gradually narrows. Whether or not this is worth the extra implementation cost or not is a different matter, however.
Since both lists are sorted, you can arrive at the solution by iterating over them at most once (you may also get to skip part of one list, depending on the actual values they contain).
This solution keeps a "pointer" to the part of list we have not yet examined, and compares the first not-examined number of each list between them. If one is smaller than the other, the pointer to the list it belongs to is incremented to point to the next number. If they are equal, the number is added to the intersection result and both pointers are incremented.
var firstCount = firstSet.Count;
var secondCount = secondSet.Count;
int firstIndex = 0, secondIndex = 0;
var intersection = new List<int>();
while (firstIndex < firstCount && secondIndex < secondCount)
{
var comp = firstSet[firstIndex].CompareTo(secondSet[secondIndex]);
if (comp < 0) {
++firstIndex;
}
else if (comp > 0) {
++secondIndex;
}
else {
intersection.Add(firstSet[firstIndex]);
++firstIndex;
++secondIndex;
}
}
The above is a textbook C-style approach of solving this particular problem, and given the simplicity of the code I would be surprised to see a faster solution.
You're using a rather inefficient Linq method for this sort of task, you should opt for Intersect as a starting point.
var intersection = firstSet.Intersect(secondSet);
Try this. If you measure it for performance and still find it unwieldy, cry for further help (or perhaps follow Jon Skeet's approach).
I was using Jon's approach but needed to execute this intersect hundreds of thousands of times for a bulk operation on very large sets and needed more performance. The case I was running in to was heavily imbalanced sizes of the lists (eg 5 and 80,000) and wanted to avoid iterating the entire large list.
I found that detecting the imbalance and changing to an alternate algorithm gave me huge benifits over specific data sets:
public static IEnumerable<T> IntersectSorted<T>(this List<T> sequence1,
List<T> sequence2,
IComparer<T> comparer)
{
List<T> smallList = null;
List<T> largeList = null;
if (sequence1.Count() < Math.Log(sequence2.Count(), 2))
{
smallList = sequence1;
largeList = sequence2;
}
else if (sequence2.Count() < Math.Log(sequence1.Count(), 2))
{
smallList = sequence2;
largeList = sequence1;
}
if (smallList != null)
{
foreach (var item in smallList)
{
if (largeList.BinarySearch(item, comparer) >= 0)
{
yield return item;
}
}
}
else
{
//Use Jon's method
}
}
I am still unsure about the point at which you break even, need to do some more testing
try
firstSet.InterSect (secondSet).ToList ()
or
firstSet.Join(secondSet, o => o, id => id, (o, id) => o)
Seemingly similar questions: "Finding closest number in an array" (in Java) and "find nearest match to array of doubles" (actually a geography problem).
I have a (sorted) array of doubles. Given an arbitrary number (which may or may not be an exact match for one of the array elements), how can I return the index of the number which is the closest match?
For example, using the following array:
1.8
2.4
2.7
3.1
4.5
Querying 2.5 would return with an index of 1, corresponding to the value of 2.4.
Bonus points for detecting values that lie completely outside of the range of the array elements. For example, using the array listed above, your code may decide that 4.6 is in, but 5.9 is out. If you want to try this part of the question, the specifics are in your hands.
Array.BinarySearch, which returns:
The index of the specified value in the specified array, if value is found. If value is not found and value is less than one or more elements in array, a negative number which is the bitwise complement of the index of the first element that is larger than value. If value is not found and value is greater than any of the elements in array, a negative number which is the bitwise complement of (the index of the last element plus 1).
Now that won't get you 100% of the way there, since you'll know the number is either less than or greater than the match, but it really only leaves you with two indices to check.
One way to do this using LINQ is like this:
public int GetClosestIndex( List<double> doublelist, double targetvalue )
{
return doublelist.IndexOf(doublelist.OrderBy(d => Math.Abs(d - targetvalue)).ElementAt(0));
}
It might have some performance issues, but If the list is not that long, it should not pose a problem. Also, if two elements are equally distant from the target value, it will return the first index of those.
Perhaps not the fastest solution, but certainly pleasant eye-candy:
double search;
double[] array;
var nearest = (
from value in array
orderby Math.Abs(value - search)
select value).First();
var index = array.IndexOf(nearest);
Note that this will absolutely be slower than a binary search algorithm, because it need to process each element in the array and sorting means building a hash table of those items.
Something like this:
double[] values = new double[]
{
1.8,
2.4,
2.7,
3.1,
4.5
};
double difference = double.PositiveInfinity;
int index = -1;
double input = 2.5;
for (int i = 0; i < values.Length; i++)
{
double currentDifference = Math.Abs(values[i] - input);
if (currentDifference < difference)
{
difference = currentDifference;
index = i;
}
// Stop searching when we've encountered a value larger
// than the inpt because the values array is sorted.
if (values[i] > input)
break;
}
Console.WriteLine("Found index: {0} value {1}", index, values[index]);
List<int> results;
int target = 0;
int nearestValue = 0;
if (results.Any(ab => ab == target)) {
nearestValue= results.FirstOrDefault<int>(i => i == target);
} else {
int greaterThanTarget = 0;
int lessThanTarget = 0;
if (results.Any(ab => ab > target) {
greaterThanTarget = results.Where<int>(i => i > target).Min();
}
if (results.Any(ab => ab < target)) {
lessThanTarget = results.Where<int>(i => i < target).Max();
}
if (lessThanTarget == 0 ) {
nearestValue= greaterThanTarget;
} else if (greaterThanTarget == 0) {
nearestValue = lessThanTarget;
} else {
if (target - lessThanTarget < greaterThanTarget - target) {
nearestValue = lessThanTarget;
} else {
nearestValue = greaterThanTarget;
}
}
}