суббота, 27 марта 2010 г.

Большие числа

Сделал мини-библиотеку для работы с большими числами (типа стозначных или пятисотзначных).

Странное дело: первое возведение тройки в тысячную степень занимает 1 секунду, а каждое следующее возведение увеличивает это время примерно на секунду, пока к пятой итерации не достигает 4,5 секунд. На этой скорости происходит стабилизация: сотое повторение занимает те же 4,5 секунды.

Подозреваю, проблема в том, что списки длиной более 100 элементов работают медленнее, чем массивы. Впрочем, так как стабилизация таки происходит, проблема некритична. Учитывая, что возведение тройки в десятитысячную степень занимает уже пять минут, следует предположить, что я отловил грань, за которой длина списка становится критичной.

; Добить нулями num до длины len
(define (big-leading-zero len num)
(append (dup 0 (- len (length num))) num))

; Выровнять большие числа по длине
(define (big-align)
(let (len (apply max (map length (args))))
(map (curry big-leading-zero len) (args))))

; 123 -> '(1 2 3)
(define (int-to-big num)
(map int (explode (string num))))

; '(1 2 3) -> 123
(define (big-to-int big)
(int (join (apply string big))))

; '(1 2 3) -> "123"
(define (big-to-string big)
(join (map string big)))

; "123" -> '(1 2 3)
(define (string-to-big big)
(map int (explode big)))

; "Упаковать" число так, чтобы в каждом разряде сидело не больше 9
(define (big-pack num)
(let (num (reverse num) o-num '() dig nil tra 0 lead nil)
(while (or (> tra) (not (null? num)))
(set 'dig (+ (or (pop num) 0) tra))
(push (% dig 10) o-num)
(set 'tra (/ dig 10)))
(if (set 'lead (find 0 o-num <)) (lead o-num) '(0))))

; Сложить большие числа
(define (big-add)
(big-pack (map (fn (x) (apply + x)) (transpose (apply big-align (args))))))

; Умножить два больших числа
(define (big-mul-2 num1 num2)
(apply big-add
(map (fn (y) (append y (dup 0 $idx)))
(reverse (map (fn (x) (map (curry mul x) num2)) num1)))))

; Перемножить большие числа
(define (big-mul)
(apply big-mul-2 (args) 2))

; Возвести большое число в большую степень
(define (big-pow num pw)
(let (pw2 (list num))
(dotimes (x (- (length (bits pw)) 1))
(push (apply big-mul-2 (dup (if pw2 (first pw2) num) 2)) pw2))
(apply big-mul
(map (fn (x y) (if (= x "1") y '(1))) (explode (bits pw)) pw2))))


А вот довесочек, проверяющий скорость:

; Проверка скорости
(dotimes (x 1000)
(println x ": " (time (big-pow '(3) 1000))))

Комментариев нет:

Отправить комментарий

Архив блога