I am trying to place currency trades that match an exact rate on a market that only accepts integral bid/offer amounts. I want to make the largest trade possible at a specific rate. This is a toy program, not a real trading bot, so I am using C#.
I need an algorithm that returns an answer in a reasonable amount of time even when the numerator and denominator can be large (100000+).
static bool CalcBiggestRationalFraction(float target_real, float epsilon, int numerator_max, int denominator_max, out int numerator, out int denominator)
{
// target_real is the ratio we are tryig to achieve in our output fraction (numerator / denominator)
// epsilon is the largest difference abs(target_real - (numerator / denominator)) we are willing to tolerate in the answer
// numerator_max, denominator_max are the upper bounds on the numerator and the denominator in the answer
//
// in the case where there are multiple answers, we want to return the largest one
//
// in the case where an answer is found that is within epsilon, we return true and the answer.
// in the case where an answer is not found that is within epsilon, we return false and the closest answer that we have found.
//
// ex: CalcBiggestRationalFraction(.5, .001, 4, 4, num, denom) returns (2/4) instead of (1/2).
}
I asked a previous question that is similar (http://stackoverflow.com/questions/4385580/finding-the-closest-integer-fraction-to-a-given-random-real) before I thought about what I was actually trying to accomplish and it turns out that I am trying to solve a different, but related problem.
The canonical way to solve your problem is with continued fraction expansion. In particular, see this section.
If you want the unreduced fraction, then here's one optimization you can do: Since you'll never be interested in n/2, because you want 2n/4, 4n/8, or 1024n/2048, we only need to check some of the numbers. As soon as we check any multiple of 2, we never need to check 2. Therefore, I believe you can try denominators denominator_max through denominator_max/2, and you'll have implicitly checked all of the factors of those numbers, which would be everything 2 through denominator_max/2.
I'm not at a compiler at the moment, so I haven't checked this code for correctness, or even that it compiles, but it should be close.
static bool CalcBiggestRationalFraction(float target_real, float epsilon,
int numerator_max, int denominator_max,
out int numerator, out int denominator)
{
if((int)Math.Round(target_real * denominator_max) > numerator_max)
{
// We were given values that don't match up.
// For example, target real = 0.5, but max_num / max_den = 0.3
denominator_max = (int)(numerator_max / target_real);
}
float bestEpsilon = float.MAX_VALUE;
for(int den = denominator_max; den >= denominator_max/2, den--)
{
int num = (int)Math.Round(target_real * den);
float thisEpsilon = Math.abs(((float)num / den) - target_real);
if(thisEpsilon < bestEpsilon)
{
numerator = num;
denominator = den;
bestEpsilon = thisEpsilon;
}
}
return bestEpsilon < epsilon;
}
Let's try this:
First, we need to turn the float into a fraction. Easiest way I can think to do this is to find the order of magnitude of the epsilon, multiply the float by that order, and truncate to get the numerator.
long orderOfMagnitude = 1
while(epsilon * orderOfMagnitude <1)
orderOfMagnitude *= 10;
numerator = (int)(target_real*orderOfMagnitude);
denominator = orderOfMagnitude;
//sanity check; if the initial fraction isn't within the epsilon, then add sig figs until it is
while(target_real - (float)numerator / denominator > epsilon)
{
orderOfMagnitude *= 10;
numerator = (int)(target_real*orderOfMagnitude);
denominator = orderOfMagnitude;
}
Now, we can break the fraction down into least terms. The most efficient way I know of is to attempt to divide by all prime numbers less than or equal to the square root of the smaller of the numerator and denominator.
var primes = new List<int>{2,3,5,7,11,13,17,19,23}; //to start us off
var i = 0;
while (true)
{
if(Math.Sqrt(numerator) < primes[i] || Math.Sqrt(denominator) < primes[i]) break;
if(numerator % primes[i] == 0 && denominator % primes[i] == 0)
{
numerator /= primes[i];
denominator /= primes[i];
i=0;
}
else
{
i++;
if(i > primes.Count)
{
//Find the next prime number by looking for the first number not divisible
//by any prime < sqrt(number).
//We are actually unlikely to have to use this, because the denominator
//is a power of 10, so its prime factorization will be 2^x*5^x
var next = primes.Last() + 2;
bool add;
do
{
add = true;
for(var x=0; primes[x] <= Math.Sqrt(next); x++)
if(next % primes[x] == 0)
{
add = false;
break;
}
if(add)
primes.Add(next);
else
next+=2;
} while(!add);
}
}
}
Related
I need to divide a variable distance in a very specific way. The spacing for the divisions must be 40 units minimum, and 80 units maximum.
I've tried several different various of this code but I am struggling to wrap my head around how to include the min/max variable in my division.
double totaldist = X;
double division = totaldist / 80;
double roundup = Math.Ceiling(division);
double space = totaldist / roundup;
double increment = 0;
while (increment < totaldist)
{
increment = increment + space;
}
The attached code is obviously short of what I want to accomplish, I'm not sure how to bridge the gap. Thank you
So all you have to do is loop over all the possible divisors and pick the best one. The simplest way to accomplish this is as follows:
public static int remainder(int totalDist)
{
double minRemainder = (totalDist % 40) / 40;
int bestDivision = 40;
for (var i = 40; i <= 80; i++)
{
double cRemainder = (totalDist % i) / i;
if (totalDist % i == 0) return i;
else if (cRemainder < minRemainder) { minRemainder = cRemainder; bestDivision = i; }
}
return bestDivision;
}
This will always return the closest result. Even if there is no real solution, it will still provide an approximate answer as a fallback.
I'd test every divisor for mod 0 (no remainder)
int d = 420;
int s = 40;
for(; s <= 80; s++){
if(d%s==0)
break;
}
if(s==81)
Console.Write("There is no suitable divisor");
else
Console.Write($"{d} divides into {s} segments of {d/s} with no remainder");
If you want to minimise the segment length (greater number of segments) start at 80 and work towards 40 in the loop instead - set your d to 480, start at 80 and you should get "80 segments of length 6" rather than "40 segments of length 12"
You can even get cute with your loop and have no body:
for(; s <= 80 && d%s > 0; s++){ }
But it's not quite so readable/self explanatory
All numbers that divide evenly into x.
I put in 4 it returns: 4, 2, 1
edit: I know it sounds homeworky. I'm writing a little app to populate some product tables with semi random test data. Two of the properties are ItemMaximum and Item Multiplier. I need to make sure that the multiplier does not create an illogical situation where buying 1 more item would put the order over the maximum allowed. Thus the factors will give a list of valid values for my test data.
edit++:
This is what I went with after all the help from everyone. Thanks again!
edit#: I wrote 3 different versions to see which I liked better and tested them against factoring small numbers and very large numbers. I'll paste the results.
static IEnumerable<int> GetFactors2(int n)
{
return from a in Enumerable.Range(1, n)
where n % a == 0
select a;
}
private IEnumerable<int> GetFactors3(int x)
{
for (int factor = 1; factor * factor <= x; factor++)
{
if (x % factor == 0)
{
yield return factor;
if (factor * factor != x)
yield return x / factor;
}
}
}
private IEnumerable<int> GetFactors1(int x)
{
int max = (int)Math.Ceiling(Math.Sqrt(x));
for (int factor = 1; factor < max; factor++)
{
if(x % factor == 0)
{
yield return factor;
if(factor != max)
yield return x / factor;
}
}
}
In ticks.
When factoring the number 20, 5 times each:
GetFactors1-5,445,881
GetFactors2-4,308,234
GetFactors3-2,913,659
When factoring the number 20000, 5 times each:
GetFactors1-5,644,457
GetFactors2-12,117,938
GetFactors3-3,108,182
pseudocode:
Loop from 1 to the square root of the number, call the index "i".
if number mod i is 0, add i and number / i to the list of factors.
realocode:
public List<int> Factor(int number)
{
var factors = new List<int>();
int max = (int)Math.Sqrt(number); // Round down
for (int factor = 1; factor <= max; ++factor) // Test from 1 to the square root, or the int below it, inclusive.
{
if (number % factor == 0)
{
factors.Add(factor);
if (factor != number/factor) // Don't add the square root twice! Thanks Jon
factors.Add(number/factor);
}
}
return factors;
}
As Jon Skeet mentioned, you could implement this as an IEnumerable<int> as well - use yield instead of adding to a list. The advantage with List<int> is that it could be sorted before return if required. Then again, you could get a sorted enumerator with a hybrid approach, yielding the first factor and storing the second one in each iteration of the loop, then yielding each value that was stored in reverse order.
You will also want to do something to handle the case where a negative number passed into the function.
The % (remainder) operator is the one to use here. If x % y == 0 then x is divisible by y. (Assuming 0 < y <= x)
I'd personally implement this as a method returning an IEnumerable<int> using an iterator block.
Very late but the accepted answer (a while back) didn't not give the correct results.
Thanks to Merlyn, I got now got the reason for the square as a 'max' below the corrected sample. althought the answer from Echostorm seems more complete.
public static IEnumerable<uint> GetFactors(uint x)
{
for (uint i = 1; i * i <= x; i++)
{
if (x % i == 0)
{
yield return i;
if (i != x / i)
yield return x / i;
}
}
}
As extension methods:
public static bool Divides(this int potentialFactor, int i)
{
return i % potentialFactor == 0;
}
public static IEnumerable<int> Factors(this int i)
{
return from potentialFactor in Enumerable.Range(1, i)
where potentialFactor.Divides(i)
select potentialFactor;
}
Here's an example of usage:
foreach (int i in 4.Factors())
{
Console.WriteLine(i);
}
Note that I have optimized for clarity, not for performance. For large values of i this algorithm can take a long time.
Another LINQ style and tying to keep the O(sqrt(n)) complexity
static IEnumerable<int> GetFactors(int n)
{
Debug.Assert(n >= 1);
var pairList = from i in Enumerable.Range(1, (int)(Math.Round(Math.Sqrt(n) + 1)))
where n % i == 0
select new { A = i, B = n / i };
foreach(var pair in pairList)
{
yield return pair.A;
yield return pair.B;
}
}
Here it is again, only counting to the square root, as others mentioned. I suppose that people are attracted to that idea if you're hoping to improve performance. I'd rather write elegant code first, and optimize for performance later, after testing my software.
Still, for reference, here it is:
public static bool Divides(this int potentialFactor, int i)
{
return i % potentialFactor == 0;
}
public static IEnumerable<int> Factors(this int i)
{
foreach (int result in from potentialFactor in Enumerable.Range(1, (int)Math.Sqrt(i))
where potentialFactor.Divides(i)
select potentialFactor)
{
yield return result;
if (i / result != result)
{
yield return i / result;
}
}
}
Not only is the result considerably less readable, but the factors come out of order this way, too.
I did it the lazy way. I don't know much, but I've been told that simplicity can sometimes imply elegance. This is one possible way to do it:
public static IEnumerable<int> GetDivisors(int number)
{
var searched = Enumerable.Range(1, number)
.Where((x) => number % x == 0)
.Select(x => number / x);
foreach (var s in searched)
yield return s;
}
EDIT: As Kraang Prime pointed out, this function cannot exceed the limit of an integer and is (admittedly) not the most efficient way to handle this problem.
Wouldn't it also make sense to start at 2 and head towards an upper limit value that's continuously being recalculated based on the number you've just checked? See N/i (where N is the Number you're trying to find the factor of and i is the current number to check...) Ideally, instead of mod, you would use a divide function that returns N/i as well as any remainder it might have. That way you're performing one divide operation to recreate your upper bound as well as the remainder you'll check for even division.
Math.DivRem
http://msdn.microsoft.com/en-us/library/wwc1t3y1.aspx
If you use doubles, the following works: use a for loop iterating from 1 up to the number you want to factor. In each iteration, divide the number to be factored by i. If (number / i) % 1 == 0, then i is a factor, as is the quotient of number / i. Put one or both of these in a list, and you have all of the factors.
And one more solution. Not sure if it has any advantages other than being readable..:
List<int> GetFactors(int n)
{
var f = new List<int>() { 1 }; // adding trivial factor, optional
int m = n;
int i = 2;
while (m > 1)
{
if (m % i == 0)
{
f.Add(i);
m /= i;
}
else i++;
}
// f.Add(n); // adding trivial factor, optional
return f;
}
I came here just looking for a solution to this problem for myself. After examining the previous replies I figured it would be fair to toss out an answer of my own even if I might be a bit late to the party.
The maximum number of factors of a number will be no more than one half of that number.There is no need to deal with floating point values or transcendent operations like a square root. Additionally finding one factor of a number automatically finds another. Just find one and you can return both by just dividing the original number by the found one.
I doubt I'll need to use checks for my own implementation but I'm including them just for completeness (at least partially).
public static IEnumerable<int>Factors(int Num)
{
int ToFactor = Num;
if(ToFactor == 0)
{ // Zero has only itself and one as factors but this can't be discovered through division
// obviously.
yield return 0;
return 1;
}
if(ToFactor < 0)
{// Negative numbers are simply being treated here as just adding -1 to the list of possible
// factors. In practice it can be argued that the factors of a number can be both positive
// and negative, i.e. 4 factors into the following pairings of factors:
// (-4, -1), (-2, -2), (1, 4), (2, 2) but normally when you factor numbers you are only
// asking for the positive factors. By adding a -1 to the list it allows flagging the
// series as originating with a negative value and the implementer can use that
// information as needed.
ToFactor = -ToFactor;
yield return -1;
}
int FactorLimit = ToFactor / 2; // A good compiler may do this optimization already.
// It's here just in case;
for(int PossibleFactor = 1; PossibleFactor <= FactorLimit; PossibleFactor++)
{
if(ToFactor % PossibleFactor == 0)
{
yield return PossibleFactor;
yield return ToFactor / PossibleFactor;
}
}
}
Program to get prime factors of whole numbers in javascript code.
function getFactors(num1){
var factors = [];
var divider = 2;
while(num1 != 1){
if(num1 % divider == 0){
num1 = num1 / divider;
factors.push(divider);
}
else{
divider++;
}
}
console.log(factors);
return factors;
}
getFactors(20);
In fact we don't have to check for factors not to be square root in each iteration from the accepted answer proposed by chris fixed by Jon, which could slow down the method when the integer is large by adding an unnecessary Boolean check and a division. Just keep the max as double (don't cast it to an int) and change to loop to be exclusive not inclusive.
private static List<int> Factor(int number)
{
var factors = new List<int>();
var max = Math.Sqrt(number); // (store in double not an int) - Round down
if (max % 1 == 0)
factors.Add((int)max);
for (int factor = 1; factor < max; ++factor) // (Exclusice) - Test from 1 to the square root, or the int below it, inclusive.
{
if (number % factor == 0)
{
factors.Add(factor);
//if (factor != number / factor) // (Don't need check anymore) - Don't add the square root twice! Thanks Jon
factors.Add(number / factor);
}
}
return factors;
}
Usage
Factor(16)
// 4 1 16 2 8
Factor(20)
//1 20 2 10 4 5
And this is the extension version of the method for int type:
public static class IntExtensions
{
public static IEnumerable<int> Factors(this int value)
{
// Return 2 obvious factors
yield return 1;
yield return value;
// Return square root if number is prefect square
var max = Math.Sqrt(value);
if (max % 1 == 0)
yield return (int)max;
// Return rest of the factors
for (int i = 2; i < max; i++)
{
if (value % i == 0)
{
yield return i;
yield return value / i;
}
}
}
}
Usage
16.Factors()
// 4 1 16 2 8
20.Factors()
//1 20 2 10 4 5
Linq solution:
IEnumerable<int> GetFactors(int n)
{
Debug.Assert(n >= 1);
return from i in Enumerable.Range(1, n)
where n % i == 0
select i;
}
I need to truncate a number to 2 decimal places, which basically means
chopping off the extra digits.
Eg:
2.919 -> 2.91
2.91111 -> 2.91
Why? This is what SQL server is doing when storing a number of a
particular precision. Eg, if a column is Decimal(8,2), and you try to
insert/update a number of 9.1234, the 3 and 4 will be chopped off.
I need to do exactly the same thing in c# code.
The only possible ways that I can think of doing it are either:
Using the stringformatter to "print" it out only
two decimal places, and then converting it to a decimal,
eg:
decimal tooManyDigits = 2.1345
decimal ShorterDigits = Convert.ToDecimal(tooManyDigits.ToString("0.##"));
// ShorterDigits is now 2.13
I'm not happy with this because it involves a to-string and then
another string to decimal conversion which seems a bit mad.
Using Math.Truncate (which only accepts an integer), so I
can multiply it by 100, truncate it, then divide by 100. eg:
decimal tooLongDecimal = 2.1235;
tooLongDecimal = Math.Truncate(tooLongDecimal * 100) / 100;
I'm also not happy with this because if tooLongDecimal is 0,
I'll get a divide by 0 error.
Surely there's a better + easier way! Any suggestions?
You've answered the question yourself; it seems you just misunderstood what division by zero means. The correct way to do this is to multiply, truncate, then devide, like this:
decimal TruncateTo100ths(decimal d)
{
return Math.Truncate(d* 100) / 100;
}
TruncateTo100ths(0m); // 0
TruncateTo100ths(2.919m); // 2.91
TruncateTo100ths(2.91111m); // 2.91
TruncateTo100ths(2.1345m); // 2.13
There is no division by zero here, there is only division by 100, which is perfectly safe.
The previously offered mathematical solutions are vulnerable to overflow with large numbers and/or a large number of decimal places. Consider instead the following extension method:
public static decimal TruncateDecimal(this decimal d, int decimals)
{
if (decimals < 0)
throw new ArgumentOutOfRangeException("decimals", "Value must be in range 0-28.");
else if (decimals > 28)
throw new ArgumentOutOfRangeException("decimals", "Value must be in range 0-28.");
else if (decimals == 0)
return Math.Truncate(d);
else
{
decimal integerPart = Math.Truncate(d);
decimal scalingFactor = d - integerPart;
decimal multiplier = (decimal) Math.Pow(10, decimals);
scalingFactor = Math.Truncate(scalingFactor * multiplier) / multiplier;
return integerPart + scalingFactor;
}
}
Usage:
decimal value = 18446744073709551615.262626263m;
value = value.TruncateDecimal(6); // Result: 18446744073709551615.262626
I agree with p.s.w.g. I had the similar requirement and here is my experience and a more generalized function for truncating.
http://snathani.blogspot.com/2014/05/truncating-number-to-specificnumber-of.html
public static decimal Truncate(decimal value, int decimals)
{
decimal factor = (decimal)Math.Pow(10, decimals);
decimal result = Math.Truncate(factor * value) / factor;
return result;
}
Using decimal.ToString('0.##') also imposes rounding:
1.119M.ToString("0.##") // -> 1.12
(Yeah, likely should be a comment, but it's hard to format well as such.)
public static decimal Rounding(decimal val, int precision)
{
decimal res = Trancating(val, precision + 1);
return Math.Round(res, precision, MidpointRounding.AwayFromZero);
}
public static decimal Trancating(decimal val,int precision)
{
if (val.ToString().Contains("."))
{
string valstr = val.ToString();
string[] valArr = valstr.Split('.');
if(valArr[1].Length < precision)
{
int NoOfZeroNeedToAdd = precision - valArr[1].Length;
for (int i = 1; i <= NoOfZeroNeedToAdd; i++)
{
valstr = string.Concat(valstr, "0");
}
}
if(valArr[1].Length > precision)
{
valstr = valArr[0] +"."+ valArr[1].Substring(0, precision);
}
return Convert.ToDecimal(valstr);
}
else
{
string valstr=val.ToString();
for(int i = 0; i <= precision; i++)
{
if (i == 1)
valstr = string.Concat(valstr, ".0");
if(i>1)
valstr = string.Concat(valstr, "0");
}
return Convert.ToDecimal(valstr);
}
}
I have a path that is evaluate at time 't' and returns an orientation and position based on the path type.
The value for time is affected by the path type:
switch (type)
{
case PathType.Closed:
time = ToolBox.Wrap(time, StartTime, EndTime);
break; // Wrap time around the path time to loop
case PathType.Open:
time = ToolBox.Min(time, EndTime);
break; // Clamp the time value to the max path time range
case PathType.Oscillating:
break;
}
The missing link is oscillating.
My question is what is a good, efficient way for oscillating between two values?
For example (2, 7). If time reaches 7 it reverses and decrements towards to 2 and once it reaches 2 it reverses and increases towards 7.
The algorithm should know whether to increase/decrease the value based on the original value so if the value is 9 it knows the answer is 7 - (Abs(7 - 9). If the value is 14 the value has wrapped around so it will result in an increase of 1.
Higher values will also increase or decrease the value depending on the number of times it wraps around the original range.
I hope that makes sense as I'm finding it difficult to explain.
EDIT:
Doesn't oscillate with floating point values:
for (float i = 0; i < 100; i += 0.1f)
{
Console.WriteLine("{0} {1}", i, Oscillate(2.5f, 7.5f, i));
}
private float Oscillate(float min, float max, float value)
{
float range = max - min;
float multiple = value / range;
bool ascending = multiple % 2 == 0;
float modulus = value % range;
return ascending ? modulus + min : max - modulus;
}
Here is what I came up with:
public static int Oscillate(int input, int min, int max)
{
int range = max - min ;
return min + Math.Abs(((input + range) % (range * 2)) - range);
}
I'm assuming input will be a counter starting at 0.
Ideally, you should be abstracting this functionality into some kind of a class and not be concerned about how the implementation actually works when you're using it. Here's an initial take on what that would look like in C++ (my C# is a little rusty). I think you can work it into C# with only little difficulty.
class oscillator
{
private:
float min;
float max;
static float mod(float num, float div)
{
float ratio = num / div;
return div * (ratio - std::floor(ratio));
}
public:
oscillator(float a, float b)
: min(a < b ? a : b), max(a > b ? a : b) {}
float range() ( return max-min; }
float cycle_length() { return 2*range(); }
float normalize(float val)
{
float state = mod(val-min, cycle_length());
if (state > range())
state = cycle_length()-state;
return state + min;
}
};
This will oscillate your numbers between 2 & 7, in this example, time is an int:
bool isIncreasing = time <= 7;
for (int i = 0; i < 20; i++) //some random loop
{
time = time + (isIncreasing ? 1 : -1);
if (time >= 7 || time <= 2) isIncreasing = !isIncreasing;
}
New answer to account for float values:
// Note: Increase FACTOR depending on how many decimal places of accuracy you need.
private const float FACTOR = 10;
public void Test()
{
for (float i = 0; i < 1000; i += 0.1F)
{
Console.WriteLine("{0} {1}", i, Oscillate(2.5F, 7.5F, i));
}
}
private float Oscillate(float min, float max, float time)
{
return (float)(Oscillate_Aux(Upscale(min), Upscale(max), Upscale(time))) / FACTOR;
}
private int Upscale(float value)
{
return (int)(value * FACTOR);
}
private int Oscillate_Aux(int min, int max, int time)
{
int range = max - min;
int multiple = time / range;
bool ascending = multiple % 2 == 0;
int modulus = time % range;
return ascending ? modulus + min : max - modulus;
}
What you're describing sounds a lot like periodic linear interpolation between two values. Consider using XNA's MathHelper.Lerp function as the basis for your oscillation.
Note that it uses a percentage to control the oscillation as its third parameter. You'll have to figure out how to translate your time t value into a percentile, but you could start with ex. sin(t) to see how things work.
If you're reluctant to import XNA into your project, the core equation is very simple:
value1 + (value2 - value1) * amount
Edit: If your question, at its heart, really is "What is a good, efficient way for oscillating between two values?", then Math.Sin(t) (or Cos) can provide you with regular oscillation between 0 and 1.
here is sample code
public static decimal factorization(decimal num, decimal factor)
{
if (num == 1)
{
return 1;
}
if ((num % factor)!= 0)
{
while(num% factor != 0)
{
factor++;
}
}
factors.Add(factorization(num / factor, factor));
return factor;
}
Note : I have initialize factors as global.
Above code will work fine for sample inputs 90 , 18991325453139 but will not work for input 12745267386521023 ... so how can I do that ? How can I achieve this efficiently ... I know recursive call will consume memory that's why I have checked the last input using without recursion .. But its not working too
You can use that if
factor*factor > num
then num is prime
It will decrease complexity from O(n) to O(sqrt(n))
EDIT
while(num% factor != 0)
{
factor++;
if(factor*factor>num){ // You can precalc sqrt(num) if use big arifmetic
factor=num; //skip factors between sqrt(num) and num;
}
}
using System.Collections;
public static int[] PrimeFactors(int num)
{
ArrayList factors = new ArrayList();
bool alreadyCounted = false;
while (num % 2 == 0)
{
if (alreadyCounted == false)
{
factors.Add(2);
alreadyCounted = true;
}
num = num / 2;
}
int divisor = 3;
alreadyCounted = false;
while (divisor <= num)
{
if (num % divisor == 0)
{
if (alreadyCounted == false)
{
factors.Add(divisor);
alreadyCounted = true;
}
num = num / divisor;
}
else
{
alreadyCounted = false;
divisor += 2;
}
}
int[] returnFactors = (int[])factors.ToArray(typeof(int));
return returnFactors;
}
I just copied and posted some code from Smokey Cogs because this is a very common problem.
The code does some things better than yours.
First you divide by two until the number is no longer even. From there, you can start with 3 and increment by 2 (skip every even number) since all the 2's have been factored out.
Nonetheless, there are ways to improve. Think about the usage of "alreadyCounted" in the code. Is it absolutely essential? For example, using
if (num % 2 == 0)
{
factors.Add(2);
num = num/2;
}
while( num %2 == 0)
{num = num/2;}
Allows you to skip the extra comparisons in the beginning.
RiaD also gave a great heuristic that factor^2 > num implies that num is prime. This is because (sqrt(n))^2 = n, so the only number after sqrt(n) that divides num will be num itself, once you've taken out the previous primes.
Hope it helps!
To see how to find the factors of a given number in C# see this (duplicate?) StackOverflow
question.
A few points on your code:
there is no need for recursion if using a naive search, just build a list of factors within the method and return it at the end (or use yield).
your second if statement is redundant as it wraps a while loop with the same condition.
you should use an integer type (and unsigned integer types will allow larger numbers than their signed counterparts, e.g. uint or ulong) rather than decimal as you are working with integers. For arbitrarily large integers, use System.Numerics.BigInteger.
if you search incrementally upwards for a factor, you can stop looking when you have got as far as the square root of the original number, as no factor can be larger than that.
Also, note that there is no known efficient algorithm for factoring large numbers (see Wikipedia for a brief overview).
Here's example code, based on the above observations:
static IList<BigInteger> GetFactors(BigInteger n)
{
List<BigInteger> factors = new List<BigInteger>();
BigInteger x = 2;
while (x <= n)
{
if (n % x == 0)
{
factors.Add(x);
n = n / x;
}
else
{
x++;
if (x * x >= n)
{
factors.Add(n);
break;
}
}
}
return factors;
}
Note that this is still a rather naive algorithm which could easily be further improved.