Common Lisp имеет bignums и пытается использовать их, когда необходимо (и если не указано иное), так что результаты математически полезны для большинства пользователей: люди, не занимающиеся вычислением, обычно не ожидают, что арифметика по модулю превышает степень двойки .
Вы можете посмотреть, как реализованы bignums (например, sbcl ), чтобы лучше понять, как они работают, как распределяется память и почему они бывают быстрыми. За bignums нужно потрудиться, чтобы сделать их быстрыми на практике (единственная проблема, с которой я когда-либо сталкивался с bignums, - это печать их (особенно в Emacs)).
Тип long long unsigned
должен быть не менее 64 бита в ширину (в C ++ ширина всегда равна степени 2, но я не уверен, что этого требует стандарт), а целые числа без знака определены так, чтобы иметь семантику переноса. Вы получаете 0, потому что факториал кратен 2 64
(mod (factorial 1000) (expt 2 64))
0
Фактически, формулу Лежандра можно использовать для определения наивысшего показателя v
, например что 2 v делит 1000 !:
CL-USER> (loop
with p = 2
with n = 1000
for i from 1
for v = (floor n (expt p i))
while (plusp v)
sum v)
994
Мы можем подтвердить, что (expt 2 994)
действительно делит это большое число:
CL-USER> (mod (factorial 1000) (expt 2 994))
0
Но (expt 2 995)
не :
CL-USER> (mod (factorial 1000) (expt 2 995))
167423219872854268898191413915625282900219501828989626163085998182867351738271269139562246689952477832436667643367679191435491450889424069312259024604665231311477621481628609147204290704099549091843034096141351171618467832303105743111961624157454108040174944963852221369694216119572256044331338563584