いろいろな関数を使うことになった。
- 最大公約数
- 最小公倍数
- 素因数分解
- 繰り返し二乗法
など基本的な数学系処理の練習にいいかも。。
さて、問題のカーマイケル数の部分だが、カーマイケル関数を使って、
となる最小のmを求める。このmをλ(n)と表記する。ここで、aとnはcoprimeである。カーマイケル数は、
となるnなので、(n-1)がλ(n)で割り切れるとき、nはカーマイケル数の候補となる。さらに、ここから素数を除く必要がある。素数の場合は、φ(n) = n-1となるので、それを除けばいい。逆に、カーマイケル数は、合成数なので、totient functionはn-1より小さくなるため、必ずφ(n) != n-1となり、この除去で偽陰性となることはない。
- map<int, int>factorize(int x) {
- map<int, int> ret;
- for (int i = 2; i*i <= x; i++) {
- while (x % i == 0) {
- ret[i]++;
- x /= i;
- }
- }
- if (x != 1)
- ret[x] = 1;
- return ret;
- }
- int mpow(int x, int p) {
- int ret = 1;
- while (p) {
- if (p & 1)
- ret = ret * x;
- x = x * x;
- p >>= 1;
- }
- return ret;
- }
- int gcd(int a, int b) {
- if (b)
- return gcd(b, a % b);
- return a;
- }
- int lcm(int a, int b) {
- return (long long int) a * b / gcd(a, b);
- }
- int carmichaelFunction(int x) {
- map<int, int> fact = factorize(x);
- int ret = 1;
- for (map<int, int>::iterator itr = fact.begin(); itr != fact.end(); itr++) {
- int p = itr->first;
- int k = itr->second;
- int lambda = (p > 2 || k < 3) ? mpow(p, k-1) * (p-1) : mpow(p, k-2);
- ret = lcm(ret, lambda);
- }
- return ret;
- }
- int eulerFunction(int x) {
- map<int, int> fact = factorize(x);
- double ret = x;
- for (map<int, int>::iterator itr = fact.begin(); itr != fact.end(); itr++)
- ret *= (1 - 1./itr->first);
- return ret + 0.5;
- }
- int main() {
- for (int i = 2; i < 100000; i++) {
- int lambda = carmichaelFunction(i);
- int phi = eulerFunction(i);
- if ((i-1) % lambda == 0 && phi != i-1) {
- printf("%d\n", i);
- }
- }
- return 0;
- }
カーマイケル数は、561, 1105, 1729, 2465, 2821, 6601, 8911, 10585, 15841, 29341, 41041, 46657, 52633, 62745, 63973, 75361...と続く。
0 件のコメント:
コメントを投稿