Ошибка сегментации в C / GMP: вычислить коэффициенты с Поллард Ро - PullRequest
1 голос
/ 20 февраля 2012

Я опытный программист, но плохо знаком с C. Я пытаюсь изучать C, чтобы я мог использовать библиотеку gmp для написания быстрых программ с большими целыми числами; в конце концов я намереваюсь написать части производительности моих программ в функциях C, которые вызываются через интерфейс сторонних функций из других языков. В качестве средства изучения и языка C, и библиотеки gmp я написал программу факторизации целых чисел polho rho, показанную ниже (да, я знаю, что это не лучшая возможная реализация polard rho, но она проста, и на данный момент я просто хочу начать). Моя программа компилируется правильно, но при запуске она умирает с сообщением «Ошибка сегментации». Я предполагаю, что это означает, что мои указатели где-то перепутались, но я этого не вижу. Вероятно, больше, чем один. Может кто-нибудь, пожалуйста, посмотрите на мою программу и скажите, где я ошибся? И указать на какие-либо проблемы со стилем или что-то еще, что я могу улучшить? Большое спасибо, Фил

/* rho.c -- pollard rho factorization
 * usage: rho n -- prints factors of n then exits
 * compile as gcc -lgmp -o rho rho.c */

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

typedef struct list {
    void *data;
    struct list *next;
} List;

List *insert(void *data, List *next)
{
    List *new;

    if (! (new = malloc(sizeof(List))))
        return NULL;
    new->data = data;
    new->next = next;
    return new;
}

List *insert_in_order(void *x, List *xs)
{
    if (xs == NULL || mpz_cmp(x, xs->data) < 0)
    {
        return insert(x, xs);
    } else {
        List *head = xs;
        while (xs->next != NULL && mpz_cmp(x, xs->next->data) < 0)
        {
            xs = xs->next;
        }
        xs->next = insert(x, xs->next);
        return head;
    }
}

void rho_factor(mpz_t f, mpz_t n, long long unsigned c)
{
    mpz_t t, h, d, r;

    mpz_init_set_ui(t, 2);
    mpz_init_set_ui(h, 2);
    mpz_init_set_ui(d, 1);
    mpz_init_set_ui(r, 0);

    while (mpz_cmp_si(d, 1) == 0)
    {
        mpz_mul(t, t, t);
        mpz_add_ui(t, t, c);
        mpz_mod(t, t, n);

        mpz_mul(h, h, h);
        mpz_add_ui(h, h, c);
        mpz_mod(h, h, n);

        mpz_mul(h, h, h);
        mpz_add_ui(h, h, c);
        mpz_mod(h, h, n);

        mpz_sub(r, t, h);
        mpz_gcd(d, r, n);
    }

    if (mpz_cmp(d, n) == 0)
    {
        rho_factor(f, n, c+1);
    }
    else if (mpz_probab_prime_p(d, 25))
    {
        mpz_set(f, d);
    }
    else
    {
        rho_factor(f, d, c+1);
    }

    mpz_clears(t, h, d, r);
}

void rho_factors(List *fs, mpz_t n)
{
    mpz_t f;
    mpz_init_set_ui(f, 0);

    while (! (mpz_probab_prime_p(n, 25)))
    {
        rho_factor(f, n, 1);
        fs = insert_in_order(f, fs);
        mpz_divexact(n, n, f);
    }

    mpz_clear(f);
    insert_in_order(n, fs);
}

int main(int argc, char *argv[])
{
    mpz_t f, n;
    mpz_init_set_ui(f, 0);
    List *fs = NULL;

    if (argc<2)
    {
        printf("usage: rho n\n");
        return 1;
    }

    mpz_init_set_str(n, argv[1], 10);

    if (mpz_probab_prime_p(n, 25))
    {
        printf("%s\n", argv[1]);
        return 0;
    }

    printf("%s", argv[1]);
    rho_factors(fs, n);
    while (fs != NULL) {
        printf(" %s", mpz_get_str(NULL, 10, fs->data));
        fs = fs->next;
    }

    return 0;
}

