【问题标题】:Calculating large power of a number in c计算c中数字的大幂
【发布时间】:2015-11-26 21:40:11
【问题描述】:

我正在用 c 编写一个程序来存储 2^100000,并且我正在使用数组来存储结果。 完整代码如下:

#include <stdio.h>
#include <math.h>

int main()
{
    int test, n, i, j, x, resul;
    int a[200], m, temp;
    scanf("%d", &test);
    for (i = 0; i < test; i++) {
        a[0] = 3;  // initializes array with only 1 digit, the digit 1.
        m = 1;     // initializes digit counter
        scanf("%d", &n);
        temp = 0;  // Initializes carry variable to 0.
        for (i = 1; i < n; i++) {
            for (j = 0; j < m; j++) {
                x = a[j] * 2 + temp; //x contains the digit by digit product
                a[j] = x % 10; //Contains the digit to store in position j
                temp = x / 10; //Contains the carry value that will be stored on later indexes
            }
            while (temp > 0) { //while loop that will store the carry value on array.
                a[m] = temp % 10;
                temp = temp / 10;
                m++; // increments digit counter
            }
        }
        for (i = m - 1; i >= 0; i--) //printing answer
            printf("%d", a[i]);
    }
    return 0;
}

有人能告诉我一种更有效的方法来降低时间复杂度吗?

【问题讨论】:

  • 这只是一小部分代码,将整个函数与变量定义和调用代码一起发布。
  • 我已根据您的要求添加了整个代码。
  • 我提供了一个详尽的答案,有帮助吗?

标签: c


【解决方案1】:

二进制中的 2^n 是一个 (n+1) 位整数,除了最高有效位设置为 1 之外,每个位都设置为 0。例如:32 = 2^5 = 0b100000

同样,可以计算 2^100000,方法是将零化的 100001 位长整数中的第 100001 位设置为 1。O(1) 尽可能节省时间。 p>

【讨论】:

  • 当然!但是如果你想以十进制打印它,用一些技巧以十进制计算它还是转换平凡的 base 2 表示更有效?
  • 假设我们一般谈论的是 2 的幂,我看不出它如何在没有计算并存储在某处的数字的情况下以十进制打印,因为数字必须从开始输出由最重要的一个(从左到右)。不过我不确定……
  • 谢谢,但我认为将二进制转换为十进制不会降低时间复杂度。或者你能告诉我一两种有效地转换成十进制的方法吗?
  • 时间复杂度只是一个提示算法的性能如何随着更大的输入而扩展,具有相同复杂度的两个算法可以——而且往往不会——产生不同的性能.更有效二进制到十进制转换算法将使用整个字节/多字节类型来进行计算,而不是单个基数为 10 位的数字。这至少可以为您节省x%10s。我会尽快用代码示例更新我的答案。
【解决方案2】:

你的代码有几个问题:

  • 数组a 的大小仅为200 位数。这对于具有30103 数字的2^100000 来说太小了。您应该增加数组大小并检查乘法算法中的溢出。

  • 您初始化a[0] = 3; 并将其注释为数字1。确实你应该写a[0] = 1;

  • 第二个循环for (i = 1; i &lt; n; i++) 应该包含所需的功率数:你应该写for (i = 1; i &lt;= n; i++)

  • 您对外部循环和第二级循环使用相同的循环变量,导致不正确的行为。

  • 您没有测试scanf 的返回值,导致无效输入出现未定义行为。

  • 您不检查溢出,对大值调用未定义的行为。

这是一个更正的版本:

#include <stdio.h>

