Вот мой код для сита эратосфенов в C. Он дает мне следующий результат.
2 3 5 7 11 13 17 19 23 25 31 35 37 41 43 47
Мой вывод включает в себя 25 и 35 также, которые не являются простыми числами, и не включает 29.
Может ли кто-нибудь сказать мне, где я ошибаюсь.
#include<stdio.h>
#include<math.h>
int main()
{
int i,a[50],b[50],j,n=0,s;
for(i=0;i<50;i++)
a[i] = 1;
a[0]=a[1] = 0;
for(i=2;i<50;i++)
if(a[i])
for(j=pow(i,2);j<50;j+=i)
a[j] = 0;
for(i=0;i<50;i++)
if(a[i])
{
b[n] = i;
n++;
}
for(j=0;j<n;j++)
printf("%d\n",b[j]);
return 0;
}
Скомпилированный с gcc версии 4.4.7 (Ubuntu/Linaro 4.4.7-2ubuntu1), ваш код возвращает правильный результат. Вероятно, проблема связана с компилятором и с реализацией pow()
.
Вероятно, у вас есть наивная реализация pow
которая вычисляет pow(x,y)
как exp(y*log(x))
. Это арифметика с плавающей запятой, и она страдает от общих проблем с плавающей точкой. Это означает, что результат pow(x,y)
преобразованный в целое, будет усечен из-за двойного арифметического log(x)*y
и экспоненциальности, который вернет двойное значение, немного меньшее, чем целое x*y
.
Измените код на
for( j = i * i; j < 50; j += i)
a[j] = 0;
Кроме того, мы можем выполнять итерацию только до sqrt (n), потому что второй цикл будет выполнен только тогда:
for( i = 2; i < sqrt(50); i++)
if( a[i]) // if not marked
/* mark starting from i*i because i*j for j<i
* has been already marked when i was j */
for( j = i * i; j < 50; j += i)
a[j] = 0;
связанная с этим проблема: функция блокировки блоков кода не работает в c
2*i
уже был помечен как не простой, когда i
было 2
. Аналогично для 3*i
, 4*i
и т. Д. Достаточно начать с i*i
.
Как наблюдали другие, ошибка находится в функции pow
. Я не могу воспроизвести вашу ошибку с кодом, который вы ppow
, но когда я откатываю свою собственную функцию ppow
:
double ppow(double a, double x)
{
return exp(log(a) * x);
}
мой список соответствует вашим. Я думаю, что стандартно-совместимые реализации pow
должны рассматривать целочисленные показатели как частный случай, который может принимать отрицательные основания, поэтому ваш pow
кажется несоответствующим.
Вместо pow(i, 2)
используйте i*i
. Это должно быть быстрее, и вам не придется ссылаться на библиотеку математики.
pow
работает с числами с плавающей точкой. Они не являются точными, тем более, когда они являются результатом вычисления, как в ppow
выше реализации ppow
. Для ppow(20, 2)
я получаю 399.9999999999999, например, который при преобразовании в int станет 399. Возможно, вы могли бы избежать округления: ppow(i, 2) + 0.5
, но я рекомендую использовать простое умножение для целочисленных степеней особенно для простых, таких как квадраты.
Я пытаюсь воспроизвести ваш результат, но я не могу.
Информация о ОС:
$ uname -a Linux 3.13.0-29-generiС# 53-Ubuntu SMP Wed Jun 4 21:00:20 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
Информация о компиляторе:
$ gcc --version gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
Обобщение:
$ gcc sieve_of_eratosthenes.c -lm -o sieve_of_eratosthenes
Запуск:
$./sieve_of_eratosthenes
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
Когда функция pow
использует блок с плавающей запятой на вашем процессоре, это может привести к исключению.
Когда такое исключение возникает во время выполнения этой функции, оно может вернуть неверное значение.
Вероятно, это относится к вашей системе, когда вы вызываете pow(i,2)
когда i
равно 5
.
В качестве косвенных доказательств, подтверждающих эту гипотезу, обратите внимание, что каждое число (в указанном диапазоне [0-49]), которое кратно 5 и просто больше 5, появляется в вашем списке простых чисел.
Вот фрагмент кода для извлечения исключений, которые могли произойти в результате операции FP:
#include <fenv.h>
#include <stdio.h>
void print_fe_exceptions()
{
printf("Exceptions raised:");
if (fetestexcept(FE_DIVBYZERO)) printf(" FE_DIVBYZERO");
if (fetestexcept(FE_INEXACT )) printf(" FE_INEXACT ");
if (fetestexcept(FE_INVALID )) printf(" FE_INVALID ");
if (fetestexcept(FE_OVERFLOW )) printf(" FE_OVERFLOW ");
if (fetestexcept(FE_UNDERFLOW)) printf(" FE_UNDERFLOW");
feclearexcept(FE_ALL_EXCEPT);
printf("\n");
}
И вот описание каждого исключения:
FE_DIVBYZERO // Pole error occurred in an earlier floating-point operation
FE_INEXACT // Inexact result: rounding was necessary to store the result of an earlier floating-point operation
FE_INVALID // Domain error occurred in an earlier floating-point operation
FE_OVERFLOW // The result of an earlier floating-point operation was too large to be representable
FE_UNDERFLOW // The result of an earlier floating-point operation was subnormal with a loss of precision
pow()
. Попробуйтеi*i
вместо этого.