Факториал в Scala & Haskell

Узнайте, что Haskell показывает factorial функцию:

Prelude> factorial n = product [1..n]

Prelude> factorial 50
30414093201713378043612608166064768844377641568960512000000000000

Похоже, что либо Int больше 32 бит, либо Haskell имеет BigNumber -like.

Выполнение этой "аналогичной" функции в Scala.

scala> def factorial(n: Int) = List.range(1, n+1).foldLeft(1)(_*_)
factorial: (n: Int)Int

Я могу вычислить факториал, который остается внутри Int.MaxValue (2 миллиарда чего-то)

scala> factorial(10)
res5: Int = 362880

Переполнение происходит из-за результата, превышающего значение Int.MaxValue

scala> factorial(33)
res6: Int = -2147483648

Но почему он не переполняется здесь?

scala> factorial(50)
res7: Int = 0

Итак, как это работает в Haskell? И почему это приводит к 0 для Scala?

+2
05 апр. '14 в 16:29
источник поделиться
3 ответа

Согласно документации:

Стандартными экземплярами Integral являются Integer (неограниченные или математические целые числа, также известные как "bignums") и Int (ограниченные целые числа машин с диапазоном, эквивалентным, по меньшей мере, 29-битовому двоичному сигналу)

Wikibooks объясняет это:

"Integer" - это произвольный тип точности: он будет содержать любое число, независимо от того, насколько он большой, вплоть до предела вашей памяти устройства.... Это означает, что у вас никогда нет арифметических переполнений. С другой стороны, это также означает, что ваша арифметика относительно медленная. Пользователи Lisp могут распознавать здесь тип "bignum".

В ghci:

Prelude> factorial 50 :: Int
-3258495067890909184
Prelude> factorial 50 :: Integer
30414093201713378043612608166064768844377641568960512000000000000

Таким образом, с Int это фактически переполняется. Integer может содержать любое число без переполнения, ограниченное системной памятью.

+9
05 апр. '14 в 16:35
источник

В Haskell есть (по крайней мере) два типа для целых чисел: Integer который может содержать произвольно большие числа, но арифметика медленнее, а Int, с максимальным значением машины и более быстрой арифметикой (см. Также этот вопрос: Haskell Int и Integer)

Haskell по умолчанию целочисленный тип - Integer, поэтому в вашем примере haskell нет переполнения. Scala Int (32-разрядная подписка), однако, может переполняться, а затем обертывается в отрицательные числа.

Это приводит к нечетным результатам, как в factorial(50)=0factorial(51) = factorial(52) =... = 0: из-за переполнения временный результат умножения равен 0, таким образом, все последующие факториалы равны 0.


Пример с использованием десятичных чисел: предположим, что мы используем десятичное представление с фиксированной шириной с тремя местами и только положительные числа (в действительности у нас было бы 32 или 64 двоичных разряда, а Haskell и Scala использовали подписанные числа, но основное понятие остается одна и та же):

  • factorial 1 = 001
  • факторный 2 = 002
  • ...
  • факториал 5 = 120
  • факториал 6 = 720
  • factorial 7 = 5040, НО мы можем хранить только 3 цифры, поэтому это усекается до 040
  • факторный 8 = (факторный 7) * 8 = 040 * 8 = 320
  • фактор 9 = 320 * 9 = 2880 = 880
  • факториал 10 = 880 * 10 = 8800 = 800
  • фактор 11 = 800 * 11 = 8800 = 800
  • факториал 12 = 800 * 12 = 9600 = 600
  • ...
  • факториал 15 = 200 * 15 = 3000 = 000
  • фактор 16 = (факторный 15) * 16 = 000 * 16 = 000
  • ...

Пример моего 64-битного окна с использованием Haskell:

Prelude> factorial 65 :: Int  # largest nonzero factorial on my box
-9223372036854775808
Prelude> factorial 66 :: Int  # all following factorials are zero
0
Prelude> -9223372036854775808 * 66 :: Int  # because (factorial 65) * 66 = 0
0

Prelude> -9223372036854775808 * 66         # with arbitrary precision:
-608742554432415203328
Prelude> -608742554432415203328 / 2^64      # this happens to be a multiple of 2^64
-33.0

То же самое произойдет и с факториалами в представлении с фиксированной длиной базы 10: каждый фактор, который кратен 10, вводит другое значение "0" в "правый конец числа", поэтому в конечном итоге все цифры нашего мнимого фиксированного десятичного числа оснований 10 сдвинулся влево.

С внутренним двоичным представлением, используемым в Haskells или Scalas Int, то же самое происходит для каждого четного фактора, так что в какой-то момент все биты равны 0.

+7
05 апр. '14 в 16:35
источник

В дополнение к вышеупомянутым причинам, зависящим от Haskell Int и Integer и произвольной точности, эквивалент в Scala для Integer был бы

def factorial(n: Int) = (BigInt(1) to BigInt(n)).product

где BigInt обеспечивает произвольную точность и полагается на java.math.BigInteger.

+6
05 апр. '14 в 17:23
источник

Посмотрите другие вопросы по меткам или Задайте вопрос