数字对变量来说太大了

时间:2015-04-16 13:46:57

标签: java biginteger

我要找到1 ^ 1 + 2 ^ 2 + 3 ^ 3 .. + 1000 ^ 1000的最后十位数字。 有没有办法用纯逻辑找到它?我认为你不能存储那么大的数字。

这个问题来自数学竞赛,但我想在Java中尝试这样做。

7 个答案:

答案 0 :(得分:4)

你不需要存储那么大的数字,你只需要最后十位数。你可以存储很长时间。

计算大功率的有效方法是乘以正方形,例如19 ^ 19 = 19 * 19 ^ 2 * 19 ^ 16 = 19 * 19 ^ 2 * 19 ^ 2 ^ 2 ^ 2 ^ 2。如果您的值大于10 ^ 10,则可以截断最后10位数。

顺便说一下,1000 ^ 1000的最后十位是0000000000,当你把它加到你的总和时,它与加零相同;)


编辑:虽然您不必使用BigInteger,但编写起来更简单。

BigInteger tenDigits = BigInteger.valueOf(10).pow(10);
BigInteger sum = BigInteger.ZERO;
for (int i= 1; i <= 1000; i++) {
    BigInteger bi = BigInteger.valueOf(i);
    sum = sum.add(bi.modPow(bi, tenDigits));
}
sum = sum.mod(tenDigits);

modPow分别比pow mod效率更高,因为它不必计算非常大的数字,只需计算mod的结果。

答案 1 :(得分:2)

你可以使用BigIntegers ......

public static void main(String[] args) {
    BigInteger acc = BigInteger.ZERO;

    for (int k = 1; k <= 1000; k++) {
        BigInteger pow = BigInteger.valueOf(k).pow(k);
        acc = acc.add(pow);
    }

    System.out.println(acc);
}

答案 2 :(得分:1)

我认为这个问题来自欧拉计划,所以它不仅仅是一个数学问题;它也应该需要一些计算。除了重复计算机可能做的计算之外,我不知道如何用铅笔和纸来解决它。我无法从纯粹的数学解决方案中看到太多东西。然而,数学可以帮助我们优化代码。

要提高^ n,找到n:

的二进制扩展
n = n_k x 2^k + n_(k-1) x 2^(k-1) + ... + n_0 x 2^0 

其中n_i = 0 or 1n的二进制数字,右边是第零个数字。然后

a^n = a^(n_k x 2^k) x a^(n_(k-1) x 2^(k-1)) x ... x a^(n_0 x 2^0). 

我们可以忽略n_i = 0的所有因素,因为因子是a^0 = 1。该过程可以编写为O(log n)时间和O(1)空间的算法(见下文)。

接下来,作为挑战,为了避免使用BigInteger,我们可以将计算分为两部分:找到答案mod 2^10并找到答案mod 5^10。在这两种情况下,相关范围中的数字和相关范围中的数字乘积都符合long s。缺点是我们必须使用中国剩余定理来重新组合结果,但它并不那么难,而且它具有指导性。使用中国剩余定理最难的部分是找到逆mod m,但这可以通过使用欧几里德算法的修改以直接的方式完成。

渐近运行时间为O(n log n),空格为O(1),并且所有内容都适合几个long变量,不需要BigInteger或其他复杂的库。

public class SeriesMod1010 {

  public static long pow(long a,long n,long m) { // a^n mod m
    long result = 1;
    long a2i = a%m; // a^2^i for i = 0, ...
    while (n>0) {
      if (n%2 == 1) {
    result *= a2i;
    result %= m;
      }
      a2i *= a2i;
      a2i %= m;
      n /= 2;
    }
    return result;
  }

  public static long inverse(long a, long m) { // mult. inverse of a mod m
    long r = m;
    long nr = a;
    long t = 0;
    long nt = 1;
    long tmp;
    while (nr != 0) {
      long q = r/nr;
      tmp = nt; nt = t - q*nt; t = tmp;
      tmp = nr; nr = r - q*nr; r = tmp;
    }
    if (r > 1) return -1; // no inverse
    if (t < 0) t += m;
    return t;
  }

