вторник, 18 мая 2010 г.

История строки User-Agent в браузерах

Любопытная статья История строки User-Agent в браузерах повествует о том, почему некоторые браузеры притворяются друг другом и почему все они притворяются браузером Mozilla.

понедельник, 17 мая 2010 г.

Common Lisp. Цикл против рекурсии

Пробуем писать простейшие циклы с помощью do. Циклы в большинстве случаев могут выполнять полезную работу только вместе с переменными, поэтому переменные, изученные в прошлых примерах, для освоения цикла будут как нельзя кстати.

1. Вычисления Факториала в цикле
;; Рекурсивное вычисление факториала
(defun fact (n)
       (if (= 1 n)
           1
           (* n (fact (- n 1)))))

;; Рекурсивное вычисление факториала
;; с использованием хвостовой рекурсии
(defun fact2 (counter &optional (val 1))
       (if (= counter 0)
           val
           (fact2 (- counter 1) (* val counter))))

;; Вычисление факториала с помощью цикла do
(defun fact3 (n)
       (let ((f 1))
            (do ((i 2 (+ i 1)))
                ((> i n) f)
                (setf f (* f i)))))

;;; Сравниваем результат вычислений и
;;; ресурсоёмкость трёх функций
(= (time (fact 20000))
   (time (fact2 20000))
   (time (fact3 20000)))
Результат выполнения:
Evaluation took:
  0.426 seconds of real time
  0.420026 seconds of total run time (0.400025 user, 0.020001 system)
  [ Run times consist of 0.064 seconds GC time, and 0.357 seconds non-GC time. ]
  98.59% CPU
  855,372,860 processor cycles
  303,248,336 bytes consed
Evaluation took:
  0.437 seconds of real time
  0.436027 seconds of total run time (0.436027 user, 0.000000 system)
  [ Run times consist of 0.044 seconds GC time, and 0.393 seconds non-GC time. ]
  99.77% CPU
  877,747,980 processor cycles
  339,314,808 bytes consed
Evaluation took:
  0.402 seconds of real time
  0.400025 seconds of total run time (0.380023 user, 0.020002 system)
  [ Run times consist of 0.076 seconds GC time, and 0.325 seconds non-GC time. ]
  99.50% CPU
  807,685,920 processor cycles
  303,254,160 bytes consed
T
Вариант вычисления при помощи цикла самый быстрый, на втором месте идёт рекурсивная функция, на третьем - рекурсивная с хвостовой рекурсией.

2. Вычисление чисел Фибоначчи
;; Вычисление чисел Фибоначчи рекурсией
(defun fib (num &key (a 1) (b 1))
       (if (= num 1)
           a
           (fib (- num 1) :a b :b (+ a b))))

;; Вычисление чисел Фибоначчи с использованием цикла do
(defun fib2 (n &key (a 1) (b 1))
       (cond ((= n 1) a)
             ((= n 2) b)
             (T (do ((i 3 (+ i 1)))
                    ((= i n) (+ a b))
                    (shiftf a b b (+ a b))))))

;;; Сравниваем результат вычислений и
;;; ресурсоёмкость функций
(= (time (fib 20000))
   (time (fib2 20000)))
Результат выполнения программы:
Evaluation took:
  0.030 seconds of real time
  0.032002 seconds of total run time (0.032002 user, 0.000000 system)
  [ Run times consist of 0.012 seconds GC time, and 0.021 seconds non-GC time. ]
  106.67% CPU
  60,564,250 processor cycles
  17,588,392 bytes consed
Evaluation took:
  0.025 seconds of real time
  0.024001 seconds of total run time (0.024001 user, 0.000000 system)
  [ Run times consist of 0.004 seconds GC time, and 0.021 seconds non-GC time. ]
  96.00% CPU
  48,908,280 processor cycles
  17,597,480 bytes consed
T
Вычисление с помощью циклов оказалось быстрее. Хотя, если производительность - не главное, то я выбрал бы рекурсивный вариант - он легче читается и красивее выглядит.

пятница, 14 мая 2010 г.

Продолжаем мучить Common Lisp

Продолжаю мучить Common Lisp, хотя ему всё равно - он железный, а вот себя я мучаю гораздо сильнее. Уж слишком головоломный язык, даже небольшой изученной мной частичке Common Lisp трудно уместиться в моей голове, это что-то находящееся за гранью моего предыдущего опыта.