int main()
{
    int n, i, j, x, m, test, temp;
    int a[32000];

    if (scanf("%d", &test) != 1)
        return 1;

    while (test-- > 0) {
        if (scanf("%d", &n) != 1)
            break;
        a[0] = 1;  // initializes array with only 1 digit, the number 1.
        m = 1;     // initializes digit counter
        temp = 0;  // Initializes carry variable to 0.
        for (i = 1; i <= n; i++) {
            for (j = 0; j < m; j++) {
                x = a[j] * 2 + temp; //x contains the digit by digit product
                a[j] = x % 10; //Contains the digit to store in position j
                temp = x / 10; //Contains the carry value that will be stored on later indexes
            }
            // while loop that will store the carry value on array.
            if (temp > 0) {
                if (m >= (int)(sizeof(a)/sizeof(*a)))
                    break;
                a[m++] = temp;
                temp = 0;
            }
        }
        if (temp > 0) {
            printf("overflow");
        } else {
            for (i = m - 1; i >= 0; i--) //printing answer
                putchar('0' + a[i]);
        }
        printf("\n");
    }
    return 0;
}

在我的笔记本电脑上使用输入 1100000 运行此代码大约需要 6.5 秒。这确实是非常低效的。使用一些不会真正改变这种简单迭代算法复杂性的优化技术仍然可以产生显着的性能提升,可能快 100 倍

这里有一些想法:

  • 在数组中每个 int 存储 9 位而不是 1。

  • 在每次迭代中乘以 2^29 而不仅仅是 2,使用 long long 计算中间结果。将第一步初始化为1 &lt;&lt; (n % 29),以说明n 不是29 的倍数。 2^292小于10^9的最大幂。

这是实现这两个想法的版本:

#include <stdio.h>

int main() {
    int n, i, j, m, test, temp;
    int a[32000];

    if (scanf("%d", &test) != 1)
        return 1;

    while (test-- > 0) {
        if (scanf("%d", &n) != 1)
            break;
        i = n % 29;
        n /= 29;
        a[0] = 1 << i;
        m = 1;
        temp = 0;
        for (i = 1; i <= n; i++) {
            for (j = 0; j < m; j++) {
                long long x = a[j] * (1LL << 29) + temp;
                a[j] = x % 1000000000;
                temp = x / 1000000000;
            }
            if (temp > 0) {
                if (m >= (int)(sizeof(a)/sizeof(*a)))
                    break;
                a[m++] = temp;
                temp = 0;
            }
        }
        if (temp > 0) {
            printf("overflow");
        } else {
            printf("%d", a[m - 1]);
            for (i = m - 2; i >= 0; i--)
                printf("%09d", a[i]);
        }
        printf("\n");
    }
    return 0;
}

在同一台笔记本电脑上运行它只需 33 毫秒即可计算出正确的结果,这快了 200 倍

时间复杂度相同,但执行效率更高。

【讨论】:

  • 非常感谢。这非常有用。这是解决问题的最有效方法。
  • @AnubhavJain:请您投票并检查答案是否被接受,以便认为问题已解决?
  • @AnubhavJain:加速代码假定int 至少有 32 位。您尝试了哪个代码,发现不起作用
  • 我尝试在每次迭代中运行第二个存储 2^29 的代码,但即使对于 n=50 它也会溢出。
  • @AnubhavJain:确实在两个版本中都存在错误:temp 必须在将其存储到 a[m++] 后重置为 0。我更新了答案。
【解决方案3】:

请注意,原生 C 整数是有限的,实际上是与计算机字长相关的 2 的幂(例如,通常为 32 位或 64 位)。了解&lt;stdint.h&gt;int32_tint64_t

也许你想要一些 bignums(或 bigints),又名arbitrary precision arithmetic

底层算法非常聪明(并且比你在学校学到的幼稚算法更有效)。所以不要试图重新发明它们,而是使用像GMPlib这样的库

【讨论】:

  • 重新发明是一种非常有效的学习方式。就像数学学生学习如何证明定理一样,CS 学生应该尝试实现算法,并自己找出编写高效代码所需的条件。 GMP 确实是一个很好的基准,对于严肃的工作来说绝对是一个更好的选择。但我相信 OP 正在完成一项任务或只是尝试想法......玩得开心,学习!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-22
  • 1970-01-01
  • 2011-05-05
  • 1970-01-01
  • 2012-07-06
相关资源
最近更新 更多