更快的查找原始根的算法

时间:2014-10-29 16:51:17

标签: c++ algorithm math primitive

我正试图用这个算法找到素根:

std::vector<unsigned long long> Keyexchange::primroot(unsigned long long val) {

    std::vector<unsigned long long> res;

    for (unsigned long long i = 2; i<val - 1; i++) {

        unsigned long long start = 1;
        bool flag = 1;

        for (unsigned long long j = 0; j<val / 2; j++) {
            start = (start * i) % val;
            if (start % val == 1) {
                flag = 0;
                break;
            }
        }
        if (flag) {
            res.push_back(i);
        }
    }
    return res;
}

效果很好,但非常慢。 我想计算像1073741789这样的大数字的原始根。如果有可能设置范围,那将是最好的,因为我现在正在计算整个数组。

因此,我正在寻找一种方法[代码snipet会很棒]从这个给定的大数字中产生大约100.000个最大的原始根。

我知道Eulerscheφ函数的速度要快得多,但我不知道如何实现它。

非常感谢。

2 个答案:

答案 0 :(得分:8)

首先,如果你选择一个从2到p-1的随机整数,那么它有可能成为一个原始根。所以你选择一个随机整数(或者从2开始),检查它,如果它失败了,你选择下一个等等。

检查x是原始根:这意味着x ^(p-1)= 1(模p),但p的幂不小。例如,p = 31,p-1 = 30 = 2 x 3 x 5.如果p不是原始根,则x ^(30/2),x ^(30/3)和x ^(30)之一/ 5)必须为1(模p)。

因子p-1的素因子,计算每个素因子f的x ^((p-1)/ f)(模p),如果结果都不是1则x是原始根。

当然x ^ y(模p)需要通过重复平方/乘法来计算。例如,要计算x ^ 10,您将按此顺序计算x ^ 2,x ^ 4,x ^ 5,x ^ 10。

一旦找到了原始根g,如果gcd(k,p-1)= 1,则g ^ k是原始根。但是,在这种情况下,您需要关注多个原始根。

答案 1 :(得分:1)

如果输入的数字是semi-prime并且你手头有(两个)素因子,那么你可以使用它:

vector<uint64> Roots(uint64 p,uint64 q)
{
    vector<uint64> roots;

    uint64 zstar = p*q;
    for (uint64 y=1; y<zstar; y++)
    {
        if (GCD(zstar,y) == 1 && InQR(y,p,q))
        {
            uint64 yp = PowMod(y,(p+1)/4,p);
            uint64 yq = PowMod(y,(q+1)/4,q);
            uint64 r1 = Map(0+yp,0+yq,p,q);
            uint64 r2 = Map(0+yp,q-yq,p,q);
            uint64 r3 = Map(p-yp,0+yq,p,q);
            uint64 r4 = Map(p-yp,q-yq,p,q);
            roots.push_back(r1);
            roots.push_back(r2);
            roots.push_back(r3);
            roots.push_back(r4);
        }
    }

    return roots;
}

以下是辅助功能:

uint64 GCD(uint64 a,uint64 b)
{
    uint64 c = a%b;
    if (c == 0)
        return b;
    return GCD(b,c);
}

uint64 PowMod(uint64 x,uint64 e,uint64 n)
{
    uint64 y = 1;
    while (e > 0)
    {
        if (e & 1)
            y = (y*x)%n;
        x = (x*x)%n;
        e >>= 1;
    }
    return y;
}

bool InQR(uint64 y,uint64 p)
{
    return PowMod(y,(p-1)/2,p) == 1;
}

bool InQR(uint64 y,uint64 p,uint64 q)
{
    return InQR(y,p) && InQR(y,q);
}

uint64 Map(uint64 u,uint64 v,uint64 p,uint64 q)
{
    uint64 a = q*Inverse(p,q);
    uint64 b = p*Inverse(q,p);
    return (u*a+v*b)%(p*q);
}

uint64 Inverse(uint64 n,uint64 a)
{
    int64  x1 = 1;
    int64  x2 = 0;
    int64  y1 = 0;
    int64  y2 = 1;
    uint64 r1 = n;
    uint64 r2 = a;

    while (r2 != 0)
    {
        uint64 r3 = r1%r2;
        uint64 q3 = r1/r2;
        int64  x3 = x1-q3*x2;
        int64  y3 = y1-q3*y2;

        x1 = x2;
        x2 = x3;
        y1 = y2;
        y2 = y3;
        r1 = r2;
        r2 = r3;
    }

    return (uint64)(y1>0? y1:y1+n);
}