Common Lisp имеет bignums и пытается использовать их при необходимости (и если не указано иное), поэтому что результаты математически полезны для большинства пользователей: некомпьютерные люди обычно не ожидают арифметики по модулю над степенями двойки.
Вы можете посмотреть, как реализованы bignums (например, sbcl), чтобы лучше понять, как они работают, как распределяется память и почему они быстрые. За бигнумами стоит большая работа, чтобы сделать их быстрыми на практике (единственная проблема, с которой я когда-либо сталкивался, это их печать (особенно в Emacs)).
Тип long long unsigned
должен иметь ширину не менее 64 бит (в C++ ширина всегда равна степени 2, но я не уверен, что стандарт требует этого), а целые числа без знака определены с семантикой переноса. Вы получаете 0, потому что факториал кратен 264
(mod (factorial 1000) (expt 2 64))
0
Фактически, формула Лежандра может быть использована для определения наивысшего показателя степени v
, такого что 2< sup>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
person
coredump
schedule
27.05.2020
(compile 'factorial)
. - person Kaz   schedule 28.05.2020