Enumerable.Range(0, int.MaxValue)
.Select(n => Math.Pow(n, 2))
.Where(squared => squared % 2 != 0)
.TakeWhile(squared => squared < 10000).Sum()
Will this code iterate over all of the integer values from 0 to max-range or just through the integer values to satisfy the take-while, where, and select operators?
Can somebody clarify?
EDIT: My first try to make sure it works as expected was dumb one. I revoke it :)
int.MaxValue + 5 overflows to be a negative number. Try it yourself:
unchecked
{
int count = int.MaxValue + 5;
Console.WriteLine(count); // Prints -2147483644
}
The second argument for Enumerable.Range has to be non-negative - hence the exception.
You can certainly use infinite sequences in LINQ though. Here's an example of such a sequence:
public IEnumerable<int> InfiniteCounter()
{
int counter = 0;
while (true)
{
unchecked
{
yield return counter;
counter++;
}
}
}
That will overflow as well, of course, but it'll keep going...
Note that some LINQ operators (e.g. Reverse) need to read all the data before they can yield their first result. Others (like Select) can just keep streaming results as they read them from the input. See my Edulinq blog posts for details of the behaviour of each operator (in LINQ to Objects).
The way to solve these sort of questions in general, is to think about what's going on in steps.
Linq turns the linq code into something that'll be executed by query provider. This could be something like producing SQL code, or all manner of things. In the case of linq-to-objects, it produces some equivalent .NET code. Thinking about what that .NET code will be lets us reason about what will happen.*
With your code you have:
Enumerable.Range(0, int.MaxValue)
.Select(n => Math.Pow(n, 2))
.Where(squared => squared % 2 != 0)
.TakeWhile(squared => squared < 10000).Sum()
Enumerable.Range is slightly more complicated than:
for(int i = start; i != start + count; ++i)
yield return i;
...but that's close enough for argument's sake.
Select is close enough to:
foreach(T item in source)
yield return func(item);
Where is close enough to:
foreach(T item in source)
if(func(item))
yield return item;
TakeWhile is close enough to:
foreach(T item in source)
if(func(item))
yield return item;
else
yield break;
Sum is close enough to:
T tmp = 0;//must be numeric type
foreach(T x in source)
tmp += x;
return tmp;
This simplifies a few optimisations and so on, but is close enough to reason with. Taking each of these in turn, your code is equivalent to:
double ret = 0; // part of equivalent of sum
for(int i = 0; i != int.MaxValue; ++i) // equivalent of Range
{
double j = Math.Pow(i, 2); // equivalent of Select(n => Math.Pow(n, 2))
if(j % 2 != 0) //equivalent of Where(squared => squared %2 != 0)
{
if(j < 10000) //equivalent of TakeWhile(squared => squared < 10000)
{
ret += j; //equaivalent of Sum()
}
else //TakeWhile stopping further iteration
{
break;
}
}
}
return ret; //end of equivalent of Sum()
Now, in some ways the code above is simpler, and in some ways it's more complicated. The whole point of using LINQ is that in many ways its simpler. Still, to answer your question "Will this code iterate over all of the integer values from 0 to max-range or just through the integer values to satisfy the take-while, where, and select operators?" we can look at the above and see that those that don't satisfy the where are iterated through to find that they don't satisfy the where, but no more work is done with them, and once the TakeWhile is satisfied, all further work is stopped (the break in my non-LINQ re-write).
Of course it's only the TakeWhile() in this case that means the call will return in a reasonable length of time, but we also need to think briefly about the others to make sure they yield as they go. Consider the following variant of your code:
Enumerable.Range(0, int.MaxValue)
.Select(n => Math.Pow(n, 2))
.Where(squared => squared % 2 != 0)
.ToList()
.TakeWhile(squared => squared < 10000).Sum()
Theoretically, this will give exactly the same answer, but it will take far longer and far more memory to do so (probably enough to cause an out of memory exception). The equivalent non-linq code here though is:
List<double> tmpList = new List<double>(); // part of ToList equivalent
for(int i = 0; i != int.MaxValue; ++i) // equivalent of Range
{
double j = Math.Pow(i, 2); // equivalent of Select(n => Math.Pow(n, 2))
if(j % 2 != 0) //equivalent of Where(squared => squared %2 != 0)
{
tmpList.Add(j);//part of equivalent to ToList()
}
}
double ret = 0; // part of equivalent of sum
foreach(double k in tmpList)
{
if(k < 10000) //equivalent of TakeWhile(squared => squared < 10000)
{
ret += k; //equaivalent of Sum()
}
else //TakeWhile stopping further iteration
{
break;
}
}
return ret; //end of equivalent of Sum()
Here we can see how adding ToList() to the Linq query vastly affects the query so that every item produced by the Range() call must be dealt with. Methods like ToList() and ToArray() break up the chaining so that non-linq equivalents no longer fit "inside" each other and none can therefore stop the operation of those that come before. (Sum() is another example, but since it's after your TakeWhile() in your example, that isn't an issue).
Another thing that would make it go through every iteration of the range is if you had While(x => false) because it would never actually perform the test in TakeWhile.
*Though there may be further optimisations, esp in the case of SQL code and also while conceptually e.g. Count() is equivalent to:
int c = 0;
foreach(item in src)
++c;
return c;
That this will be turned into a call to the Count property of an ICollection or the Length property of an array means the O(n) above is replaced by an O(1) (for most ICollection implementations) call, which is a massive gain for large sequences.
Your first code will only iterate as long the TakeWhile condition is met. It will not iterate until int.MaxValue.
int.MaxValue + 5 will result in a negative integer. Enumerable.Range throws an ArgumentOutOfRangeException if its second argument is negative. So that's why you get the exception (before any iteration takes place).
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]
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 was adapting a simple prime-number generation one-liner from Scala to C# (mentioned in a comment on this blog by its author). I came up with the following:
int NextPrime(int from)
{
while(true)
{
n++;
if (!Enumerable.Range(2, (int)Math.Sqrt(n) - 1).Any((i) => n % i == 0))
return n;
}
}
It works, returning the same results I'd get from running the code referenced in the blog. In fact, it works fairly quickly. In LinqPad, it generated the 100,000th prime in about 1 second. Out of curiosity, I rewrote it without Enumerable.Range() and Any():
int NextPrimeB(int from)
{
while(true)
{
n++;
bool hasFactor = false;
for (int i = 2; i <= (int)Math.Sqrt(n); i++)
{
if (n % i == 0) hasFactor = true;
}
if (!hasFactor) return n;
}
}
Intuitively, I'd expect them to either run at the same speed, or even for the latter to run a little faster. In actuality, computing the same value (100,000th prime) with the second method, takes 12 seconds - It's a staggering difference.
So what's going on here? There must be fundamentally something extra happening in the second approach that's eating up CPU cycles, or some optimization going on the background of the Linq examples. Anybody know why?
For every iteration of the for loop, you are finding the square root of n. Cache it instead.
int root = (int)Math.Sqrt(n);
for (int i = 2; i <= root; i++)
And as other have mentioned, break the for loop as soon as you find a factor.
The LINQ version short circuits, your loop does not. By this I mean that when you have determined that a particular integer is in fact a factor the LINQ code stops, returns it, and then moves on. Your code keeps looping until it's done.
If you change the for to include that short circuit, you should see similar performance:
int NextPrimeB(int from)
{
while(true)
{
n++;
for (int i = 2; i <= (int)Math.Sqrt(n); i++)
{
if (n % i == 0) return n;;
}
}
}
It looks like this is the culprit:
for (int i = 2; i <= (int)Math.Sqrt(n); i++)
{
if (n % i == 0) hasFactor = true;
}
You should exit the loop once you find a factor:
if (n % i == 0){
hasFactor = true;
break;
}
And as other have pointed out, move the Math.Sqrt call outside the loop to avoid calling it each cycle.
Enumerable.Any takes an early out if the condition is successful while your loop does not.
The enumeration of source is stopped as soon as the result can be determined.
This is an example of a bad benchmark. Try modifying your loop and see the difference:
if (n % i == 0) { hasFactor = true; break; }
}
throw new InvalidOperationException("Cannot satisfy criteria.");
In the name of optimization, you can be a little more clever about this by avoiding even numbers after 2:
if (n % 2 != 0)
{
int quux = (int)Math.Sqrt(n);
for (int i = 3; i <= quux; i += 2)
{
if (n % i == 0) return n;
}
}
There are some other ways to optimize prime searches, but this is one of the easier to do and has a large payoff.
Edit: you may want to consider using (int)Math.Sqrt(n) + 1. FP functions + round-down could potentially cause you to miss a square of a large prime number.
At least part of the problem is the number of times Math.Sqrt is executed. In the LINQ query this is executed once but in the loop example it's executed N times. Try pulling that out into a local and reprofiling the application. That will give you a more representative break down
int limit = (int)Math.Sqrt(n);
for (int i = 2; i <= limit; i++)
I was wondering if it is possible to find the largest prime factor of a number by using modulos in C#. In other words, if i % x == 0 then we could break a for loop or something like that, where x is equal to all natural numbers below our i value.
How would I specify the all natural numbers below our i value as equal to our x variable? It becomes a little tedious to write out conditionals for every single integer if you know what I'm saying.
By the way, I'm sure there is a far easier way to do this in C#, so please let me know about it if you have an idea, but I'd also like to try and solve it this way, just to see if I can do it with my beginner knowledge.
Here is my current code if you want to see what I have so far:
static void Main()
{
int largestPrimeFactor = 0;
for (long i = 98739853; i <= 98739853; i--)
{
if (true)
{
largestPrimeFactor += (int) i;
break;
}
}
Console.WriteLine(largestPrimeFactor);
Console.ReadLine();
}
If I were to do this using loop and modulos I would do:
long number = 98739853;
long biggestdiv = number;
while(number%2==0) //get rid of even numbers
number/=2;
long divisor = 3;
if(number!=1)
while(divisor!=number)
{
while(number%divisor==0)
{
number/=divisor;
biggestdiv = divisor;
}
divisor+=2;
}
In the end, biggestdiv would be the largest prime factor.
Note: This code is written directly in browser. I didn't try to compile or run it. This is only for showing my concept. There might be algorithm mistakes. It they are, let me know. I'm aware of the fact that it is not optimized at all (I think Sieve is the best for this).
EDIT:
fixed: previous code would return 1 when number were prime.
fixed: previous code would end in loop leading to overflow of divisor where number were power of 2
Ooh, this sounds like a fun use for iterator blocks. Don't turn this in to your professor, though:
private static List<int> primes = new List<int>() {2};
public static IEnumerable<int> Primes()
{
int p;
foreach(int i in primes) {p = i; yield return p;}
while (p < int.MaxValue)
{
p++;
if (!primes.Any(i => p % i ==0))
{
primes.Add(p);
yield return p;
}
}
}
public int LargestPrimeFactor(int n)
{
return Primes.TakeWhile(p => p <= Math.Sqrt(n)).Where(p => n % p == 0).Last();
}
I'm not sure quite what your question is: perhaps you need a loop over the numbers? However there are two clear problems with your code:
Your for loop has the same stop and end value. Ie it will run once and once only
You have a break before the largestPrimeFactor sum. This sum will NEVER execute, because break will stop the for loop ( and hence execution of that block). The compiler should be giving a warning that this sum is unreachable.
Let's say the first N integers divisible by 3 starting with 9.
I'm sure there is some one line solution using lambdas, I just don't know it that area of the language well enough yet.
Just to be different (and to avoid using a where statement) you could also do:
var numbers = Enumerable.Range(0, n).Select(i => i * 3 + 9);
Update This also has the benefit of not running out of numbers.
Using Linq:
int[] numbers =
Enumerable.Range(9,10000)
.Where(x => x % 3 == 0)
.Take(20)
.ToArray();
Also easily parallelizeable using PLinq if you need:
int[] numbers =
Enumerable.Range(9,10000)
.AsParallel() //added this line
.Where(x => x % 3 == 0)
.Take(20)
.ToArray();
const int __N = 100;
const int __start = 9;
const int __divisibleBy = 3;
var array = Enumerable.Range(__start, __N * __divisibleBy).Where(x => x % __divisibleBy == 0).Take(__N).ToArray();
int n = 10; // Take first 10 that meet criteria
int[] ia = Enumerable
.Range(0,999)
.Where(a => a % 3 == 0 && a.ToString()[0] == '9')
.Take(n)
.ToArray();
I want to see how this solution stacks up to the above Linq solutions. The trick here is modifying the predicate using the fact that the set of (q % m) starting from s is (s + (s % m) + m*n) (where n represent's the nth value in the set). In our case s=q.
The only problem with this solution is that it has the side effect of making your implementation depend on the specific pattern you choose (and not all patterns have a suitable predicate). But it has the advantage of:
Always running in exactly n iterations
Never failing like the above proposed solutions (wrt to the limited Range).
Besides, no matter what pattern you choose, you will always need to modify the predicate, so you might as well make it mathematically efficient:
static int[] givemeN(int n)
{
const int baseVal = 9;
const int modVal = 3;
int i = 0;
return Array.ConvertAll<int, int>(
new int[n],
new Converter<int, int>(
x => baseVal + (baseVal % modVal) +
((i++) * modVal)
));
}
edit: I just want to illustrate how you could use this method with a delegate to improve code re-use:
static int[] givemeN(int n, Func<int, int> func)
{
int i = 0;
return Array.ConvertAll<int, int>(new int[n], new Converter<int, int>(a => func(i++)));
}
You can use it with givemeN(5, i => 9 + 3 * i). Again note that I modified the predicate, but you can do this with most simple patterns too.
I can't say this is any good, I'm not a C# expert and I just whacked it out, but I think it's probably a canonical example of the use of yield.
internal IEnumerable Answer(N)
{
int n=0;
int i=9;
while (true)
{
if (i % 3 == 0)
{
n++;
yield return i;
}
if (n>=N) return;
i++;
}
}
You have to iterate through 0 or 1 to N and add them by hand. Or, you could just create your function f(int n), and in that function, you cache the results inside session or a global hashtable or dictionary.
Pseudocode, where ht is a global Hashtable or Dictionary (strongly recommend the later, because it is strongly typed.
public int f(int n)
{
if(ht[n].containsValue)
return ht[n];
else
{
//do calculation
ht[n] = result;
return result;
}
}
Just a side note. If you do this type of functional programming all the time, you might want to check out F#, or maybe even Iron Ruby or Python.