我要找到1 ^ 1 + 2 ^ 2 + 3 ^ 3 .. + 1000 ^ 1000的最后十位数字。 有没有办法用纯逻辑找到它?我认为你不能存储那么大的数字。
这个问题来自数学竞赛,但我想在Java中尝试这样做。
答案 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 1
是n
的二进制数字,右边是第零个数字。然后
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);
}