【问题标题】:Prime factorization of larger numbers than int比 int 更大的数的素数分解
【发布时间】:2016-11-07 15:39:33
【问题描述】:

这个程序在 C 中对数字进行质因数分解。

#include <stdio.h>

int main(void) {
    int number, i, p, n, factors, count;
    int numbers[1000000];
    int counter = 0;
    char text[100000]; 

    for (count = 0; count < 1000000; count++) {
        fgets(text, 10000000, stdin);
        if (sscanf(text, "%d", &number) == 1) {
            if (number == 0)
                break;
            numbers[count] = number;
        } else {
            numbers[count] = 0;
        }
    }
    counter = 0; 
    for (i = 0; i < count; i++) {
        if ((numbers[i] < 0) || (numbers[i] == 0)) {
            fprintf(stderr, "Error: Wrong Input!\n");
            return 100;
            break;
        }
        number = numbers[i];
        printf("Prime factorization of nubmer %d is:\n", number);
        factors = 0;
        for (p = 2; p * p <= number; p += 1 + (p & 1)) {
            if (number % p == 0) {
                n = 0;
                factors++;
                do {
                    number /= p;
                    n++;
                } while (number % p == 0);
                if (n == 1) {
                    printf("%d ", p);
                    ++counter;
                } else 
                    printf("%d^%d ", p, n);
                ++counter;

                if (count > 0 && number != 1) 
                    printf("x ");
            }
        }
        if (factors == 0 || number != 1)
            printf("%d", number);
        printf("\n");
    }
    return 0;
}

该程序适用于小于 108 的数字。但我的问题是,是否有办法让这个程序甚至适用于像 1012 这样的数字。我知道 int 是不够的,但是当我尝试例如 long int 时,它没有用。我也听说过一些关于 malloc 的事情,但我一直未能实现(理解)它。

【问题讨论】:

  • 给定 32 位系统,intlong 都可以包含高达 (2^32)/2 -1 的数字。您可以使用long long,它是一个 64 位类型,或者更好的是,uint64_t,它给出 (2^64)-1。整数值的范围通常在每本初级 C 编程书籍的第一章中都有说明。
  • 你想看看 C 整数类型。
  • @Lundin Win64 也有 32 位 intlong;后者是为了与 Win32 兼容。

标签: c int primes long-integer


【解决方案1】:

分解大数通常需要比简单的试除法更微妙的方法。这是一个可能的大纲方法:

  1. 列出所有质数,例如 25,000。
  2. 使用该列表删除所有低于 25,000 的质因数。
  3. 如果余数 > 1,则使用 Miller-Rabin 检验或类似方法检查余数是否为素数。
  4. 如果余数是质数,那么您找到了最后一个因数。
  5. 如果余数不是素数,那么您将不得不对其进行因式分解。恐怕这将不可避免地很慢。

【讨论】:

  • 您可能需要补充一点,您需要使用 2、3、5、7、11、13、17、19、23、29、31 和 37 运行 Miller-Rabin 测试以避免低于 2^64 的误报。
  • @chqrlie 存在一组只有 7 个碱基,使得 MR 在 n
【解决方案2】:

您可以使用 long long。但可能真正的问题是,对不适合正常 int 的数字进行因式分解需要很长时间。例如。您正在尝试分解 10^12 范围内的素数,那么您将不得不进行大约 10^6 次除法。

关于 malloc 的东西根本无法帮助您解决这个问题,因为更大的值将需要更长的时间来分解。所以,如果你想知道 malloc 是如何工作的,我建议为此单独开一个问题。

【讨论】:

  • 10 ^ 6 == 12 in C :-) 说:1000000 分区对于当前的 x86 或 ARMv7/8A CPU 来说并不是真正的问题。
  • 啊,我没有检查代码,因为这与问题无关。好吧,我们不知道他手头有多少个 CPU,它们的速度有多快。让我们等待下一个问题“我怎样才能加快我的代码?”
【解决方案3】:

下面是使用unsigned long long 重新编写的代码。 (我扔掉了文件内容以保持这个最小示例。)这是否适用于您的目的取决于您的系统如何定义long long(在我的系统上它是 64 位)。我还重新修改了输出格式,使其与 Unix dc 命令的后缀符号兼容,这样我就可以轻松检查结果是否正确:

#include <stdio.h>
#include <stdlib.h>

int main() {

    unsigned long long large = 18446744073709551615ULL; // 2^64 - 1

    for (unsigned long long counter = large - 1000; counter < large; counter++) {

        unsigned long long number = counter;

        printf("Prime factorization of %llu is:", number);

        unsigned long factors = 0;

        for (unsigned long long p = 2; p * p <= number; p += 1 + (p & 1)) {

            if (number % p == 0) {
                factors++;

                unsigned long n = 0;

                do {
                    number /= p;
                    n++;
                } while (number % p == 0);

                if (n == 1) {
                    printf(" %llu", p);
                }
                else {
                    printf(" %llu %lu ^", p, n);
                }

                if (number != 1 && factors > 1) {
                    printf(" *");
                }
            }
        }

        if (factors == 0 || number != 1) {
            factors++;

            printf(" %llu", number);
        }

        if (factors > 1) {
            printf(" *");
        }

        printf("\n");
    }

    return 0;
}