1. Пересчёт секунд, минут и часов

Закрепляем уже пройденный материал, пытаясь применить уже известное в чуть-чуть более практической, приземлённой задаче.
;; Переводит часы, минуты и секунды в секунды
(defun hms-to-seconds (h m s)
       (+ (* h 3600) (* m 60) s))

;; Переводит секунды в часы, минуты и секунды
(defun seconds-to-hms (sec)
       (list (floor (/ sec 3600))
             (floor (/ (mod sec 3600) 60))
             (mod sec 60)))

;; Проверка правильности показаний часов:
;; часы не показывают отрицательные числа
;; а минуты и секунды не могут быть больше 59
(defun hms-ok (h m s)
       (and (>= h 0)
            (>= m 0)
            (>= s 0)
            (< m 60)
            (< s 60)))

;; Нормализовать часы, минуты и секунды:
;; перевести их в вид, пригодный для
;; отображения на часах
(defun normalize-hms (h m s)
       (seconds-to-hms (hms-to-seconds h m s)))

;; Перевести секунды в минуты,
;; возвращается количество полных минут
(defun seconds-to-minutes (sec)
       (floor (/ sec 60))) 

;; Перевести секунды в часы,
;; возвращается количество полных часов
(defun seconds-to-hours (sec)
       (floor (/ sec 3600)))

;; Получить показания секундной стрелки часов
(defun s-from-seconds (sec)
       (mod sec 60))

;; Получить показания минутной стрелки часов
(defun m-from-seconds (sec)
       (mod (floor (/ sec 60)) 60))

;; другой вариант одноимённой функции
;(defun m-from-seconds (sec)
;       (floor (/ (mod sec 3600) 60)))

;; Получить показания часовой стрелки часов
(defun h-from-seconds (sec)
       (floor (/ sec 3600)))

;; другой вариант одноимённой функции
;(defun seconds-to-hms (sec)
;       (list (h-from-seconds sec)
;             (m-from-seconds sec)
;             (s-from-seconds sec)))
2. Пример использования замыканий

Трудно объяснить всю нижеследующую магию простыми словами. Здесь используются анонимные функции (они же lambda), передача ссылки на функцию - это ещё можно понять. Но вот переменные в Common Lisp довольно необычны.

Я толком не усвоил терминологию, но своими словами могу сказать следующее: существуют глобальные переменные и локальные переменные. Глобальные переменные доступны из любого места программы, локальные - только в пределах области видимости.

Интересная особенность заключается в том, что можно в любом месте программы создать дополнительную область видимости, внутри которой переопределить значение переменной. Это делается с помощью оператора let. Все обращения к этой переменной внутри блока будут происходить к локальному же значению этой переменной. Вне оператора let переменная восстанавливает своё прежнее значение. Но при этом, в нижеследующем примере, анонимная функция сохраняет внутри себя переопределённую с помощью оператора let переменную и её значение. Это и называется замыканием. Очень необычно.
;;;; Пример использования замыканий

;; Функция, возвращающая фукцию-счётчик
;; Начальное значение счётчика - 0
(defun get-counter ()
       (let ((n 0))
            (function (lambda ()
                              (setf n (+ n 1))))))

;; Более лаконичный аналог предыдущей функции
;(defun get-counter ()
;       (let ((n 0))
;            #'(lambda ()
;                      (incf n))))

;; Переменная с первой функцией-счётчиком
(setf counter1 (get-counter))

;; Переменная со второй функцией-счётчиком
(setf counter2 (get-counter))

;; Более лаконичный вариант двух предыдущих присваиваний
;(setf counter1 (get-counter)
;      counter2 (get-counter))

;;; Попеременно вызываем счётчики
;;; Счётчики при каждом вызове увеличиваются на 1
(funcall counter1)

(funcall counter2)

(funcall counter1)

(funcall counter1)

(funcall counter1)

(funcall counter2)
В этом примере функция counter1 будет наращивать значение своего счётчика, а функция counter2 - значение своего.

3. Чуть более сложный пример использования замыканий

