在算法竞赛中处理没有BigInteger库的大整数

时间:2014-08-19 08:24:47

标签: c# algorithm biginteger

问题:Topcoder SRM 170 500

考虑序列{x0,x1,x2,...}。根据先前术语定义某个术语xn的关系称为递归关系。线性递归关系是递归形式为xn = c(k-1)* x(n-1)+ c(k-2)* x(n-2)+ ... + c(0 )* x(nk) 其中所有c(i)都是实值常数,k是递归关系的长度,n是任意大于或等于k的正整数。 您将获得一个int []系数,按顺序指示c(0),c(1),...,c(k-1)。您还将获得一个int []初始值,给出x(0),x(1),...,x(k-1)和int N的值。您的方法应返回xN modulo 10。 / p>

更具体地说,如果系数的大小为k,那么递归关系将是 xn =系数[k-1] * xn-1 +系数[k-2] * xn-2 + ... +系数[0] * xn-k。

例如,如果系数= {2,1},初始= {9,7},N = 6,那么我们的递归关系是xn = xn-1 + 2 * xn-2,我们有x0 = 9然后x2 = x1 + 2 * x0 = 7 + 2 * 9 = 25,同样,x3 = 39,x4 = 89,x5 = 167,x6 = 345,所以你的方法会返回(345模数) 10)= 5。

约束: - 代码必须在小于或等于2秒的时间内运行 - 内存利用率不得超过64 MB

我的尝试解决方案:

class RecurrenceRelation
{

    public int moduloTen(int[] coefficients, int[] initial, int N)
    {
        double xn = 0; int j = 0;
        int K = coefficients.Length;
        List<double> xs = new List<double>(Array.ConvertAll<int, double>(initial,
        delegate(int i)
        {
            return (double)i;
        })); 
        if (N < K)
            return negativePositiveMod(xs[N]);
        while (xs.Count <= N)
        {
            for (int i = xs.Count - 1; i >= j; i--)
            {
                xn += xs[i] * coefficients[K--];
            }
            K = coefficients.Length;
            xs.Add(xn);
            xn = 0;
            j++;
        }
        return negativePositiveMod(xs[N]);
    }
    public int negativePositiveMod(double b)
    {
        while (b < 0)
        {
            b += 10;
        }
        return (int)(b % 10);
    }
}

我对这个解决方案的问题是双重表示的精确性,因为我不能在.NET中使用第三方库或BigInteger库用于此SRM,所以我需要找到一种解决方法来解决它。我怀疑我可以使用递归,但我对如何去做有点无能为力。

这是一个测试用例,显示我的代码何时起作用以及何时起作用

{2,1},{9,7},6 - 成功返回5 {9,8,7,6,5,4,3,2,1,0},{1,2,3,4,5,6,7,8,9,10},654 - 未成功返回8而不是5由于双精度的精度

任何人都可以帮我解决这个问题吗?我打算考虑使用数组来存储值,但它有点超出我的特别是如何满足乘法,并且仍然在问题中设置的时间和空间复杂度。也许我的整个方法都错了?我很欣赏一些指示和方向(没有完全充实的答案)答案。

由于

2 个答案:

答案 0 :(得分:4)

请注意,我们只需要返回xn的模10。

我们还需要知道,如果a = b + c,我们有a % 10 = (b % 10 + c % 10) %10.

a = b*c,我们也有a % 10 = (b %10 * c % 10) % 10;

所以,对于

xn = c(k-1) * x(n-1) + c(k-2) * x(n-2) + ... + c(0) * x(n-k) 
            = a0 + a1 + .... + an 

(a0 = c(k - 1)* x(n-1),a1 = ......)

我们有xn % 10 = (a0 % 10 + a1 % 10 + ...)%10

对于每个ai = ci*xi,所以ai % 10 = (ci % 10 * xi % 10)% 10

因此,通过所有这些数学计算,我们可以避免使用double并将结果保持在可管理的大小。

答案 1 :(得分:1)

正如Pham所回答的那样,诀窍是要意识到你只需要返回一个模数,从而绕过溢出问题。这是我的快速尝试。我使用一个队列来输入最后一个结果xN,并逐出最旧的结果。

    static int solve(int[] coefficients, int[] seed, int n)
    {
        int k = coefficients.Count();
        var queue = new Queue<int>(seed.Reverse().Take(k).Reverse());

        for (int i = k; i <= n; i++)
        {

            var xn = coefficients.Zip(queue, (x, y) => x * y % 10).Sum() % 10;
            queue.Enqueue(xn);
            queue.Dequeue();
        }

        return (int) (queue.Last() );

    }

编辑: 获得与预期相同的结果,但我不保证此示例中没有错误。