样本输出

% ./a.out
Prime factorization of 18446744073709550615 is: 5 563 * 751 * 8725722280871 *
Prime factorization of 18446744073709550616 is: 2 3 ^ 3 * 41 * 7523 * 8243 * 14479 * 20879 *
Prime factorization of 18446744073709550617 is: 79 557 * 419215600611539 *
Prime factorization of 18446744073709550618 is: 2 2298974999 * 4011949691 *
Prime factorization of 18446744073709550619 is: 3 3 ^ 1008659 * 677347590683 *
Prime factorization of 18446744073709550620 is: 2 2 ^ 5 * 7 * 149 * 233 * 3795329598449 *
Prime factorization of 18446744073709550621 is: 11 23 * 72912031911895457 *
Prime factorization of 18446744073709550622 is: 2 3 * 479909 * 6406334004193 *
Prime factorization of 18446744073709550623 is: 3421377637 5391612979 *
Prime factorization of 18446744073709550624 is: 2 5 ^ 61 * 593 * 1699 * 9379762391 *
Prime factorization of 18446744073709550625 is: 3 5 4 ^ * 13 * 756789500459879 *
Prime factorization of 18446744073709550626 is: 2 3743461 * 2463862195133 *
Prime factorization of 18446744073709550627 is: 7 1283 * 4339 * 627089 * 754877 *
Prime factorization of 18446744073709550628 is: 2 2 ^ 3 2 ^ * 101 * 293 * 42751 * 405025111 *
Prime factorization of 18446744073709550629 is: 17 43 * 613 * 66457 * 619442699 *
...

这运行速度较慢但合理。您可以通过将 unsigned long long 替换为 uint128_t 来在某些系统上进一步推动这一点,某些编译器在某种程度上支持:

typedef unsigned __int128 uint128_t;

(将unsigned long 声明增加到unsigned long long。)您需要为uint128_t 类型提供数字打印例程,因为printf() 不会直接处理它们。我用上面的代码试过了,它成功了:

Prime factorization of 340282366920938463426481119284349108124 is: 2 2 ^ 31 * 6131 * 7654271 * 21163829 * 21491837 * 128562653437 *

% dc
2 2 ^ 31 * 6131 * 7654271 * 21163829 * 21491837 * 128562653437 * p 
340282366920938463426481119284349108124

但我在运行它时从未见过它完成超过一个数字!

【讨论】:

  • 超过 64 位会导致实际素数的计算时间过长。
【解决方案4】:

number 使用unsigned long long 类型,主要因素将带你到1019,代价是更长的计算时间。

但是请注意,使用自动存储定义大型本地数组可能会导致问题,尤其是当它达到 8 兆字节的大小时,例如类型 unsigned long long(这种类型至少为 64 位宽)。从堆中分配它更安全。

这是代码的改编版本:

#include <stdio.h>
#include <stdlib.h>

#define NUMBER_MAX  1000000

int main(void) {
    unsigned long long *numbers;
    unsigned long long number, p;
    int i, n, factors, count;
    char text[100]; 

    numbers = calloc(NUMBER_MAX, sizeof(*numbers));
    if (numbers == NULL) {
        printf("cannot allocate number array\n");
        return 1;
    }

    for (count = 0; count < NUMBER_MAX; count++) {
        if (!fgets(text, sizeof text, stdin)) {
            break;
        }
        if (sscanf(text, "%llu", &number) == 1 && number > 0) {
            numbers[count] = number;
        } else {
            fprintf(stderr, "Error: Wrong Input!\n");
            return 100;
        }
    }
    for (i = 0; i < count; i++) {
        number = numbers[i];
        printf("Prime factorization of nubmer %llu is:\n", number);
        factors = 0;
        for (p = 2; p < 0x100000000 && p * p <= number; p += 1 + (p & 1)) {
            if (number % p == 0) {
                n = 0;
                factors++;
                do {
                    number /= p;
                    n++;
                } while (number % p == 0);
                if (n == 1) {
                    printf("%llu ", p);
                } else {
                    printf("%llu^%d ", p, n);
                }
                if (number != 1) {
                    printf("* ");
                }
            }
        }
        if (factors == 0 || number != 1) {
            printf("%llu", number);
        }
        printf("\n");
    }
    free(numbers);
    return 0;
}

【讨论】:

    猜你喜欢
    • 2012-05-20
    • 2014-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多