Здесь уже используются ассоциативные массивы, а функция get-counter-methods возвращает в ассоциативном массиве три функции, замкнутые на одной переменной.
;;;; Ещё один пример использования замыканий

;; Функция возвращает ассоциативный список
;; из трёх функций, замкнутых на одну переменную
(defun get-counter-methods ()
  (let ((n 0))
    (list
     :increase #'(lambda () (incf n))
     :decrease #'(lambda () (decf n))
     :value #'(lambda () n))))

;; Создаём счётчик, содержащий ассоциативный
;; список трёх функций
(setf counter (get-counter-methods))

;;; Далее вызываем функции и следим
;;; за состоянием счётчика
(funcall (getf counter :increase))

(funcall (getf counter :value))

(funcall (getf counter :decrease))

(funcall (getf counter :value))

Light Bot

Предлагаю поиграть в Light Bot. Игрушка для программистов. Мой результат: на всю игру я потратил 198 команд.

среда, 12 мая 2010 г.

Продолжаем упражняться в Common Lisp

На этот раз я пробую освоить переменное количество аргументов функций, именованные аргументы и значения по умолчанию.

1. Вычисление квадратного корня с необязательным указанием точности и приближённого значения корня
;;;; Приближённое вычисление квадратного корня

;; Модуль числа
(defun abs-value (num)
(if (> num 0.0)
num
(- 0.0 num)))

;; Логическая функция, возвращающая истину,
;; если числа a и b отличаются друг от друга
;; менее, чем на precision
(defun precision-ok (a b &optional (precision 0.001))
(< (abs-value (- a b)) precision))

;; Рекурсивная функция, использующая root
;; как приближённое значение корня num.
;; Функция вызывает сама себя до тех пор,
;; пока точность результата не достигнет precision
;; По умолчанию использует 1.0 в качестве приближённого
;; значения корня и 0.001 как значение точности
(defun square-root (num &optional (root 1.0))
(if (precision-ok num (* root root))
root
(square-root num (/ (+ (/ num root) root) 2))))

;; Вычисление квадратного корня 25
(square-root 25.0)

;; Вычисление квадратного корня с указанием его
;; приближённого значения
(square-root 25.0 3.0)

Этот вариант программы плох тем, что нельзя указать при вычислении корня необходимую точность результата. Поэтому перепишем программу в следующем виде:

2. Вычисление квадратного корня с необязательным указанием точности и приближённого значения корня
;;;; Приближённое вычисление квадратного корня

;; Модуль числа
(defun abs-value (num)
(if (> num 0.0)
num
(- 0.0 num)))

;; Логическая функция, возвращающая истину,
;; если числа a и b отличаются друг от друга
;; менее, чем на precision
(defun precision-ok (a b precision)
(< (abs-value (- a b)) precision))

;; Рекурсивная функция, использующая root
;; как приближённое значение корня num.
;; Функция вызывает сама себя до тех пор,
;; пока точность результата не достигнет precision
;; По умолчанию использует 1.0 в качестве приближённого
;; значения корня и 0.001 как значение точности
(defun square-root (num &optional (root 1.0) (precision 0.001))
(if (precision-ok num (* root root) precision)
root
(square-root num (/ (+ (/ num root) root) 2) precision)))

;; Вычисление квадратного корня 25
(square-root 25.0)

;; Вычисление квадратного корня с указанием его
;; приближённого значения
(square-root 25.0 3.0)

;; Вычисление квадратного корня с указанием его
;; приближённого значения и необходимой точности
(square-root 25.0 3.0 0.00001)

По-моему гораздо удобнее. Однако если мы хотим указать точность, то необходимо также указать и приближённое значение корня. Исправим это при помощи именованных аргументов.

3. Вычисление квадратного корня с необязательным указанием точности или приближённого значения корня
;;;; Приближённое вычисление квадратного корня

;; Модуль числа
(defun abs-value (num)
(if (> num 0.0)
num
(- 0.0 num)))

;; Логическая функция, возвращающая истину,
;; если числа a и b отличаются друг от друга
;; менее, чем на precision
(defun precision-ok (a b precision)
(< (abs-value (- a b)) precision))

;; Рекурсивная функция, использующая root
;; как приближённое значение корня num.
;; Функция вызывает сама себя до тех пор,
;; пока точность результата не достигнет precision
;; По умолчанию использует 1.0 в качестве приближённого
;; значения корня и 0.001 как значение точности
(defun square-root (num &key (root 1.0) (precision 0.001))
(if (precision-ok num (* root root) precision)
root
(square-root num
:root (/ (+ (/ num root) root) 2)
:precision precision)))

;; Вычисление квадратного корня 25
(square-root 25.0)

;; Вычисление квадратного корня с указанием его
;; приближённого значения
(square-root 25.0 :root 3.0)

;; Вычисление квадратного корня с указанием
;; необходимой точности
(square-root 25.0 :precision 0.000001)

;; Вычисление квадратного корня с указанием его
;; приближённого значения и необходимой точности
(square-root 25.0 :precision 0.000001 :root 25.0)

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

4. Вычисление квадратных корней списка чисел

Дополним программу из предыдущего пункта следующей функцией:
;;Вычисление квадратных корней списка чисел
(defun square-roots (&rest rest)
(if (eql rest nil)
nil
(cons (square-root (car rest))
(apply (function square-roots) (cdr rest)))))

;; Вычисление корней 25, 64 и 121
(square-roots 25.0 64.0 121.0)

Или можно записать немного короче, заменив оператор function на его сокращённую запись:

;; То же самое, но с использованием краткой записи
;; оператора function
(defun square-roots (&rest rest)
(if (eql rest nil)
nil
(cons (square-root (car rest))
(apply #'square-roots (cdr rest)))))

;; Вычисление корней 25, 64 и 121
(square-roots 25.0 64.0 121.0)

5. Вычисление чисел Фибоначчи

Именованные аргументы со значением по умолчанию можно использовать и для сокращения других программ. Например, уже рассмотренной ранее программы для вычисления чисел из ряда Фибоначчи:
;;;; Вычисление 20000-го числа в ряду Фибоначчи

;; Рекурсивная функция, вычисляющая число Фибоначчи
;; Вызывает себя num раз, используя a и b в качестве
;; двух предыдущих чисел в ряду
(defun fib (num &key (a 1) (b 1))
(if (eql num 1)
a
(fib (- num 1) :a b :b (+ a b))))

;; Вычисление 20000-го числа в ряду Фибоначчи
(fib 20000)

;; То же самое
(fib 20000 :a 1 :b 1)

6. Вычисление числа Фи ("Золотого сечения")
;; Вычисление числа Фи
;; Совмещённое вычисление двух последовательных
;; чисел в ряду Фибоначчи с последующим делением
;; следующего числа на предыдущее
(defun phi (&key (num 200) (a 1) (b 1))
(if (eql num 1)
(* 1.0 (/ b a))
(phi :num (- num 1) :a b :b (+ a b))))

(phi)

7. Вычисление факториала

И даже вычисление факториала, написанное в расчёте на оптимизацию хвостовой рекурсии, можно переписать в подобном же виде:

;;;; Вычисление факториала 20000

;; Вычисление факториала с применением хвостовой рекурсии
(defun fact (counter &optional (val 1))
(if (eql counter 0)
val
(fact (- counter 1) (* val counter))))

;; Вычисляем факториал 20000
(fact 20000)

вторник, 11 мая 2010 г.

Немножко упражнений на Common Lisp

Решил поупражняться тут в Lisp'е, пописать математических функций в функциональном же стиле.

1. Вычисление квадратного корня

Я специально определил собственный вариант функции abs, мне так интереснее :)

;;;; Приближённое вычисление квадратного корня

;; Модуль числа
(defun abs-value (num)
(if (> num 0.0)
num
(- 0.0 num)))

;; Логическая функция, возвращающая истину,
;; если числа a и b отличаются друг от друга
;; менее, чем на precision
(defun precision-ok (a b precision)
(< (abs-value (- a b)) precision))

;; Рекурсивная функция, использующая root
;; как приближённое значение корня num.
;; Функция вызывает сама себя до тех пор,
;; пока точность результата не достигнет precision
(defun square-root-iter (num root precision)
(if (precision-ok num (* root root) precision)
root
(square-root-iter num (/ (+ (/ num root) root) 2) precision)))

;; "Лицо" функции вычисления квадратного корня
;; Использует 1.0 в качестве приближённого значения корня
;; и точность 0.001
(defun square-root (num)
(square-root-iter num 1.0 0.001))

;; Вычисление квадратного корня 25
(square-root 25.0)

2. Вычисление числа из ряда Фибоначчи

Для примера я решил вычислить двдацатитысячное по порядку число в ряду Фибоначчи.

;;;; Вычисление 20000-го числа в ряду Фибоначчи

;; Рекурсивная функция, вычисляющая число Фибоначчи
;; Вызывает себя num раз, используя a и b в качестве
;; двух предыдущих чисел в ряду
(defun fib-iter (a b num)
(if (eql num 1)
a
(fib-iter b (+ a b) (- num 1))))

;; "Лицо" функции вычисления числа Фибоначчи
(defun fib (num)
(fib-iter 1 1 num))

;; Вычисление 20000-го числа в ряду Фибоначчи
(fib 20000)

3. Вычисление числа Фи ("Золотого сечения") двумя способами

Сначала я воспользовался для вычислений уже полученной в прошлом примере функцией для вычисления чисел Фибоначчи, а затем написал второй вариант функции, более оптимизированный.

;;;; Сравнение двух способов вычисления числа Фи
;;;; - "Золотого сечения"

;; Рекурсивная функция, вычисляющая число Фибоначчи
;; Вызывает себя num раз, используя a и b в качестве
;; двух предыдущих чисел в ряду
(defun fib-iter (a b num)
(if (eql num 1)
a
(fib-iter b (+ a b) (- num 1))))

;; "Лицо" функции вычисления числа Фибоначчи
(defun fib (num)
(fib-iter 1 1 num))

;; Вычисление числа Фи через два вызова функции,
;; вычисляющей число в ряду Фибоначчи
(defun phi ()
(* 1.0 (/ (fib 2000) (fib 1999))))

;; Вычисление числа Фи
;; Совмещённое вычисление двух последовательных
;; чисел в ряду Фибоначчи с последующим делением
;; следующего числа на предыдущее
(defun phi2-iter (a b num)
(if (eql num 1)
(* 1.0 (/ b a))
(phi2-iter b (+ a b) (- num 1))))

;; "Лицо" функции вычисления числа Фи
(defun phi2 ()
(phi2-iter 1 1 2000))

;; Сравниваем результаты вычисления обеих функций,
;; попутно измеряя время, потраченное на вычисления
;; Первая функция должна быть медленнее в 2 раза
;; второй
(eql (time (phi)) (time (phi2)))

А вот и результаты работы программы:

Evaluation took:
0.001 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
0.00% CPU
2,279,699 processor cycles
400,944 bytes consed
Evaluation took:
0.001 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
0.00% CPU
1,470,207 processor cycles
204,584 bytes consed
T

Первая функция не только отработала медленнее, но и затребовала для своего выполнения почти в два раза больше памяти.

Сравнение скорости вычисления факториала на Scheme и Common Lisp

Scheme

Рекурсивное вычисление факториала на Scheme:
(define (fact n)
(if (= n 0)
1
(* n (fact (- n 1)))))

(fact 20000)

Итеративное вычисление факториала на Scheme:
(define (fact n)
(fact-iter 1 n))

(define (fact-iter val counter)
(if (= counter 0)
val
(fact-iter (* val counter) (- counter 1))))

Теперь создаём две программы, в первой из которых определяется рекурсивный вариант функции вычисления факториала, а во второй - итеративный. Добавляем в каждый из файлов вычисление факториала 20000:
(= 1 (fact 20000))

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

Запускаем обе программы следующим образом и получаем время выполнения каждой из них:
time mit-scheme < fact.scheme > /dev/null ; time mit-scheme < fact2.scheme > /dev/null

real    0m3.760s
user    0m3.712s
sys     0m0.048s

real    0m3.379s
user    0m3.344s
sys     0m0.036s

То было на MIT'овской реализации, а теперь попробуем на PLT:
time plt-r5rs < fact.scheme > /dev/null ; time plt-r5rs < fact2.scheme > /dev/null

real    0m1.363s
user    0m1.256s
sys     0m0.108s

real    0m1.235s
user    0m1.152s
sys     0m0.080s

Рекурсивный код оказался немного быстрее в обоих случаях. Реализация PLT обгоняет MIT'овскую на этой задаче.

Common Lisp

Рекурсивное вычисление факториала на Common Lisp:
(defun fact (n)
(if (eql n 0)
1
(* n (fact (- n 1)))))

(fact 20000)

Итеративное вычисление факториала в Common Lisp. Процесс вычисления факториала происходит итеративно за счёт применения в компиляторе оптимизации хвостовой рекурсии:
(defun fact (n)
(fact-iter 1 n))

(defun fact-iter (val counter)
(if (eql counter 0)
val
(fact-iter (* val counter) (- counter 1))))

(fact 20000)

Программа для сравнения рекурсивного и итеративного вариантов вычисления факториала:
;;; Итеративное вычисление факториала.
;;; Ожидается именно итеративное вычисление за счёт возможности
;;; применить хвостовую рекурсию
(defun fact (n)
(fact-iter 1 n))

(defun fact-iter (val counter)
(if (eql counter 0)
val
(fact-iter (* val counter) (- counter 1))))

;;; Рекурсивное вычисление факториала.
(defun fact2 (n)
(if (eql n 0)
1
(* n (fact2 (- n 1)))))

;;;; Пробуем сравнить оба варианта вычисления по идентичности полученного
;;;; результата и времени выполнения
(eql (time (fact 20000))
(time (fact2 20000)))

(quit)

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

Результат выполнения программы:
Evaluation took:
0.408 seconds of real time
0.408026 seconds of total run time (0.392025 user, 0.016001 system)
[ Run times consist of 0.040 seconds GC time, and 0.369 seconds non-GC time. ]
100.00% CPU
819,948,580 processor cycles
339,303,792 bytes consed
Evaluation took:
0.383 seconds of real time
0.380023 seconds of total run time (0.376023 user, 0.004000 system)
[ Run times consist of 0.072 seconds GC time, and 0.309 seconds non-GC time. ]
99.22% CPU
769,179,220 processor cycles
303,251,800 bytes consed
T

Рекурсивный вариант оказался немного быстрее. Код тестировался на реализации SBCL, которая компилирует программу в машинный код платформы x86. Есть ещё реализация CMUCL, являющаяся предком SBCL, она компилирует программу в код собственной виртуальной машины. Не стоит говорить, что SBCL оказался быстрее, чем CMUCL.

Какие из всего этого можно сделать выводы? Либо в этих компиляторах оптимизация хвостовой рекурсии не выполняется, и поэтому итеративные функции показывают сравнимое и даже большее время вычисления, либо оптимизация выполняется настолько качественно, что компилятору удаётся свести рекурсивные функции к итеративным.

Как (не) работает рыночная экономика

Несколько недель назад наткнулся на такой вот сайт http://knukim-edu.kiev.ua/. Настолько элементарного изложения проблем современной рыночной экономики я ещё не видел. Это букварь макроэкономики для дошкольников. Обязательно прочитайте цитату. Вы поймёте, почему общепризнанная экономика не является наукой: микроэкономика и макроэкономика противоречат друг другу и вообще не могут создать непротиворечивую картину мира. Они не могут даже объяснить происходящего, а потому и речи быть не может о том, чтобы с их помощью вывести рецепты достижения равновесного положения экономики.

Элементарнейший пример:

Представьте себе "остров", в котором существует замкнутая экономика в виде одной деревни (Наша земля это тот же остров, только побольше), которая на заводике производит некую "еду" и продает ее в своем "сельпо". Вся наша деревня работает (возможно, сама с себя собирает налог, с которого кормит этой "едой" десяток пенсионеров, одного инвалида, детский садик и сторожа возле леса).

Работающие получают зарплату (часть ее уходит в виде налогов на содержание вышеперечисленных "нетрудоспособных" и "армии"), еда раскупается, все довольны.

Если кто-то из работающих "недоедает", - остается после работы на сверхурочные... производит больше "еды", получает больше зарплаты (и соответственно больше налогов и больше тратит в магазине). Ест и сам больше и больше остается другим (нетрудоспособным).

Идиллия. Деньги вращаются по кругу. Каждый работает ровно на столько, чтобы ему хватало.

А теперь представим, что у нашего заводика появился "хозяин".

Заводик за месяц произвел необходимое количество еды. Цена ее известна и устоялась, налоги уплачены, но... тут "хозяин" накидывает в цену 20% своей законной(!) прибыли, и выставляет "еду" на продажу в магазине.

Что происходит дальше?

Правильно. Продано будет только около ~ 80%. (Строго говоря, сам хозяин также является потребителем. Но весь избыток он все равно не съест. Лопнет.)

Потому что только на эту сумму выплачено зарплат и налогов. На остальные 20% деревня просто будет недоедать. Не потому что «еды» нет. Есть. Но купить ее, - нет денег. На их возмущение, он им порекомендует не лениться, а лучше и больше работать.

В следующем месяце хозяин произведет только 80% от количества необходимой еды. (Зачем больше? У него и те 20% остались нераспроданными.) Соответственно и его работающие будут заняты на 20% меньше времени, и естественно получат настолько же меньшую зарплату...

С каждым циклом производство будет сворачиваться.

В пределе этой сходящейся последовательности мы получим:

1. Остановившееся производство.

2. Полный склад товара ("еды"). (А мы еще удивлялись в Советском Союзе, откуда такое изобилие на витринах капитализма? Да просто денег у населения меньше, чем суммарная стоимость товара. Недальновидный Госкомцен, следивший за соответствием суммарной заработной платы – товарной массе, мог организовать изобилие витрин одним росчерком пера. Заодно и стимулы к интенсивной работе.)

3. Все деньги стекшиеся к "хозяину".

4. Голодную, безработную деревню, которой конечно можно посоветовать побольше работать, чтобы заработать денег, но особого смысла в этом нет. В нашей модели экономики просто не осталось денег. Они все выведены из нее и сосредоточились на одном из полюсов.

... пропущено ...

Можно ли что-то сделать, чтобы наша экономическая система не сколлапсировала а продолжала работать?

Да, можно.

1. Если хозяин, к примеру, купит у самого голодного «сарай с курями». Тот получит немного денег. Отдаст долги, отправит перевод матери, даст сынишке на карманные расходы… То есть в обороте нашей экономики снова появились деньги. В магазине купят еды. Значит, появился спрос. Раз есть спрос, - хозяин срочно наймет работников, раскрутит производство, появятся зарплаты и экономика оживет… - но!

Денег за сарай надолго не хватит. Через несколько циклов производства, чтобы его не останавливать, нужно будет покупать сарай уже у другого. Потом дома, землю… и так далее. Через какое-то количество времени, все снова вернется к состоянию «Великой Депрессии», но ни у кого уже не будет ни земли, ни недвижимости, ни имущества.

Можно ли в этой ситуации что-то сделать, чтобы продолжить производство еды?

Да, можно.

2. Можно еще взять денег в долг у хозяина, но взамен предложить ему свою долговую расписку.

Это снова оживит экономику, на какое-то время. Пока хозяину не надоест давать в долг. Он назовет всех дармоедами, лентяями, вечными должниками и остановит «кредит».

Вот тут уже сделать ничего нельзя. Если это остров, - то все. Можно считать, что пришел пушистый северный зверек. Даже натуральное хозяйство здесь уже невозможно, потому что у большинства не осталось ни товаров, ни собственности для обмена.


И вот тут уже неизбежна пролетарская революция. Опять повторится 1917 год. Вы понимаете? Если законными способами не забирать деньги у самого богатого и не возвращать их снова в обращение, то остаётся только революция.

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

понедельник, 10 мая 2010 г.

Роботы

Хочу поделиться несколькими интересными новостями из мира робототехники.

1. Робот PR2 складывает полотенца. Это первый в мире робот, оперирующий нежёсткими объектами, меняющими свою форму прямо в процессе работы робота.



Подробнее тут: В Беркли научили робота складывать полотенца

2. Робот, балансирующий на шаре для боулинга. Это не первый такой робот. Новинка именно этого робота заключается в том, что он умеет не только балансировать на шаре и кататься на нём по полу в любом направлении, а ещё и поворачиваться на шаре вокруг своей оси. Робот работает в одном из двух режимов:
1. удержание позиции - при толчке робота, он стремится вернуться на прежнюю точку,
2. подчинение воздействию - при небольшом усилии, приложенном к роботу, робот подчиняется ему и проезжает небольшое расстояние в сторону действия усилия. Второй режим работы может быть полезен для транспортировки предметов - робот может выполнять роль примитивной тачки :)



Подробнее здесь: Новый японский робот ездит на шаре для боулинга

3. 11 роботов модели PR2 стоимостью 4,4 миллиона долларов каждый, подарены 11 исследовательским группам в различных странах. Подарок всё же не бесплатен: исследовательские группы обязуются открыть исходный код своих наработок для подаренных роботов.



Подробнее тут: Исследователям бесплатно раздали персональных роботов

"Пикник на обочине" и "Отель "У погибшего Альпиниста""

Пикник на обочине

Идея романа очень интересна. Наивные чукотские школьники писатели-фантасты представляют себе первый контакт самым разным образом, но почти всегда объединяет их одна идея - первый контакт будет встречей равных, или по крайней мере не очень различающихся по уровню развития, цивилизаций.

А вот не тут то было. Стругацкие подумали и решили разорвать шаблон. Мы всё ждём первого контакта, ждём братьев по разуму, но не тут то было. Братья по разуму оказываются богами по разуму и просто не замечают нас. Им даже не интересно исследовать какую-то необычную форму жизни, что было бы безусловно интересно сделать людям. У них свои цели, которых мы просто не можем представить, а уж тем более - понять. Их возможности настолько велики, что то, о чём мы иногда думаем - для них обыденность, а то, о чём мы даже не можем помыслить - давно привычные вещи.

Летят они по своим делам и высаживаются на Землю. Высадились, отдохнули, и полетели дальше, не заметив тысяч погибших по их вине живых существ, и оставив за собой горы высокотехнологического мусора. Если перенести аналогию на наш уровень - это как если бы мы поехали в соседний город, остановившись где-то на полянке вдоль дороги, чтобы поесть. Раздавили колёсами машины муравейник, примяли длинные полосы травы, уронили крошки хлеба, колбасы, капнули машинным маслом. Отдохнули и поехали дальше.

И вот мы, как жители муравейника, не поняв толком произошедшего, пытаемся восстановить муравейник, попадая в лужицы машинного масла, наталкиваясь на трупы своих соплеменников. В конце концов мы понимаем, что восстанавить муравейник совершенно невозможно, т.к. каждого, отправившегося в Зону, ждёт практически неминуемая гибель. Самые смелые из нас, плюя на все запреты, пытаются попасть в Зону и унести из неё какой-нибудь артефакт. Самые любопытные из нас изучают артефакты и иногда находят им применение, не понимая принципов, по которым они работают. Нарушение законов термодинамики, физические силы неизвестной природы - учёных это вводит в ужас. А сталкеры боятся "комариных плешей" - областей повышенной гравитации, мгновенно расплющивающих в лужи металла вертолёты, и "ведьминых студней" - артефактов, проникающих сквозь стены, превращающих твёрдые предметы в желеобразные.

И те и другие испытывают жуткий страх, поняв насколько мало они на самом деле знают. И только для обывателей, которых никак не затронуло посещение, всё остаётся по-прежнему. Они посмеиваются над рассказами бывших в Зоне сталкеров и учёных, испытывают усталость от надоедливой истерии журналистов. Для них никакого посещения не было и жизнь как шла, так и идёт.

С другой стороны, сюжет у романа не прослеживается, его как будто нет. Есть просто отдельные эпизоды из жизни нескольких людей, проживающих рядом с Зоной, и живущих ею.

Отель "У погибшего Альпиниста"

Этот роман хорош своим юмором, закрученным детективным сюжетом. Интересна идея того, что не опытные инопланетяне, не достаточно изучившие людей, могут попасть в лапы преступников. Недостаточное изучение тонкостей земной цивилизации и неосторожное, поспешное внедрение на Землю грозит обернуться межпланетным скандалом и последующим разрывом всяких дипломатических отношений. В этом романе, наконец, объясняется каким образом инопланетяне могут спокойно обитать на чужой планете незамеченными. Наконец-то мы видим не непонятно каким образом взявшихся на разных планетах абсолютно одинаковых людей, а применение скафандров, имеющих полное внешнее сходство местными обитателями планеты.