2016-12-30 15 views
-2

换言之是有m^e mod (n)这样的算法:如何实现模块幂运算,最多需要在C中模块化取幂指数的字节数的两倍?

// powmod(m,e,n) = m^e % n 
unsigned long long powmod(unsigned long m, unsigned long e, unsigned long long n) 

不会溢出用于让我们说,其中m = 2^32 - 1,E = 3,N = 2^64 - 1没有gmp或使用这样的库?

+1

的几十个关键词中'powmod' SO柱。当然,其中一个会帮助你。 OTOH,张贴你已经尝试过的东西很好。 – chux

+1

如果'n = 2^64 - 1',我认为不需要代码需要“n”字节的两倍大小。 “要模块化取幂的数字”如果'0 chux

+2

@Nae无论你在哪里发布它,答案都是一样的:对于x位的'n'的给定位,你总是需要2 * x bitlen操作。鉴于你''unsigned long long' bitlen for'n',如果没有'unsigned long long long'精度,你将无法做到这一点。 –

回答

3

是的,你可以做到这一点。由于内置Exp需要测试,因此请执行以下代码;对C的转换应该非常简单,因为唯一的库使用仅限于测试。的上面的代码在主功能边缘检测值

package main 

import (
    "fmt" 
    "math" 
    "math/big" 
    "math/rand" 
) 

// AddMod returns a + b (mod m). 
func AddMod(a, b, m uint64) uint64 { 
    a %= m 
    b %= m 
    if a >= m-b { 
     return a - (m - b) 
    } 
    return a + b 
} 

// SubMod returns a - b (mod m). 
func SubMod(a, b, m uint64) uint64 { 
    a %= m 
    b %= m 
    if a < b { 
     return a + (m - b) 
    } 
    return a - b 
} 

// Lshift32Mod returns 2^32 a (mod m). 
func Lshift32Mod(a, m uint64) uint64 { 
    a %= m 
    // Let A = 2^32 a. The desired result is A - q* m, where q* = [A/m]. 
    // Approximate q* from below by q = [A/(m+err)] for the err in (0, 2^32] such 
    // that 2^32|m+err. The discrepancy is 
    // 
    // q* - q < A (1/m - 1/(m+err)) + 1 = A err/(m (m+err)) + 1 
    // 
    // A - q m = A - q* m + (q* - q) m < m + A err/(m+err) + m < 2 m + 2^64. 
    // 
    // We conclude that a handful of loop iterations suffice. 
    m0 := m & math.MaxUint32 
    m1 := m >> 32 
    q := a/(m1 + 1) 
    q0 := q & math.MaxUint32 
    q1 := q >> 32 
    p := q0 * m0 
    p0 := p & math.MaxUint32 
    p1 := p >> 32 
    a -= p1 + q0*m1 + q1*m0 + ((q1 * m1) << 32) 
    for a > math.MaxUint32 { 
     p0 += m0 
     a -= m1 
    } 
    return SubMod(a<<32, p0, m) 
} 

// MulMod returns a b (mod m). 
func MulMod(a, b, m uint64) uint64 { 
    a0 := a & math.MaxUint32 
    a1 := a >> 32 
    b0 := b & math.MaxUint32 
    b1 := b >> 32 
    p0 := a0 * b0 
    p1 := AddMod(a0*b1, a1*b0, m) 
    p2 := a1 * b1 
    return AddMod(p0, Lshift32Mod(AddMod(p1, Lshift32Mod(p2, m), m), m), m) 
} 

// PowMod returns a^b (mod m), where 0^0 = 1. 
func PowMod(a, b, m uint64) uint64 { 
    r := 1 % m 
    for b != 0 { 
     if (b & 1) != 0 { 
      r = MulMod(r, a, m) 
     } 
     a = MulMod(a, a, m) 
     b >>= 1 
    } 
    return r 
} 

func randUint64() uint64 { 
    return uint64(rand.Uint32()) | (uint64(rand.Uint32()) << 32) 
} 

