N个数的模数连接N次

时间:2016-08-21 19:50:08

标签: java algorithm math precision mod

我今天在开发人员工作面试时遇到了以下情况,这是其中一个问题,只有一个我没有回答。

连接数N N次,然后到2017年计算他的模数。

例如:对于N = 5,数字将是55555,结果将是Mod(55555,2017)= 1096,对于N = 10,数字将是10101010101010101010,结果Mod(10101010101010101020,2017)= 1197

现在我必须计算的数字是58184241583791680.我得到的唯一提示是58184241583791680连接58184241583791680次模数2017的结果是4位数字。

我在math.stackexchange上发布了this question,除了蛮力外,我得到了一个解决这个问题的数学方法。

我在JAVA中编写了以下代码

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;

public class Puzzle {
    final static BigInteger M = new BigInteger("2017");

public static void main(String [ ] args) {
    for (long n : new long[] { 1L, 2L, 5L, 10L, 20L }) { 
        System.out.println(n + " --> " + bruteForce(n) + " --- " + formulaV2(n));
    }
}


private static BigInteger bruteForce(long n) {
    String s = "";
    for (long i = 0; i < n; i++) {
        s = s + n;
    }
    return new BigInteger(s.toString()).mod(M);
}

private static BigInteger formulaV2(long n) {
    String aux = String.valueOf(n);
    long L = aux.length();
    long K = n;           

    double op1 = Math.pow(10,L*K);
    BigDecimal minus1 = new BigDecimal(1);
    BigDecimal p1 = new BigDecimal(op1).subtract(minus1);

    double op2 = Math.pow(10,L);
    BigDecimal p2 = new BigDecimal(op2).subtract(minus1).pow(-1,MathContext.DECIMAL64);

    BigDecimal R = new BigDecimal(n).multiply(p1).multiply(p2);
    R = R.setScale(0,BigDecimal.ROUND_UP);
    BigInteger ret = R.toBigInteger();
    return ret.mod(M);
}
}

我正在使用BigInteger和BigDecimal,因为我想得到真正大数字(16位数)的值。

bruteForce方法将简单地连接循环内的数字,而formulaV2方法将使用数学论坛中提出的问题的公式。

bruteForce方法仅用于验证。

然而,公式方法对于N <10是有效的,但对于N> = 10则没有。在N = 10时,我的结果不正确。

对于所提供的公式,编码似乎是一致的(至少对我来说,可能有一些我遗漏的东西),并且公式是正确的(我在Wolfram Alpha中检查过)。

我的猜测是我有精确问题,在这种情况下BigDecimal可能不是正确的对象吗?

5 个答案:

答案 0 :(得分:4)

使用数学!一位优秀的面试官希望你使用你的大脑并解决一个问题,而不是用蹩脚的代码来蛮力。

在这种情况下,他可能希望你使用等值

  1. ab mod n = [(a mod n)(b mod n)] mod n

  2. (a + b)mod n = [(a mod n)+(b mod n)] mod n

  3. 以及例如将三位数字连接三次与xyzu * 100010001相同,后者可以进一步细分为10000^0+10000^1+10000^2

    现在在你的情况下,基数x和重复次数y是相同的,两者都相当大。但模数n很小。设D是大于x的下一个10的幂。

    因此D mod n实际上不是D,而是小于2017.而不是计算D ^ y,你可以实际计算((D mod n)^ y)mod n,如果你在for中执行此操作循环它最终(在最多2017步之后)循环或变为0.因此,您可以在最多2017次迭代中计算该项。因此,这种智能方法的复杂性是O(n),其中n是模数项,而朴素方法则需要O(x * y)内存。

    通过这些技巧,你应该能够在没有 BigInteger的情况下,并且更多更快。

答案 1 :(得分:3)

这是一个问题:

double op1 = Math.pow(10,L*K);

例如,n = 20L*K = 40op1 = 10000000000000000303786028427003666890752时。至于为什么,通常的浮点业务:它是最接近的。没有double的值恰好是1E40。

op1不会像那样打印(你会看到1E40并认为一切都很好),但它会像那样转换。那么你将使用BigDecimal这很好(虽然很奇怪),之前它已经出错了。

我假设您使用了BigDecimal ,因为是从double转换而来的,否则您将使用BigInteger。只需使用BigInteger.pow代替Math.pow它就可以了(它解决了这个问题,如果还有其他事情我没有注意到,但我不能保证它会工作)。

另一方面,

Math.pow(10,L)不应该是一个问题,因为L现在还不够......你也可以改变它,并让它适用于大L

答案 2 :(得分:3)

double op1 = Math.pow(10,L*K)

这对于大的n值会溢出。对于L * K,Double.MAX_VALUE~1.7 * 10 ^ 308(即)。 308,它会溢出。

编辑:双倍将无法准确地表示他在答案中提到的更小的值。

答案 3 :(得分:2)

我在https://stackoverflow.com/questions/38988665/java-puzzle-whats-the-correct-answer#comment65349106_38988665的评论中把这一切都放在了这一点上,但是你可以把@ Anony-Mousse和其他人的话说出来并与之合作。

一个关键是在前面找到10 ^ 17 mod 2017。 Wolfram Alpha无论如何都是正确的,但是在其他平台上你可能需要将其视为((10 ^ 9 mod 2017)(10 ^ 8 mod 2017))mod 2017以获得599.你也可以需要58184241583791680 mod 2017是2005年(再次Wolfram得到这个权利,但是如果你不把它分解成(((581842415 mod 2017)(10 ^ 8 mod 2017)mod 2017),较小的平台可能会动摇+83791680)mod 2017。

因此,在(58184241583791680,但没有恐惧)过程的每一步,您将获取当前数字并将其乘以10 ^ 17(移动它以为连接腾出空间)并添加58184241583791680.但我们可以这样做所有mod 2017.在序列的语言中,子1 = 2005和子(n + 1)=((子n)* 599 + 2005)mod 2017。 这是你的for循环中的关键步骤。我在R中做了第一个4040左右的术语(得到了爱,你可以将它们交互式地放在屏幕上并对它们进行毛孔 - 语言在我身上越来越大),尽管它实际上已经足够大约一半了(工作中良好的老鸽子原则)。并且它们不会循环,直到你实际上完全到达2017年的子版本,即2005年。 4033也是如此。

一般来说,子n = a sub(n mod 2016)。你想要一个子58184241583791680. 58184241583791680 mod 2016是224说Wolfram Alpha [再次在较小的平台上你可能需要把它分解成(((581842415 mod 2016)*(10 ^ 8 mod 2016)mod 2016)+83791680)mod 2016],所以你想要一个子224。

如果人们想要检查自己或我,我得到的R是465.但正如上面所指出的,这个过程才是真正感兴趣的问题。

答案 4 :(得分:-1)

import java.math.BigInteger;
class Puzzle {

    final static BigInteger M = new BigInteger("2017");
    private static BigInteger compute(long n) {
         String s = "";
         String a = new BigInteger(n+"").mod(M)+"";

         for (long i = 0; i < Integer.parseInt(a); i++) {
              s = s + a ;
              s = new BigInteger(s).mod(M)+"";
         }
         return new BigInteger(s.toString()).mod(M);

    }

    public static void main(String args[]) {

       for (long n : new long[] { 1L, 2L, 5L, 10L, 20L, 58184241583791680L }) {
           System.out.println("" + n + ": " + compute(n));
       }

    }
}