计算序列中的尾随零的数量

时间:2018-05-27 04:25:01

标签: java recursion sequence multiplication

考虑输入s(0)s(1)的序列,以及所有s(n) = s(n-1) * s(n-2)的{​​{1}}。我想在n >= 2中找到尾随零的数量。我们可以假设以下内容:

  • s(n)ns(0)作为输入
  • s(1)
  • n <= 40
  • s(0) <= 20

以下是我的代码尝试。当s(1) <= 20大于30(它运行很长时间)时,它不会运行。有没有其他方法可以计算尾随零的数量?

n

2 个答案:

答案 0 :(得分:4)

您只需跟踪2和5的因子,而不是执行整个乘法。如果数字可以写为N = 2^a * 5^b * (factors other than 2 or 5),那么N中的尾随零的数量是min(a, b)。 (这是因为尾随零只是10的因子,需要一个2和一个5。)

请注意,乘法会将因子的指数加在一起。所以,如果你能写:

s(n-2) = 2^a * 5^b * (factors other than 2 or 5)
s(n-1) = 2^c * 5^d * (factors other than 2 or 5)

然后我们有:

s(n) = s(n-1) * s(n-2)
     = 2^(a+c) * 5^(b+d) * (factors other than 2 or 5)

因此,我们可以将此问题视为两个Fibonacci sequences。您从s(0)s(1)中的2和5开头,并以Fibonacci序列方式计算s(2), s(3), ..., s(n)中的2和5的数量:

#2s in s(n) = (#2s in s(n-1)) + (#2s in s(n-2))
#5s in s(n) = (#5s in s(n-1)) + (#5s in s(n-2))

最后,尾随零的数量是min(#2s in s(n), #5s in s(n))

上述算法(如果使用循环或memoized递归实现)为O(n)。您的尝试在n中是指数级的,这就是为什么即使n = 30也需要很长时间才能运行的原因。我并不打算抨击你的尝试,但理解这些错误是件好事 - 你的代码很慢,主要有两个原因:

首先,将非常大的整数乘以完全精度(正如您对BigInteger所做的那样)非常慢,因为每次乘法的位数可以加倍。如果您只关心尾随零的数量,则无需完全精确。

其次,忽略乘法的成本,s的递归实现仍然是指数时间,但它并非必须如此。请注意,您多次计算相同的值 - s(n-2)s(n)s(n-1)分开计算的,但s(n-2)的值明显相同。诀窍是通过记住先前计算的结果来memoize the recursion,以避免重新计算。或者,您可以使用循环计算Fibonacci类序列:

// Computes the n-th Fibonacci number in O(n) time
int[] fib = new int[n + 1];
fib[0] = 0;
fib[1] = 1;
for (int i = 2; i <= n; i++)
    fib[i] = fib[i-1] + fib[i-2];
return fib[n];

这是一种比memoized递归更简单的方法,至少对于这个问题。

答案 1 :(得分:1)

public static void main(String[] args) throws IOException {

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    int n = Integer.parseInt(br.readLine());

    int s0 = Integer.parseInt(br.readLine());

    int s1 = Integer.parseInt(br.readLine());

    int num21 = findNumberOf2s(s0);

    int num22 = findNumberOf2s(s1);

    int num51 = findNumberOf5s(s0);

    int num52 = findNumberOf5s(s1);

    int arr2[] = new int[n + 1];
    arr2[0] = num21;
    arr2[1] = num22;
    for (int i = 2; i <= n; i++)
        arr2[i] = arr2[i - 1] + arr2[i - 2];

    int arr5[] = new int[n + 1];
    arr5[0] = num51;
    arr5[1] = num52;
    for (int i = 2; i <= n; i++)
        arr5[i] = arr5[i - 1] + arr5[i - 2];

    System.out.println(Math.min(arr2[n], arr5[n]));

}

static int findNumberOf2s(int num) {
    int num2 = 0;
    while (num % 2 == 0) {
        num = num / 2;
        num2++;
    }
    return num2;
}

static int findNumberOf5s(int num) {
    int num5 = 0;
    while (num % 5 == 0) {
        num = num / 5;
        num5++;
    }
    return num5;
}