C# 算法竞赛中无BigInteger库的大整数处理

C# 算法竞赛中无BigInteger库的大整数处理,c#,algorithm,biginteger,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(n-k) 其中所有c(i)都是实值常数,k是递推关系的长度,n是大于或等于k的任意正整数。 将给您一个int[]系数,按顺序指示c(0)、c(1)、…、c(k-1)。您还将获得一个int[]初始值,给出x(0)、x(1)、…、x(k-1)和int N的值。您

问题:Topcoder SRM 170 500

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

更具体地说,如果系数的大小为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和x1=7。然后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);
    }
}
类复发
{
公共整数模函数(整数[]系数,整数[]初始值,整数N)
{
双xn=0;int j=0;
int K=系数。长度;
List xs=新列表(Array.ConvertAll)(初始,
代表(int i)
{
返回(双)i;
})); 
if(N
这个解决方案的问题是双重表示的精度,因为我不能使用第三方库或.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失败

有人能帮我弄清楚吗?我将考虑使用数组来存储这些值,但它有点超出了我的范围,特别是如何满足乘法运算,并且仍然在问题中提出的时间和空间复杂度之内。也许我的整个方法都错了?请给我一些指点和方向(不是完全充实的答案)的答案


谢谢

注意,我们只需要返回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*席< /Cube >,所以<代码> AI % 10 =(CI 10×席% 10)% 10 < /代码> ./P>
因此,通过进行所有这些数学计算,我们可以避免使用double,并将结果保持在可管理的大小。

正如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() );

    }
static int solve(int[]系数,int[]种子,int n)
{
int k=系数。计数();
var queue=新队列(seed.Reverse().Take(k.Reverse());
对于(int i=k;i x*y%10);
排队,排队(xn);
queue.Dequeue();
}
return(int)(queue.Last());
}
编辑:
得到与您预期相同的结果,但是我不能保证在这个示例中没有bug

这不是更适合你吗?我不这么认为,我对解决问题所涉及的话题的方法和讨论感兴趣。但我认为把它贴在那里没有坏处。在滴答声中就可以了。坚持住。不鼓励越过柱子。这只是我的想法,也许其他人不同意。@Phamtrong系数数组是固定的。另一方面,当我们寻找第n个项时,初始值会增加。递归关系由系数数组和xs中的值(也包含初始值)确定。这些价值是巨大的!比int可以存储的值大好几倍(参见测试用例二,您将看到原因),因此使用double存储值的原因,例如1.234384579347943e102。但是我需要更高的精度(最多100位小数)才能得到正确的答案。int不能在没有溢出的情况下完成这项工作。@TheJackal我看不出有任何理由需要处理大数字,因为只需要返回xn模10,这也意味着只需要处理小于10的数字。我不知道k可以小于n。当系数小于种子值时,此版本也应起作用。对于负xn,这将失败,您需要处理-xn(通过加法)以获得正确的模量;如果允许负系数,您可以使用
(x,y)=>((x*y%10)+10)%10)
。将10加在大于-10的负数上,使其再次成为正数。然而,我的回答只是概述了一个可能的解决方案,因为您还提到了解决方案的性能问题。我不