Is there a way to build e.g. (853467 * 21660421200929) % 100000000000007
without BigInteger libraries (note that each number fits into a 64 bit integer but the multiplication result does not)?
This solution seems inefficient:
int64_t mulmod(int64_t a, int64_t b, int64_t m) {
if (b < a)
std::swap(a, b);
int64_t res = 0;
for (int64_t i = 0; i < a; i++) {
res += b;
res %= m;
}
return res;
}
a * b % m
equalsa * b - (a * b / m) * m
Use floating point arithmetic to approximate
a * b / m
. The approximation leaves a value small enough for normal 64 bit integer operations, form
up to 63 bits.This method is limited by the significand of a
double
, which is usually 52 bits.This method is limited by the significand of a
long double
, which is usually 64 bits or larger. The integer arithmetic is limited to 63 bits.These methods require that
a
andb
be less thanm
. To handle arbitrarya
andb
, add these lines beforec
is computed.In both methods, the final
%
operation could be made conditional.I can suggest an improvement for your algorithm.
You actually calculate
a * b
iteratively by adding each timeb
, doing modulo after each iteration. It's better to add each timeb * x
, whereasx
is determined so thatb * x
won't overflow.An improvement to the repeating doubling algorithm is to check how many bits at once can be calculated without an overflow. An early exit check can be done for both arguments -- speeding up the (unlikely?) event of N not being prime.
e.g. 100000000000007 == 0x00005af3107a4007, which allows 16 (or 17) bits to be calculated per each iteration. The actual number of iterations will be 3 with the example.
You should use Russian Peasant multiplication. It uses repeated doubling to compute all the values
(b*2^i)%m
, and adds them in if thei
th bit ofa
is set.It improves upon your algorithm because it takes
O(log(a))
time, notO(a)
time.Caveats: unsigned, and works only if
m
is 63 bits or less.You could try something that breaks the multiplication up into additions:
Keith Randall's answer is good, but as he said, a caveat is that it works only if
m
is 63 bits or less.Here is a modification which has two advantages:
m
is 64 bits.(Note that the
res -= m
andtemp_b -= m
lines rely on 64-bit unsigned integer overflow in order to give the expected results. This should be fine since unsigned integer overflow is well-defined in C and C++. For this reason it's important to use unsigned integer types.)