func main() { 
    var biga, bigb, bigm, actual, bigmul, expected big.Int 
    for i := 1; true; i++ { 
     a := randUint64() 
     b := randUint64() 
     m := randUint64() 
     biga.SetUint64(a) 
     bigb.SetUint64(b) 
     bigm.SetUint64(m) 
     actual.SetUint64(MulMod(a, b, m)) 
     bigmul.Mul(&biga, &bigb) 
     expected.Mod(&bigmul, &bigm) 
     if actual.Cmp(&expected) != 0 { 
      panic(fmt.Sprintf("MulMod(%d, %d, %d): expected %s; got %s", a, b, m, expected.String(), actual.String())) 
     } 
     if i%10 == 0 { 
      actual.SetUint64(PowMod(a, b, m)) 
      expected.Exp(&biga, &bigb, &bigm) 
      if actual.Cmp(&expected) != 0 { 
       panic(fmt.Sprintf("PowMod(%d, %d, %d): expected %s; got %s", a, b, m, expected.String(), actual.String())) 
      } 
     } 
     if i%100000 == 0 { 
      println(i) 
     } 
    } 
} 

C译法:

#include <stdio.h> 
// AddMod returns a + b (mod m). 
unsigned long long AddMod(unsigned long long a, unsigned long long b, unsigned long long m){ 
    a %= m; 
    b %= m; 
    if (a >= m-b) { 
     return a - (m - b); 
    } 
    return a + b; 
} 

// SubMod returns a - b (mod m). 
unsigned long long SubMod(unsigned long long a, unsigned long long b, unsigned long long m){ 
    a %= m; 
    b %= m; 
    if (a < b) { 
     return a + (m - b); 
    } 
    return a - b; 
} 

// Lshift32Mod returns 2^32 a (mod m). 
unsigned long long Lshift32Mod(unsigned long long a, unsigned long long m){ 
    a %= m; 
    // Let A = 2^32 a. The desired result is A - q* m, where q* = [A/m]. 
    // Approximate q* from below by q = [A/(m+err)] for the err in (0, 2^32] such 
    // that 2^32|m+err. The discrepancy is 
    // 
    // q* - q < A (1/m - 1/(m+err)) + 1 = A err/(m (m+err)) + 1 
    // 
    // A - q m = A - q* m + (q* - q) m < m + A err/(m+err) + m < 2 m + 2^64. 
    // 
    // We conclude that a handful of loop iterations suffice. 
    unsigned long long m0 = m & 0xFFFFFFFF; 
    unsigned long long m1 = m >> 32; 
    unsigned long long q = a/(m1 + 1); 
    unsigned long long q0 = q & 0xFFFFFFFF; 
    unsigned long long q1 = q >> 32; 
    unsigned long long p = q0 * m0; 
    unsigned long long p0 = p & 0xFFFFFFFF; 
    unsigned long long p1 = p >> 32; 
    a -= p1 + q0*m1 + q1*m0 + ((q1 * m1) << 32); 
    while (a > 0xFFFFFFFF) { 
     p0 += m0; 
     a -= m1; 
    } 
    return SubMod(a<<32, p0, m); 
} 

// MulMod returns a b (mod m). 
unsigned long long MulMod(unsigned long long a, unsigned long long b, unsigned long long m){ 

    unsigned long long a0 = a & 0xFFFFFFFF; 
    unsigned long long a1 = a >> 32; 
    unsigned long long b0 = b & 0xFFFFFFFF; 
    unsigned long long b1 = b >> 32; 
    unsigned long long p0 = a0 * b0; 
    unsigned long long p1 = AddMod(a0*b1, a1*b0, m); 
    unsigned long long p2 = a1 * b1; 

    return AddMod(p0, Lshift32Mod(AddMod(p1, Lshift32Mod(p2, m), m), m), m); 
} 

// PowMod returns a^b (mod m), where 0^0 = 1. 
unsigned long long PowMod(unsigned long long a, unsigned long long b, unsigned long long m){ 
    unsigned long long r = 1 % m; 
    while (b != 0) { 
     if ((b & 1) != 0) { 
      r = MulMod(r, a, m); 
     } 
     a = MulMod(a, a, m); 
     b >>= 1; 
    } 
    return r; 
} 


int main(void){ 
    unsigned long long a = 4294967189; 
    unsigned long long b = 4294967231; 
    unsigned long long m = 18446743979220271189; 
    unsigned long long c = 0; 

    c = PowMod(a, b, m); 
    printf("%llu %llu %llu %llu", a, b, m, c); 
} 
+0

@Nae这是去。运算符与C中的相同,除了':=',它声明并分配一个新变量。 –

+1

好的,我发现'func MulMod(a,b,mint32)uint32'通过将操作分解为四个n/2数学乘积来执行比n位乘法更广泛的操作。希望OP可以转换为C而不会产生错误。 – chux

+0

@Nae将操作数拆分为半角块并使用长乘法/除法。 –