Как я могу преобразовать эту функцию C ++ из использования long в этот другой тип? - PullRequest
2 голосов
/ 14 февраля 2010

У меня есть эта оригинальная функция C ++ под названием "s":

long s(long n) {
  long sum = 0;
  long m;

  m = (long) sqrt(n);
  for (long i = 2; i < m; i++)
    if ((n % i) == 0) sum += (i + (n/i));
  if (n>1) sum += 1;
  if ((m*m) == n) sum += m;
  return sum;
}

Я изо всех сил пытался преобразовать эту функцию в использование типа mpz GMP, чтобы она допускала произвольно длинные целые числа.

Это моя попытка:

void s(mpz_t n, mpz_t *final)
{
    mpz_t sum;
    mpz_t m;
    mpz_t temp;

    mpz_init (sum);
    mpz_init (m);
    mpz_init (temp);

    mpz_set_str (sum, "0", 10);

    mpz_sqrt(m, n);
    for (mpz_t i, mpz_init(i), mpz_set_str(i, "2", 10); mpz_cmp(i,m)< 0; mpz_add_ui(i, i, 1))
    {
        mpz_mod(temp, n, i);
        if (mpz_cmp_si(temp, 0) == 0)
        {
            // use divexact since we know they are divisable
            mpz_divexact(temp, n, i);
            mpz_add(temp, temp, i);
            mpz_add(sum, sum, temp);
        }
    }

    if (mpz_cmp_si(n, 1) > 0) mpz_add_ui(sum, sum, 1);

    mpz_mul(temp, m, m);
    if (mpz_cmp(temp, n) == 0) mpz_add(sum, sum, m);
    final = sum;
}

С оригинальной программой можно ознакомиться здесь: http://pastebin.com/mf751592

Что я делаю не так? У меня изначально возникли проблемы, потому что я не мог вернуть тип mpz_t. Поэтому вместо этого я передал указатель на то, что я хочу, чтобы функция возвращала.

Я все еще борюсь с этим, хотя. Может ли кто-нибудь указать мне правильное направление?

Эта строка: for (mpz_t i, mpz_init(i), mpz_set_str(i, "2", 10); mpz_cmp(i,m)< 0; mpz_add_ui(i, i, 1))
Выдает эту ошибку: 23: ошибка: невозможно инициализировать массивы с использованием этого синтаксиса

Ответы [ 5 ]

3 голосов
/ 14 февраля 2010

Вы были в основном на правильном пути. Однако оба параметра функции должны иметь тип mpz_t. Таким образом, заголовок как:

void s(mpz_t n, mpz_t final)

Вам не нужно финал = сумма в конце. Вместо этого просто используйте final везде, где вы используете sum. Также выполните:

mpz_t i;
for (mpz_init_set_ui(i, 2); mpz_cmp(i,m) < 0; mpz_add_ui(i, i, 1))

для цикла for. Звонок как:

mpz_t final, n;
mpz_init(final);
mpz_init_set_ui(n, 5);
s(n, final);

РЕДАКТИРОВАТЬ: Как отметил Steve314, вы должны сделать mpz_clear для каждого mpz_init. Поскольку вы можете сделать так, чтобы вызывающий абонент прошел инициализацию финала, это оставляет очистку m, temp и i.

1 голос
/ 14 февраля 2010

gmp - библиотека на чистом C, IIRC.

Обеспокоенность вызывает отсутствие очистки переменных mpz_t (без деструкторов в C) и использование оператора присваивания в последней строке (без перегруженных операторов в C - это фактически POD memcpy). IIRC, mpz_clear - целочисленная функция очистки.

Замените присвоение либо mpz_set, либо mpz_swap - swap более эффективен, если вы собираетесь удалить временный объект, поскольку он позволяет избежать копирования базовых структур данных.

Тем не менее, все, что действительно могло бы объяснить, это утечка памяти.

Моя следующая подозрительная область - вызовы, в которых одна и та же переменная используется как для ввода, так и для вывода. Я не знаю о gmp, но многим библиотекам это не нравится при передаче указателей - входной параметр изменяется во время использования (так как он также является выходным), что приводит к повреждению. Может потребоваться несколько дополнительных временных.

1 голос
/ 14 февраля 2010

Просто объявите и инициализируйте i перед циклом for ..

mpz_t i;
mpz_init(i);
mpz_set_str(i, "2", 10);

for (; mpz_cmp(i,m)< 0; mpz_add_ui(i, i, 1)) {
   ...
}
1 голос
/ 14 февраля 2010

Ваша последняя строка должна быть * final = sum; В противном случае вы меняете адрес, на который указывает ваш указатель.

Или используйте ссылку вместо:)

0 голосов
/ 14 февраля 2010

Попробуйте использовать шаблоны

template <class myType>
myType s(myTypen) {
myType sum = 0;
myType m;

m = (myType) sqrt(n);
for (myType i = 2; i < m; i++)
  if ((n % i) == 0) sum += (i + (n/i));
 if (n>1) sum += 1;
if ((m*m) == n) sum += m;
return sum;
}
...