  public static void main(String[] args) {
    long twoTo10 = 1024;
    long sum210 = 0;
    for (long i=1; i<=1000; i++) {
      sum210 += pow(i,i,twoTo10);
      sum210 %= twoTo10;
    }

    long fiveTo10 = 9_765_625;
    long sum510 = 0;
    for (long i=1; i<=1000; i++) {
      sum510 += pow(i,i,fiveTo10);
      sum510 %= fiveTo10;
    }

    // recombine the numbers with the Chinese remainder theorem
    long tenTo10 = 10_000_000_000L;
    long answer = sum210 * inverse(fiveTo10,twoTo10) * fiveTo10
      + sum510 * inverse(twoTo10,fiveTo10) * twoTo10;
    answer %= tenTo10;
    System.out.println(answer);
  }

}

答案 3 :(得分:0)

它可以在没有BigInteger的情况下解决,因为你需要在每次加法或乘法运算时只存储10个最后一位数,使用%来避免溢出:

int n = 1000;
long result = 0;
long tenDigits = 10_000_000_000L;
for (int i = 1; i <= n; i++) {
    long r = i;
    for (int j = 2; j <= i; j++) {
        r = (r * i) % tenDigits;
    }
    result += r;
}  
return result % tenDigits;

复杂度是O(N ^ 2),假设乘法在恒定时间内运行。

答案:9110846700。

答案 4 :(得分:0)

使用BigIntegers:

    import java.math.BigInteger;

public class Program {

    public static void main(String[] args) {
        BigInteger result = new BigInteger("1");
        BigInteger temp = new BigInteger("1");
        BigInteger I;
        for(int i = 1 ; i < 1001 ; i++){
            I = new BigInteger(""+i);
            for(int j = 1 ; j < i ; j++){
                temp = temp.multiply(I);
            }
            result = result.multiply(temp);
            temp = new BigInteger("1");
        }
        System.out.println(result);
    }
}

答案 5 :(得分:0)

十进制基数使用0 ... 9(10位)表示数字,第二个位置从右到左的数字表示Digits * base.length ^ l2rPosition。使用这个逻辑,你可以创建一个类,它几乎可以帮助你的小学老师告诉你,当我们用纸来计算东西,但是使用baseN编号和base-to-base转换&#34;我已经在C#中完成了这个类的功能,但我没有时间将它完全翻译成java,这与java.math.BigInteger背后的逻辑相同。 (表现较差,我打赌因为我使用了很多列表&gt; _&gt;&#34;现在没时间对其进行优化

class IntEx {
    ArrayList<Integer> digits = new ArrayList<>();
    long baseSize = Integer.MAX_VALUE+1;
    boolean negative = false;

    public IntEx(int init)
    {
        set(init);
    }

    public void set(int number)
    {
        digits = new ArrayList<>();
        int backup = number;
        do
        {
            int index = (int)(backup % baseSize);
            digits.add(index);
            backup = (int) (backup / baseSize);
        } while ((backup) > 0);
    }

    // ... other operations
    private void add(IntEx number)
    {
        IntEx greater = number.digits.size() > digits.size() ? number : this;
        IntEx lesser = number.digits.size() < digits.size() ? number : this;
        int leftOvers = 0;
        ArrayList<Integer> result = new ArrayList<>();
        for (int i = 0; i < greater.digits.size() || leftOvers > 0; i++)
        {
            int sum;
            if (i >= greater.digits.size())
                sum = leftOvers;
            else if(i >= lesser.digits.size())
                sum = leftOvers + greater.digits.get(i);
            else
                sum = digits.get(i) + number.digits.get(i) + leftOvers;
            leftOvers = 0;
            if (sum > baseSize-1)
            {
                while (sum > baseSize-1)
                {
                    sum -= baseSize;
                    leftOvers += 1;
                }
                result.add(sum);
            }
            else
            {
                result.add(sum);
                leftOvers = 0;
            }
        }
        digits = result;
    }
    private void multiply(IntEx target)
    {
        ArrayList<IntEx> MultiParts = new ArrayList<>();
        for (int i = 0; i < digits.size(); i++)
        {
            IntEx thisPart = new IntEx(0);
            thisPart.digits = new ArrayList<>();
            for (int k = 0; k < i; k++)
                thisPart.digits.add(0);
            int Leftovers = 0;
            for (int j = 0; j < target.digits.size(); j++)
            {
                int multiFragment = digits.get(i) * (int) target.digits.get(j) + Leftovers;
                Leftovers = (int) (multiFragment / baseSize);
                thisPart.digits.add((int)(multiFragment % baseSize));
            }
            while (Leftovers > 0)
            {
                thisPart.digits.add((int)(Leftovers % baseSize));
                Leftovers = (int) (Leftovers / baseSize);
            }
            MultiParts.add(thisPart);
        }
        IntEx newNumber = new IntEx(0);
        for (int i = 0; i < MultiParts.size(); i++)
        {
            newNumber.add(MultiParts.get(i));
        }
        digits = newNumber.digits;
    }

