阶乘函数 - 设计和测试

时间:2011-02-20 06:17:18

标签: c# unit-testing exception

我正试图解决一些面试问题,所以我盯着一个简单的问题。

设计阶乘函数。

这个函数是一个叶子(没有依赖 - 很容易测试),所以我在帮助器类中使它成为静态的。

public static class MathHelper
{
    public static int Factorial(int n)
    {
        Debug.Assert(n >= 0);
        if (n < 0)
        {
            throw new ArgumentException("n cannot be lower that 0");
        }

        Debug.Assert(n <= 12);
        if (n > 12)
        {
            throw new OverflowException("Overflow occurs above 12 factorial");
        }

        int factorialOfN = 1;
        for (int i = 1; i <= n; ++i)
        {
            //checked
            //{
                factorialOfN *= i;   
            //}
        }
        return factorialOfN;
    }
}

测试:

    [TestMethod]
    [ExpectedException(typeof(OverflowException))]
    public void Overflow()
    {
      int temp = FactorialHelper.MathHelper.Factorial(40);
    }

    [TestMethod]
    public void ZeroTest()
    {
        int factorialOfZero = FactorialHelper.MathHelper.Factorial(0);
        Assert.AreEqual(1, factorialOfZero);
    }

    [TestMethod]
    public void FactorialOf5()
    {
       int factOf5 = FactorialHelper.MathHelper.Factorial(5);
       Assert.AreEqual(5*4*3*2*1,factOf5);
    }

    [TestMethod]
    [ExpectedException(typeof(ArgumentException))]
    public void NegativeTest()
    {
        int factOfMinus5 = FactorialHelper.MathHelper.Factorial(-5);
    }

我有几个问题:

  1. 这是对的吗? (我希望如此;))
  2. 是否会抛出正确的异常?
  3. 我应该使用已检查上下文还是这个技巧(n> 12)可以吗?
  4. 使用uint是否更好,而不是检查负值?
  5. 未来的改进:长,小数,BigInteger或者通用方法的重载?
  6. 谢谢

3 个答案:

答案 0 :(得分:3)

对我来说看起来是对的,但是对于更大的数字来说效率会很低。如果你允许大整数,那么随着每个乘法的增加,这个数字会不断增长,所以如果你将它们成倍增加,你会看到一个巨大的(渐近更好的)速度增加。例如:

bigint myFactorial(uint first, uint last)
{
    if (first == last) return first;
    uint mid = first + (last - first)/2;
    return myFactorial(first,mid) * myFactorial(1+mid,last);
}
bigint factorial(uint n)
{
    return myFactorial(2,n);
}

如果你真的想要一个快速的阶乘方法,你也可以考虑这样的事情:

  1. 使用修改过的Eratosthenes筛子对因子进行因子分析
  2. 使用快速求幂算法(以及快速乘法和平方算法)计算每个素因子的幂
  3. 将所有素数的所有权力分层次地加在一起

答案 1 :(得分:3)

  1. 是的,看起来不错
  2. 这个例外对我来说似乎没问题,而且作为一名采访者,我看不到自己在那里担心
  3. 经过。此外,在一次采访中,你永远不会知道12恰好是正确的数字。
  4. UINT。如果您可以使用签名而不是异常强制执行某些操作,请执行此操作。
  5. 你应该把它做成长(或bigint)并完成它(int是一个愚蠢的返回类型选择)
  6. 如果我是你的面试官,我会问以下一些后续问题:

    1. 你为什么不递归地解决这个问题?因子是一个自然递归的问题。
    2. 您可以为此添加memoization,以便更快地完成计算12!如果它已经完成11!?
    3. 您在这需要n==0案例吗?
    4. 作为一名采访者,我肯定会有一些这样的曲线球向你投掷。一般来说,我喜欢用白板和模拟面试者练习的方法,因为很多都是敏捷的,并且想着站起来。

答案 2 :(得分:0)

在for循环中,你可以从for(int i = 2 ...)开始。乘以1是没用的。 我会为两个&lt;抛出一个ArgumentOutOfRangeException。 0和&gt; 12.当您使用单元测试时,Debug.Assert将屏蔽异常(您必须在发布模式下测试它)。