我正在使用具有不同功能的数学软件中的一个来查找给定区间内的所有Carmichael数字[a,b)
这是我的代码,但我不知道我是否已经正确完成,因为我无法测试它,因为最小的Carmichael数字是560,这对我的电脑来说太大了。
#include <stdio.h>
int main() {
unsigned int begin, end;
printf("Write an int (begin):\n");
scanf("%d", &begin);
printf("Write an int (end):\n");
scanf("%d", &end);
int i;
for( int i=begin; i<end; i++ ) {
long unsigned int a_nr = i-1;
int a[a_nr];
for( int j=0; j<a_nr; j++ ) {
a[j] = j;
}
unsigned long c_nr[a_nr];
for( int k=0; k<a_nr; k++ ) {
unsigned long current_c_nr;
int mod;
for( int l=0; l<i; l++ ) {
current_c_nr= current_c_nr * a[k];
}
mod = current_c_nr%i;
if( mod==a[k] && mod!=a[k] ) {
c_nr[k] = i;
}
}
}
return 0;
}
如果不正确,错误在哪里?
谢谢
应该防止P.S溢出。
答案 0 :(得分:9)
当你说“这是我的代码,但我不知道我是否做得正确或因为我无法测试它,因为最小的Carmichael数字是560,这对我的电脑来说太大了”然后结论是 - 你还没有正确完成。你应该能够在很短的一秒钟内处理561(560必须是一个错字)。即使你的算法原则上是正确的,如果它无法处理最小的Carmichael数,那么它就没用了。
n
是Carmichael,当且仅当它是复合的时候,对于a
1 < a < n
n
的{{1}}而言,a^(n-1) = 1 (mod n)
是相对素数的a
成立。要直接使用此定义,您需要:
1)测试n
和a^(n-1) (mod n)
是否相对优质的有效方法
2)计算gcd(a,b) = gcd(b,a%b)
首先 - 使用Euclidean algorithm获取最大公约数。它在循环中最有效地计算,但也可以通过基于gcd(a,0) = a
的简单重复unsigned int gcd(unsigned int a, unsigned int b){
return b == 0? a : gcd(b, a%b);
}
来定义。在C中,这只是:
a^k (mod n)
对于第二点 - 计算a^k
时几乎可能做的最糟糕的事情是首先通过重复乘法计算n
,然后按n
修改结果。相反 - 使用exponentiation by squaring,在中间阶段取余数(mod a^10 = (a^5)^2
)。它是一种基于观察的分而治之的算法,例如, a^11 = (a^5)^2 * a
和unsigned int modexp(unsigned int a, unsigned int p, unsigned int n){
unsigned long long b;
switch(p){
case 0:
return 1;
case 1:
return a%n;
default:
b = modexp(a,p/2,n);
b = (b*b) % n;
if(p%2 == 1) b = (b*a) % n;
return b;
}
}
。一个简单的C实现是:
unsigned long long
请注意使用b*b
来防止计算n
时出现溢出。
要测试n
是否为Carmichael,您可以首先测试0
是否为偶数,并在此情况下返回a
。否则,请在2
到n-1
范围内逐步执行数字gcd(a,n) == 1
。首先检查是否n
请注意,如果a
是复合的,那么在到达n
的平方根gcd(a,n) > 1
之前,您必须至少有一个a
。保留一个布尔标志,跟踪是否遇到过这样的a
,如果超过平方根而没有找到0
,请返回a
。对于gcd(a,n) == 1
a^(n-1) (mod n)
的{{1}},计算模幂运算0
。如果这与1不同,请返回a
。如果你的循环完成检查n
以下的所有0
而不返回int is_carmichael(unsigned int n){
int a,s;
int factor_found = 0;
if (n%2 == 0) return 0;
//else:
s = sqrt(n);
a = 2;
while(a < n){
if(a > s && !factor_found){
return 0;
}
if(gcd(a,n) > 1){
factor_found = 1;
}
else{
if(modexp(a,n-1,n) != 1){
return 0;
}
}
a++;
}
return 1; //anything that survives to here is a carmichael
}
,那么该数字是Carmichael,那么返回1.实现是:
int main(void){
unsigned int n;
for(n = 2; n < 100000; n ++){
if(is_carmichael(n)) printf("%u\n",n);
}
return 0;
}
一个简单的驱动程序:
C:\Programs>gcc carmichael.c
C:\Programs>a
561
1105
1729
2465
2821
6601
8911
10585
15841
29341
41041
46657
52633
62745
63973
75361
输出:
org.apache.commons.net.ftp.FTPConnectionClosedException: Connection closed without indication.
at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:317)
at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:294)
at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:483)
at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:608)
at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:582)
at org.apache.commons.net.ftp.FTP.pasv(FTP.java:1007)
at org.apache.commons.net.ftp.FTPClient._openDataConnection_(FTPClient.java:869)
at org.apache.commons.net.ftp.FTPClient._retrieveFile(FTPClient.java:1854)
at org.apache.commons.net.ftp.FTPClient.retrieveFile(FTPClient.java:1845)
这只需要大约2秒的时间来运行并匹配this列表的初始部分。
这可能是一种有点实用的方法,用于检查数百万左右的数字是否是Carmichael数字。对于更大的数字,你应该给自己一个很好的保理算法并使用Korseldt的标准,如Wikipedia entry中关于Carmichael数字的描述。