    public long longValue() throws Exception
    {
        int position = 0;
        long multi = 1;
        long retValue = 0;
        if (digits.isEmpty()) return 0;
        if (digits.size() > 16) throw new Exception("The number within IntEx class is too big to fit into a long");
        do
        {
            retValue += digits.get(position) * multi;
            multi *= baseSize;
            position++;
        } while (position < digits.size());
        return retValue;
    }

    public static long BaseConvert(String number, String base)
    {
        boolean negative = number.startsWith("-");
        number = number.replace("-", "");
        ArrayList<Character> localDigits = new ArrayList<>();
        for(int i = number.toCharArray().length - 1; i >=0; i--) {
            localDigits.add(number.charAt(i));
        }
        // List<>().reverse is missing in this damn java. -_-
        long retValue = 0;
        long Multi = 1;
        char[] CharsBase = base.toCharArray();
        for (int i = 0; i < number.length(); i++)
        {
            int t = base.indexOf(localDigits.get(i));
            retValue += base.indexOf(localDigits.get(i)) * Multi;
            Multi *= base.length();
        }
        if (negative)
            retValue = -retValue;
        return retValue;
    }

    public static String BaseMult(String a, String b, String Base)
    {
        ArrayList<String> MultiParts = new ArrayList<>();
        // this huge block is a tribute to java not having "Reverse()" method.
        char[] x = new char[a.length()];
        char[] y = new char[b.length()];
        for(int i = 0; i < a.length(); i++) {
            x[i] = a.charAt(a.length()-i);
        }
        for(int i = 0; i < b.length(); i++) {
            y[i] = a.charAt(a.length()-i);
        }
        a = new String(x);
        b = new String(y);
        // ---------------------------------------------------------------------
        for (int i = 0; i < a.length(); i++)
        {
            ArrayList<Character> thisPart = new ArrayList<>();
            for (int k = 0; k < i; k++)
                thisPart.add(Base.charAt(0));
            int leftOvers = 0;
            for (int j = 0; j < b.length(); j++)
            {
                // Need I say repeated characters in base may cause mayhem?
                int MultiFragment = Base.indexOf(a.charAt(i)) * Base.indexOf(b.charAt(j)) + leftOvers;
                leftOvers = MultiFragment / Base.length();
                thisPart.add(Base.charAt(MultiFragment % Base.length()));
            }
            while (leftOvers > 0)
            {
                thisPart.add(Base.charAt(leftOvers % Base.length()));
                leftOvers = leftOvers / Base.length();
            }
            char[] thisPartReverse = new char[thisPart.size()];
            for(int z = 0; z < thisPart.size();z++)
                thisPartReverse[z] = thisPart.get(thisPart.size()-z);
            MultiParts.add(new String(thisPartReverse));
        }
        String retValue = ""+Base.charAt(0);
        for (int i = 0; i < MultiParts.size(); i++)
        {
            retValue = BaseSum(retValue, MultiParts.get(i), Base);
        }
        return retValue;
    }

