找到给定数字的所有因子的最佳方法

时间:2008-10-27 13:28:52

标签: c# .net math

所有数字均匀分配为x。

我输入4它返回:4,2,1

编辑:我知道这听起来像家庭作业。我正在编写一个小应用程序,用半随机测试数据填充一些产品表。其中两个属性是ItemMaximum和Item Multiplier。我需要确保乘数不会产生不合逻辑的情况,即购买1个项目会使订单超过允许的最大值。因此,这些因子将为我的测试数据提供有效值列表。

编辑++: 这是我在所有人的帮助下走的路。再次感谢!

编辑#:我写了3个不同的版本,看看我更喜欢哪个版本并测试它们以防止小数字和非常大的数字。我会粘贴结果。

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;
        }
    }
}

蜱虫。 将数字分别考虑20次,每次5次:

  • GetFactors1-5,445,881
  • GetFactors2-4,308,234
  • GetFactors3-2,913,659

将数字分解为20000时,每次5次:

  • GetFactors1-5,644,457
  • GetFactors2-12,117,938
  • GetFactors3-3,108,182

12 个答案:

答案 0 :(得分:38)

伪代码:

  • 从1循环到数字的平方根,调用索引“i”。
  • 如果number mod i为0,则将i和number / i添加到因子列表中。

realocode:

public List<int> Factor(int number) {
    List<int> 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;
}

正如Jon Skeet所提到的,您也可以将其作为IEnumerable<int>实现 - 使用yield而不是添加到列表中。 List<int>的优点是,如果需要,可以在返回之前对其进行排序。然后,你可以得到一个带有混合方法的排序枚举器,产生第一个因子并在循环的每次迭代中存储第二个因子,然后产生以相反顺序存储的每个值。

您还需要做一些事情来处理传递给函数的负数的情况。

答案 1 :(得分:20)

%(余数)运算符是此处使用的运算符。如果x % y == 0,那么x可以被y整除。 (假设0 < y <= x

我个人将此实现为使用迭代器块返回IEnumerable<int>的方法。

答案 2 :(得分:14)

很晚但接受的答案(前一段时间)没有给出正确的结果。

感谢Merlyn,我现在得到了正方形作为校正样本下方“最大”的原因。虽然Echostorm的答案似乎更完整。

    public static IEnumerable<uint> getFactors(uint x)
    {
        for (uint i = 1; i*i <= x; i++)
        {
            if (0 == (x % i))
            {
                yield return i;
                if (i != (x / i))
                {
                    yield return x / i;
                }
            }
        }
    }

答案 3 :(得分:5)

作为扩展方法:

    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;
    }

以下是一个使用示例:

        foreach (int i in 4.Factors())
        {
            Console.WriteLine(i);
        }

请注意,我已针对清晰度进行了优化,而不是针对性能进对于较大的i值,此算法可能需要很长时间。

答案 4 :(得分:2)

这里再次,只计算平方根,正如其他人提到的那样。如果你希望改善表现,我想人们会被这个想法所吸引。我宁愿首先编写优雅的代码,并在测试我的软件之后优化性能。

仍然,作为参考,这里是:

    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;
            }
        }
    }

结果不仅可读性差得多,而且这些因素也是这样的。

答案 5 :(得分:2)

另一种LINQ风格并且要保持O(sqrt(n))复杂性

        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;
            }


        }

答案 6 :(得分:2)

我是懒惰的做法。我不太了解,但我被告知简单有时可能意味着优雅。这是一种可行的方法:

    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;
    }

编辑:正如Kraang Prime指出的那样,此函数不能超过整数的限制,并且(诚然)不是处理此问题的最有效方法。

答案 7 :(得分:1)

从2开始并且朝着一个上限值开始是不是有意义,这个上限值会根据您刚刚检查过的数字不断重新计算?请参阅N / i(其中N是您要查找因子的数字,而i是要检查的当前数字...)理想情况下,您将使用返回N / i的除法函数而不是mod可能有的任何余数。这样你就可以执行一个除法运算来重新创建你的上限以及你要检查甚至除法的余数。

Math.DivRem http://msdn.microsoft.com/en-us/library/wwc1t3y1.aspx

答案 8 :(得分:1)

如果您使用双打,则以下方法有效:使用for循环从1迭代到您想要的数字。在每次迭代中,将数量除以i来计算。如果(number / i)%1 == 0,那么i是一个因子,就像number / i的商。将其中一个或两个放入列表中,您就拥有了所有因素。

答案 9 :(得分:1)

还有另一种解决方案。不确定它是否具有可读性之外的其他优点。

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;
}

答案 10 :(得分:0)

该程序用于在javascript代码中获取整数的素数。

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);

答案 11 :(得分:-2)

Linq解决方案:

IEnumerable<int> GetFactors(int n)
{
  Debug.Assert(n >= 1);
  return from i in Enumerable.Range(1, n)
         where n % i == 0
         select i;
}