更好的方法来实现模运算(算法问题)

时间:2010-05-05 13:33:54

标签: algorithm modulo vhdl

我最近一直试图实现模块化指数器。我正在用VHDL编写代码,但我正在寻找更具算法性质的建议。模幂运算器的主要组件是模块化乘法器,我也必须自己实现。我对乘法算法没有任何问题 - 它只是添加和移位而且我已经很好地弄清楚了所有变量的含义,这样我就可以在相当合理的时间内繁殖。

我遇到的问题是在乘法器中实现模数运算。我知道重复减法会有效,但也会很慢。我发现我可以改变模数以有效地减去模数的大倍数,但我认为可能还有更好的方法来做到这一点。我正在使用的算法是这样的(奇怪的伪代码如下):

result,modulus : integer (n bits) (previously defined)
shiftcount : integer (initialized to zero)
while( (modulus<result) and  (modulus(n-1) != 1) ){
     modulus = modulus << 1
     shiftcount++
}
for(i=shiftcount;i>=0;i--){
     if(modulus<result){result = result-modulus}
     if(i!=0){modulus = modulus >> 1}
}

所以...这是一个很好的算法,或者至少是一个好的开始?维基百科并没有真正讨论用于实现模运算的算法,每当我尝试在其他地方搜索时,我发现它真的很有趣,但却非常复杂(通常是无关的)研究论文和出版物。如果有一种明显的方法可以实现这一点,我没有看到,我真的很感激一些反馈。

5 个答案:

答案 0 :(得分:11)

我不确定你在那里计算什么是诚实的。您谈论模运算,但通常模数运算在两个数字ab之间,其结果是将a除以b的余数。您的伪代码中的ab在哪里??

无论如何,也许这会有所帮助:a mod b = a - floor(a / b) * b

我不知道这是否更快,这取决于你是否可以比大量减法更快地进行除法和乘法。

加速减法方法的另一种方法是使用二分搜索。如果您需要a mod b,则需要从b中减去a,直到a小于b。所以基本上你需要找到k这样:

a - k*b < b, k is min

查找此k的一种方法是线性搜索:

k = 0;
while ( a - k*b >= b )
    ++k;

return a - k*b;

但是你也可以二进制搜索它(只运行了一些测试,但它适用于所有测试):

k = 0;
left = 0, right = a
while ( left < right )
{
    m = (left + right) / 2;
    if ( a - m*b >= b )
       left = m + 1;
    else
       right = m;
}

return a - left*b;

我猜测二进制搜索解决方案在处理大数字时会是最快的。

如果您想计算a mod b并且只有a是一个很大的数字(您可以将b存储在原始数据类型上),您可以更快地完成:

for each digit p of a do
    mod = (mod * 10 + p) % b
return mod

这是有效的,因为我们可以将a写为a_n*10^n + a_(n-1)*10^(n-1) + ... + a_1*10^0 = (((a_n * 10 + a_(n-1)) * 10 + a_(n-2)) * 10 + ...

我认为二进制搜索是你正在寻找的。

答案 1 :(得分:5)

如果你使用shift-and-add进行乘法运算(这绝不是最快的方法),你可以在每个加法步骤后进行模运算。如果总和大于模数,则减去模数。如果可以预测溢出,则可以同时进行加法和减法。在每一步执行模数也会减小乘数的整体大小(与输入相同而不是双倍)。

你正在做的模数的转移正在使你走向完全除法算法(模数只是取余数)。

编辑这是我在python中的实现:

def mod_mul(a,b,m):
    result = 0
    a = a % m
    b = b % m
    while (b>0):
        if (b&1)!=0:
            result += a
            if result >= m: result -= m
        a = a << 1
        if a>=m: a-= m
        b = b>>1
    return result

这只是模乘(结果= a * b mod m)。不需要顶部的模运算,但提醒一下,算法假定a和b小于m。

当然,对于模幂运算,你将有一个外部循环,在每个步骤执行整个运算,进行平方或乘法。但我想你知道的。

答案 2 :(得分:2)

有许多方法可以在O(log n )时间内完成 n 位;您可以进行乘法运算,而不必一次迭代1位。例如,

a mod b = a - floor((a * r)/2^n) * b

其中

r = 2^n / b

是预先计算的,因为通常您多次使用相同的b。如果不是,请使用标准的超收敛多项式迭代方法进行倒数(在固定点迭代2x - bx^2)。

根据您需要的结果范围选择n(对于诸如模幂的许多算法,其不必为0..b)。

(很多年前,我以为我看到了避免连续两次乘法的窍门...更新:我认为它是Montgomery Multiplication(请参阅REDC算法)。我回想起来,REDC的工作与不知道为什么要发明REDC ...由于在链式乘法中使用了低阶结果而不是高阶结果,因此延迟可能会略低吗?)

当然,如果您有很多内存,则可以只为2^n mod b预先计算所有n = log2(b)..log2(a)的部分和。许多软件实现都是这样做的。

答案 3 :(得分:0)

对于模数本身,我不确定。对于模数作为较大模块指数运算的一部分,您是否在Montgomery multiplication的维基百科页面中查找了modular exponentiation?我已经研究过这种类型的算法已经有一段时间了,但我记得,它通常用于快速模幂运算。

编辑:为了它的价值,你的模数算法乍一看似乎没问题。你基本上做分裂,这是一个重复的减法算法。

答案 4 :(得分:0)

该测试(modulus(n-1) != 1) //有点测试?

- 看似多余的(modulus<result)

设计硬件实现我会意识到比按位运算和零分支更多逻辑(减法)的测试更小/更大。

如果我们可以轻松进行按位测试,这可能很快:

m=msb_of(modulus)

while( result>0 ) 
{
  r=msb_of(result) //countdown from prev msb onto result
  shift=r-m        //countdown from r onto modulus or 
                   //unroll the small subtraction 

  takeoff=(modulus<<(shift))  //or integrate this into count of shift

  result=result-takeoff;  //necessary subtraction

  if(shift!=0 && result<0)
  { result=result+(takeoff>>1); }

  } //endwhile

if(result==0) { return result }
else          { return result+takeoff }

(未经测试的代码可能包含陷阱)

resultmodulus重复递减以匹配最高有效位。

每次减法后:result有大约50/50的机会失去超过1 msb。它还有~50 / 50的消极机会, 减去一半的东西将再次使其成为正面。 &GT;如果shift不是= 0,它应该被放回正值

result欠载并且'shift'为0时,工作循环退出。