    public static String BaseSum(String a, String b, String Base)
    {
        // this huge block is a tribute to java not having "Reverse()" method.
        char[] x = new char[a.length()];
        char[] y = new char[b.length()];
        for(int i = 0; i < a.length(); i++) {
            x[i] = a.charAt(a.length()-i);
        }
        for(int i = 0; i < b.length(); i++) {
            y[i] = a.charAt(a.length()-i);
        }
        a = new String(x);
        b = new String(y);
        // ---------------------------------------------------------------------
        String greater = a.length() > b.length() ? a : b;
        String lesser = a.length() < b.length() ? a : b;
        int leftOvers = 0;
        ArrayList<Character> result = new ArrayList();
        for (int i = 0; i < greater.length() || leftOvers > 0; i++)
        {
            int sum;
            if (i >= greater.length())
                sum = leftOvers;
            else if (i >= lesser.length())
                sum = leftOvers + Base.indexOf(greater.charAt(i));
            else
                sum = Base.indexOf(a.charAt(i)) + Base.indexOf(b.charAt(i)) + leftOvers;
            leftOvers = 0;
            if (sum > Base.length()-1)
            {
                while (sum > Base.length()-1)
                {
                    sum -= Base.length();
                    leftOvers += 1;
                }
                result.add(Base.charAt(sum));
            }
            else
            {
                result.add(Base.charAt(sum));
                leftOvers = 0;
            }
        }
        char[] reverseResult = new char[result.size()];
        for(int i = 0; i < result.size(); i++)
            reverseResult[i] = result.get(result.size() -i);
        return new String(reverseResult);
    }

    public static String BaseConvertItoA(long number, String base)
    {
        ArrayList<Character> retValue = new ArrayList<>();
        boolean negative = false;
        long backup = number;
        if (negative = (backup < 0))
            backup = -backup;
        do
        {
            int index = (int)(backup % base.length());
            retValue.add(base.charAt(index));
            backup = backup / base.length();
        } while ((backup) > 0);
        if (negative)
            retValue.add('-');
        char[] reverseRetVal = new char[retValue.size()];
        for(int i = 0; i < retValue.size(); i++)
            reverseRetVal[i] = retValue.get(retValue.size()-i);
        return new String(reverseRetVal);
    }

    public String ToString(String base)
    {
        if(base == null || base.length() < 2)
            base = "0123456789";
        ArrayList<Character> retVal = new ArrayList<>();
        char[] CharsBase = base.toCharArray();
        int TamanhoBase = base.length();
        String result = ""+base.charAt(0);
        String multi = ""+base.charAt(1);
        String lbase = IntEx.BaseConvertItoA(baseSize, base);
        for (int i = 0; i < digits.size(); i++)
        {
            String ThisByte = IntEx.BaseConvertItoA(digits.get(i), base);
            String Next = IntEx.BaseMult(ThisByte, multi, base);
            result = IntEx.BaseSum(result, Next, base);
            multi = IntEx.BaseMult(multi, lbase, base);
        }
        return result;
    }

    public static void main(String... args) {
        int ref = 0;
        IntEx result = new IntEx(0);
        while(++ref <= 1000)
        {
            IntEx mul = new IntEx(1000);
            for (int i = 0; i < 1000; ++i) {
                mul.multiply(new IntEx(i));
            }
            result.add(mul);
        }
        System.out.println(result.toString());
    }
}

免责声明:这是C#研究的粗略翻译/本地化,省略了大量代码。这几乎是&#34; java.math.BigInteger背后的逻辑相同(你可以在自己喜欢的设计师身上打开BigInteger代码并自行检查。如果我忘记了一个未翻译成java的重载运算符,请有一点耐心和宽恕,这个例子只是对于&#34;可能&#34;澄清理论。

另外,只是一个旁注,我知道它是&#34;试图重新发明轮子&#34;但是考虑到这个问题具有学术目的,我认为它很难分享。 我可以在gitHub上看到这项研究的结果(虽然没有本地化),但我没有在这里扩展C#代码,因为它非常广泛,而不是这个问题的语言。

答案 6 :(得分:0)

这给出了正确的答案,没有多余的计算。很长就足够了。

public String lastTen() {
        long answer = 0;
        String txtAnswer = "";
        int length = 0;
        int i = 1;

        for(i = 1; i <= 1000; i++) {
            answer += Math.pow(i, i);
            txtAnswer = Long.toString(answer);
            length = txtAnswer.length();
            if(length > 9) break;
        }
        return txtAnswer.substring(length-10);
    }