РЕДАКТИРОВАТЬ1: Благодаря Даниэлю Фишеру, я добился некоторого прогресса и больше не получаю ошибку сегментации. Код, показанный ниже, правильно печатает коэффициент 127, когда rho_factor вызывается с n = 1234567, но rho_factors возвращает нулевой список, поэтому с rho_factors все еще что-то не так. Я предполагаю, что проблема в том, что я не могу продолжать инициализировать одну и ту же переменную так, как я делаю, но я не знаю правильный способ сделать это. Каким-то образом мне нужно сделать копию каждого найденного фактора, а затем вставить эту копию в фс. Большое спасибо, Фил

/* rho.c -- pollard rho factorization
 * usage: rho n -- prints factors of n then exits
 * compile as gcc -lgmp -o rho rho.c */

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

typedef struct list {
    void *data;
    struct list *next;
} List;

List *insert(void *data, List *next)
{
    List *new;

    if (! (new = malloc(sizeof(List))))
        return NULL;
    new->data = data;
    new->next = next;
    return new;

List *insert_in_order(void *x, List *xs)
{
    if (xs == NULL || mpz_cmp(x, xs->data) < 0)
    {
        return insert(x, xs);
    } else {
        List *head = xs;
        while (xs->next != NULL && mpz_cmp(x, xs->next->data) < 0)
        {
            xs = xs->next;
        }
        xs->next = insert(x, xs->next);
        return head;
    }
}

void rho_factor(mpz_t f, mpz_t n, long long unsigned c)
{
    mpz_t t, h, d, r;

    mpz_init_set_ui(t, 2);
    mpz_init_set_ui(h, 2);
    mpz_init_set_ui(d, 1);
    mpz_init_set_ui(r, 0);

    while (mpz_cmp_si(d, 1) == 0)
    {
        mpz_mul(t, t, t);
        mpz_add_ui(t, t, c);
        mpz_mod(t, t, n);

        mpz_mul(h, h, h);
        mpz_add_ui(h, h, c);
        mpz_mod(h, h, n);

        mpz_mul(h, h, h);
        mpz_add_ui(h, h, c);
        mpz_mod(h, h, n);

        mpz_sub(r, t, h);
        mpz_gcd(d, r, n);
    }

    if (mpz_cmp(d, n) == 0)
    {
        rho_factor(f, n, c+1);
    }
    else if (mpz_probab_prime_p(d, 25))
    {
        mpz_set(f, d);
    }
    else
    {
        rho_factor(f, d, c+1);
    }

    mpz_clears(t, h, d, r, NULL);
}

void rho_factors(List *fs, mpz_t n)
{
    while (! (mpz_probab_prime_p(n, 25)))
    {
        mpz_t f;
        mpz_init_set_ui(f, 0);

        rho_factor(f, n, 1);
        fs = insert_in_order(f, fs);
        mpz_divexact(n, n, f);
    }

    insert_in_order(n, fs);
}

int main(int argc, char *argv[])
{
    mpz_t f, n;
    mpz_init_set_ui(f, 0);
    List *fs = NULL;

    if (argc<2)
    {
        printf("usage: rho n\n");
        return 1;
    }

    mpz_init_set_str(n, argv[1], 10);

    if (mpz_probab_prime_p(n, 25))
    {
        printf("%s is prime\n", argv[1]);
        return 0;
    }

    rho_factor(f, n, 1);
    printf("%s\n", mpz_get_str(NULL, 10, f));

    printf("%s", argv[1]);
    rho_factors(fs, n);
    while (fs != NULL) {
        printf(" %s", mpz_get_str(NULL, 10, fs->data));
        fs = fs->next;
    }

    return 0;
}

EDIT2: Понял. Спасибо Даниэлю Фишеру. Окончательная версия показана ниже.

/* rho.c -- pollard rho factorization
 * usage: rho n -- prints factors of n then exits
 * compile as gcc -lgmp -o rho rho.c */

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

typedef struct list {
    void *data;
    struct list *next;
} List;

List *insert(void *data, List *next)
{
    List *new;

    new = malloc(sizeof(List));
    new->data = data;
    new->next = next;
    return new;
}

List *insert_in_order(void *x, List *xs)
{
    if (xs == NULL || mpz_cmp(x, xs->data) < 0)
    {
        return insert(x, xs);
    }
    else
    {
        List *head = xs;
        while (xs->next != NULL && mpz_cmp(x, xs->next->data) < 0)
        {
            xs = xs->next;
        }
        xs->next = insert(x, xs->next);
        return head;
    }
}

void rho_factor(mpz_t f, mpz_t n, long long unsigned c)
{
    mpz_t t, h, d, r;

    mpz_init_set_ui(t, 2);
    mpz_init_set_ui(h, 2);
    mpz_init_set_ui(d, 1);
    mpz_init_set_ui(r, 0);

    while (mpz_cmp_si(d, 1) == 0)
    {
        mpz_mul(t, t, t);
        mpz_add_ui(t, t, c);
        mpz_mod(t, t, n);

        mpz_mul(h, h, h);
        mpz_add_ui(h, h, c);
        mpz_mod(h, h, n);

        mpz_mul(h, h, h);
        mpz_add_ui(h, h, c);
        mpz_mod(h, h, n);

        mpz_sub(r, t, h);
        mpz_gcd(d, r, n);
    }

    if (mpz_cmp(d, n) == 0)
    {
        rho_factor(f, n, c+1);
    }
    else if (mpz_probab_prime_p(d, 25))
    {
        mpz_set(f, d);
    }
    else
    {
        rho_factor(f, d, c+1);
    }

    mpz_clears(t, h, d, r, NULL);
}

void rho_factors(List **fs, mpz_t n)
{
    while (! (mpz_probab_prime_p(n, 25)))
    {
        mpz_t *f = malloc(sizeof(*f));
        mpz_init_set_ui(*f, 0);

        rho_factor(*f, n, 1);
        *fs = insert_in_order(*f, *fs);
        mpz_divexact(n, n, *f);
    }

    *fs = insert_in_order(n, *fs);
}

int main(int argc, char *argv[])
{
    mpz_t f, n;
    mpz_init_set_ui(f, 0);
    List *fs = NULL;

    if (argc<2)
    {
        printf("usage: rho n\n");
        return 1;
    }

    mpz_init_set_str(n, argv[1], 10);

    if (mpz_probab_prime_p(n, 25))
    {
        printf("%s is prime\n", argv[1]);
        return 0;
    }

    printf("Factors of %s:", argv[1]);
    rho_factors(&fs, n);
    while (fs != NULL) {
        printf(" %s", mpz_get_str(NULL, 10, fs->data));
        fs = fs->next;
    }
    printf("\n");

    return 0;
}

1 Ответ

3 голосов
/ 20 февраля 2012

Вы не можете повторно использовать mpz_t, например:

void rho_factors(List *fs, mpz_t n)
{
    mpz_t f;
    mpz_init_set_ui(f, 0);

    while (! (mpz_probab_prime_p(n, 25)))
    {
        rho_factor(f, n, 1);
        fs = insert_in_order(f, fs);
        mpz_divexact(n, n, f);
    }

    mpz_clear(f);
    insert_in_order(n, fs);
}

Когда GMP перераспределяет хранилище, на которое указывает f, старое хранилище равно free d. Это может произойти в цикле while, но не обязательно. Если это не так, все записи списка указывают на один и тот же номер.

Но после цикла, когда вы mpz_clear(f), каждый data указатель в списке fs становится диким указателем, а когда вы затем insert_in_order(n, fs), вы разыменовываете ранее free d указателей. (Это очень вероятно происходит уже во время некоторой вставки в цикле while, но здесь это гарантировано.)

Также, в rho_factor, вы должны позвонить

mpz_clears(t, h, d, r, NULL);

список аргументов для mpz_clears должен быть NULL -определенным.

Дальнейшие проблемы:

  • Вы передаете List *, поэтому изменения, внесенные в fs в rho_factors, не влияют на fs в main. Передайте List ** и разыщите его там, где это необходимо, также вы забыли присвоить окончательное возвращаемое значение insert_in_order.
  • Локальная переменная f выходит из области видимости в конце блока, что приводит к повреждению. malloc указывает на mpz_t, чтобы поддержать их.

    void rho_factors (Список ** fs, mpz_t n) { while (! (mpz_probab_prime_p (n, 25))) { mpz_t * f = malloc (sizeof (* f)); mpz_init_set_ui (* f, 0);

        rho_factor(*f, n, 1);
        *fs = insert_in_order(*f, *fs);
        mpz_divexact(n, n, *f);
    }
    
    *fs = insert_in_order(n, *fs);
    

    }

и main

List *fs = NULL;
/* snip */
rho_factors(&fs, n);

должен это сделать. Наконец (?) У вас есть сбой в insert_in_order, сравнение в цикле while обратное.

...