Python
Python
Именно поэтому я надеюсь, что кто-то из читателей с помощью этой книги откроет
для себя в математике что-то новое. Увы, в представлении большинства, математика -
это достаточно скучная наука, вероятно так ее преподают в школе. Если кто-то с
помощью этой книги найдет для себя что-то новое, можно считать что время было
потрачено не зря.
Эта книга не задачник, а скорее сборник рассказов о тех или иных математических
вопросах. Т.к. математические примеры без цифр бессмысленны, “практическая” часть
дается на языках программирования Python и Си.
Приятного чтения.
Елисеев Дмитрий
int main()
{
printf("Hello world\n");
return 0;
}
Cи
: необходимо указать тип и значение переменной.
int x = 3;
int y = 10;
printf("x=%d\n", x);
printf("%d\n", x+y);
Циклы
В отличие от того же С++ или Java, циклы задаются отступами, что после других
языков программирования может быть непривычным. Часть кода, находящаяся внутри
цикла, будет выполнена заданное количество раз.
Python
Вывод чисел от 1 до 9:
for p in range(1,10):
print (p)
Си
Вывод чисел от 1 до 9:
for(int i=1; i<10; i++) {
printf("%d\n", i);
}
Массивы
Массив это линейный набор чисел, с которыми удобно выполнять однотипные
операции, например вычисление суммы или среднего арифметического.
Python
Объявляем массив чисел:
values = [1,2,3,5,10,15,20]
Си:
Динамические массивы поддерживаются только в C++, статические массивы
создаются так:
int values[7] = { 1,2,3,5,10,15,20 };
for(int i=0; i<7; i++) {
printf("%d\n", values[i]);
}
При желании можно слегка схитрить, если максимальный размер массива заранее
известен.
int values[255] = { 1,2,3,5,10,15,20 }, cnt = 7;
for(int i=0; i<cnt; i++) {
printf("%d\n", values[i]);
}
values[cnt] = 7;
cnt++;
Арифметические операции
Сложение, умножение,деление:
x1 = 3
x2 = (2*x1*x1 + 10*x1 + 7)/x1
Возведение в степень:
x3 = x1**10
print (x1,x2,x3)
Остаток от деления:
x2 = x1 % 6
print (x2)
print (math.sqrt(x3))
Можно вывести даже факториал числа 1024, что не сделает ни один калькулятор:
print(math.factorial(1024))
2. Математические фокусы
Для “разминки” рассмотрим несколько фокусов, имеющих отношение к числам.
Никаких особых сложностей в них нет, но их знание поможет развеселить или удивить
знакомых знанием математики.
Сделать это в уме просто, если взять сумму чисел и поместить в середину:
26*11 = 2 [ 2+6 ] 6
Аналогично 43*11 = 473, 71*11 = 781 и так далее.
Чуть длиннее расчет, если сумма чисел больше либо равна 10. Но и тогда все просто:
в середину кладется младший разряд, а 1 уходит в старший разряд:
47*11 = [4] [4+7=11] [7] = [4+1] [1] [7] = 517
94*11 = [9] [9+4=13] [4] = [10] [3] [4] = 1034
Аналогично:
2
25 = [2*3] 25 = 625 852= [8*9] 25 = 7225 и так далее.
Отгадывание результата
Попросим человека загадать любое число. Например 73. Затем чтобы еще больше
запутать отгадывающего, попросим сделать следующие действия:
- удвоим число (146)
- прибавляем 12 (158)
- разделим на 2 (79)
- вычтем из результата исходное число (79-73 = 6)
В конце мы отгадываем, что результат - 6. Суть в том, что число 6 появляется
независимо от того, какое число загадал человек.
3. Число Пи
Вобьем в стену гвоздь, привяжем к нему веревку с карандашом, начертим окружность.
Как вычислить длину окружности? Сегодня ответ знает каждый школьник - с помощью
числа Пи. Число Пи - несомненно, одна из основных констант мироздания, значение
которой было известно еще в древности. Оно используется везде, от кройки и шитья
до расчетов гармонических колебаний в физике и радиотехнике.
Сегодня достаточно нажать одну кнопку на калькуляторе, чтобы увидеть его значение:
Pi = 3.1415926535… Однако, за этими цифрами скрывается многовековая история. Что
такое число Пи? Это отношение длины окружности к ее диаметру. То что это
константа, не зависящая от самой длины окружности, знали еще в древности. Но чему
она равна? Есть ли у этого числа какая-то внутренняя структура, неизвестная
закономерность? Узнать это хотели многие. Самый простой и очевидный способ -
взять и измерить. Примерно так вероятно и поступали в древности, точность
разумеется была невысокой. Еще в древнем Вавилоне значение числа Пи было
известно как 25/8. Затем Архимед предложил первый математический метод
вычисления числа Пи, с помощью расчета вписанных в круг многоугольников. Это
позволяло вычислять значение не «напрямую», с циркулем и линейкой, а
математически, что обеспечивало гораздо большую точность. И наконец в 3-м веке
нашей эры китайский математик Лю Хуэй придумал первый итерационный алгоритм
— алгоритм, в котором число вычисляется не одной формулой, а
последовательностью шагов (итераций), где каждая последующая итерация
увеличивает точность. С помощью своего метода Лю Хуэй получил Пи с точностью 5
знаков: π = 3.1416. Дальнейшее увеличение точности заняло сотни лет. Математик из
Ирана Джамшид ибн Мас‘уд ибн Махмуд Гияс ад-Дин ал-Каши в 15-м веке
вычислил число Пи с точностью до 16 знаков, а в 17-м веке голландский математик
Лудольф вычислил 32 знака числа Пи. В 19-м веке англичанин Вильям Шенкс ,
потратив 20 лет, вычислил Пи до 707 знака, однако он так и не узнал, что в 520-м
знаке допустил ошибку и все последние годы вычислений оказались напрасны (в
итерационных алгоритмах хоть одна ошибка делает все дальнейшие шаги
бесполезными).
И наконец, перейдем к формулам вычисления Пи, т.к. именно в них можно увидеть
красоту числовых взаимосвязей - то, чем интересна математика.
Первым шагом необходимо вычислить √12. Возникает резонный вопрос - как это
сделать? Оказывается, уже в Вавилоне был известен метод вычисления квадратного
корня, который сейчас так и называется “вавилонским”. Суть его в вычислении √S по
простой формуле:
Как можно видеть, сделав всего 4 шага, можно получить √12 с достаточной точностью,
задача вполне посильная даже для ручных расчетов 15 века.
Уже на 24м шаге мы получаем искомые 11 знаков числа Пи. Задача явно требовала
больше времени чем сейчас, но вполне могла быть решена в средние века.
Современные формулы не столь просты внешне, зато работают еще быстрее. Для
примера можно привести формулу Чудновского:
Если сделать 100 итераций и вычислить 1000 знаков Пи, то можно увидеть так
называемую “точку Фейнмана”:
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089
986280348253421170679821480865132823066470938446095505822317253594081284811174502
841027019385211055596446229489549303819644288109756659334461284756482337867831652
712019091456485669234603486104543266482133936072602491412737245870066063155881748
815209209628292540917153643678925903600113305305488204665213841469519415116094330
572703657595919530921861173819326117931051185480744623799627495673518857527248912
279381830119491298336733624406566430860213949463952247371907021798609437027705392
171762931767523846748184676694051320005681271452635608277857713427577896091736371
787214684409012249534301465495853710507922796892589235420199561121290219608640344
181598136297747713099605187072113499999983729780499510597317328160963185950244594
553469083026425223082533446850352619311881710100031378387528865875332083814206171
776691473035982534904287554687311595628638823537875937519577818577805321712268066
13001927876611195909216420207
def chudnovsky(n):
pi = Decimal(0)
k=0
while k < n:
pi += (Decimal(-1)**k)*(Decimal(factorial(6*k))/((factorial(k)**3)*(factorial(3*k)))*
(13591409 + 545140134*k)/(640320**(3*k)))
k += 1
print("Шаг: {} из {}".format(k, n))
pi = pi * Decimal(10005).sqrt()/4270934400
pi = pi**(-1)
return pi
val = chudnovsky(N/14)
print(val)
На этом мы закончим с числом Пи, хотя с ним конечно, связано куда больше
интересных фактов. Например 3 марта (т.е. 03.14) отмечается международный “день
числа Пи”, ну а другие факты читатели могут поискать самостоятельно.
4. Вычисление радиуса Земли
О том, что Земля круглая сегодня знает каждый школьник, и никого не удивить таким
видом планеты из космоса.
Что это значит? Объяснение данному факту могло быть только одно - Земля круглая,
и угол падения солнечных лучей в разных точках Земли в одно время различается.
Картинка с сайта physicsforme.com
Дальше, как говорится, дело техники. Зная примерное расстояние между Сиеном и
Александрией (которое было известно из времени в пути каравана верблюдов) и угол,
легко получить длину всей окружности. К чести Эратосфена, его результат отличается
от сегодняшнего всего лишь на 1%. Так, задолго до эпохи авиации и воздухоплавания,
человек впервые смог измерить радиус планеты, даже при этом не отрываясь от нее.
Увидеть настоящую кривизну Земли сумели лишь пилоты стратостатов в начале 20
века, более чем через 2000 лет после описанного опыта.
5. Простые числа
Каждый знает, что простые числа — такие числа, которые делятся только на единицу
и самих себя. Но так ли они просты, как кажутся, и актуальны ли сегодня? Попробуем
разобраться.
То, что существуют числа, которые не делятся ни на какое другое число, люди знали
еще в древности. Последовательность простых чисел имеет следующий вид:
1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61 …
Видимо, уже во время Эратосфена стало ясно, что какого-либо четкого критерия,
является ли число простым, не существует — это можно проверить лишь
экспериментально. Существуют различные способы для упрощения процесса
(например, очевидно, что число не должно быть четным), но простой алгоритм
проверки не найден до сих пор, и скорее всего найден не будет: чтобы узнать, простое
число или нет, надо попытаться разделить его на все меньшие числа.
def is_prime(n):
if n % 2 == 0 and n > 2:
return False
for i in range(3, int(math.sqrt(n)) + 1, 2):
if n % i == 0:
return False
return True
Так что с простыми числами не все так просто. Есть и удивительные факты. Например,
в 1883 г. русский математик И.М.Первушин из Пермского уезда доказал простоту
61
числа 2 — 1 = 2305843009213693951. Даже сейчас компьютеру с запущенной
вышеприведенной программой требуется несколько минут на проверку данного числа,
а на то время это была поистине гигантская работа.
являются простыми. Эти числа называются “числами Ферма”. Однако, это оказалось
верным только для 5 первых чисел: F 0=3, F
1=5, F
2=17,F
3=257, F
4=65537. В 1732 году
Эйлер опроверг гипотезу, найдя разложение на множители для F 5: F
5= 641·6700417.
Существуют ли другие простые числа Ферма, пока неизвестно до сих пор. Такие числа
растут очень быстро (для примера, F7=340282366920938463463374607431768211457),
и их проверка является непростой задачей даже для современных компьютеров.
Актуальны ли простые числа сегодня? Еще как! Простые числа являются основой
современной криптографии, так что большинство людей пользуются ими каждый день,
даже не задумываясь об этом. Любой процесс аутентификации, например,
регистрация телефона в сети, банковские платежи и прочее, требуют
криптографических алгоритмов. Суть идеи тут крайне проста и лежит в основе
алгоритма RSA, предложенного еще в 1975 году. Отправитель и получатель
совместно выбирают так называемый «закрытый ключ», который хранится в надежном
месте. Этот ключ представляет собой, как, наверное, читатели уже догадались,
простое число. Вторая часть — «открытый ключ», тоже простое число, формируется
отправителем и передается в виде произведения вместе с сообщением открытым
текстом, его можно опубликовать даже в газете. Суть алгоритма в том, что не зная
«закрытой части», получить исходный текст невозможно.
В чем хитрость? А в том, что произведение двух простых чисел вычислить несложно, а
вот обратной операции не существует — если не знать первой части, то такая
процедура может быть выполнена лишь перебором. И если взять действительно
большие простые числа (например, в 2000 символов длиной), то декодирование их
произведения займет несколько лет даже на современном компьютере (к тому
времени сообщение станет давно неактуальным). Гениальность данной схемы в том,
что в самом алгоритме нет ничего секретного — он открыт и все данные лежат на
поверхности (и алгоритм, и таблицы больших простых чисел известны). Сам шифр
вместе с открытым ключом можно передавать как угодно, в любом открытом виде. Но
не зная секретной части ключа, которую выбрал отправитель, зашифрованный текст
мы не получим. Для примера можно сказать, что описание алгоритма RSA было
напечатано в журнале в 1977 году, там же был приведен пример шифра. Лишь в 1993
году при помощи распределенных вычислений на компьютерах 600 добровольцев,
был получен правильный ответ.
В завершение темы простых чисел, приведем вид так называемой “спирали Улама”.
Математик Станислав Уламоткрыл ее случайно в 1963 году, рисуя во время скучного
доклада на бумаге числовую спираль и отмечая на ней простые числа:
6. Совершенные числа
Еще одно удивительное свойство мира чисел было доказано еще Евклидом
: если
p
число вида 2-1 является простым (уже известное нам число Мерсенна), то число
P-1 p
2 (2-1) является совершенным, т.е. равно сумме всех его делителей.
is_perfect(33550336)
Действительно, 33550336 делится на числа 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
2048, 4096, 8191, 16382, 32764, 65528, 131056, 262112, 524224, 1048448, 2096896,
4193792, 8387584, 16775168. И сумма этих чисел равна искомому 33550336.
p-1 p
Кстати, еще Эйлер доказал, что все совершенные числа имеют только вид 2 (2-1). А
вот нечетных совершенных чисел пока не обнаружено, но и не доказано что их не
существует. Интересно проверить этот факт практически. Совершенное число
137438691328 обнаружил еще немецкий математик Иоганн Мюллер в 16 веке. Сегодня
такое число несложно проверить на компьютере.
def is_perfect(n):
s = Decimal(1)
p = Decimal(2)
while p < n.sqrt()+1:
if n % p == 0:
s += p
if p != n/p: s += n/p
p += 1
return s == n
print(is_perfect(Decimal('137438691328')))
#include <string.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
int main()
{
unsigned long long int n = 137438691328LL;
bool res = isPerfect(n);
printf("%d\n", res);
return 0;
}
int main()
{
unsigned long long int MAX = 200000000000LL;
unsigned long long int p;
for (p=1; p<MAX; p++) {
if (isPerfect(p))
printf(" %llu ", p);
if (p % 1000000 == 0)
printf("*%llu,%llu*", 100*p/MAX, p);
}
}
7. Магический квадрат
Еще одна старинная математическая головоломка - магический квадрат. Магическим
называют квадрат, заполненный неповторяющимися числами так, что суммы чисел по
горизонталям, вертикалям и диагоналям одинаковы. Такие квадраты известны давно,
самым старым из известных является магический квадрат Ло Шу, изображенный в
Китае в 2200г до нашей эры. Если подсчитать количество точек, то можно перевести
квадрат в современный вид, изображенный справа.
Как можно видеть, уже математики прошлого умели строить магические квадраты
разной размерности. Интересно рассмотреть их свойства.
2
Это несложно доказать, т.к. сумма всех чисел квадрата равна сумме ряда 1..N .
Действительно, для квадрата Дюрера M(4) = 34, что можно посчитать на картине. Для
квадратов разной размерности суммы равны соответственно: M(3) = 15, M(4) = 34,
M(5) = 65, M(6) = 111, M(7) = 175, M(8) = 260, M(9) = 369, M(10) = 505.
if j == -1:
return False
k=n-1
while arr[j] >= arr[k]: k -= 1
swap(arr, j, k)
l=j+1
r=n-1
while l < r:
swap(arr, l, r)
l += 1
r -= 1
return True
def show_squares(n):
N = n*n
arr = [i+1 for i in range(N)]
cnt = 0
while next_set(arr, N):
if is_magic(arr, n):
print(arr)
cnt += 1
return cnt
# Требуемая размерность
cnt = show_squares(3)
print("Число вариантов:", cnt)
Остальные являются лишь его поворотами или отражениями (очевидно что при
повороте квадрата его свойства не изменятся).
И наконец, общая сумма: т.к. квадрат заполнен числами 1..16, то если сложить все 4
строки квадрата, то получаем 4S = 1+..+16 = 136, т.е. S=34 (что соответствует
приведенной в начале главы формуле).
Это значит, что мы легко можем выразить последние элементы через предыдущие:
x14 = S - x11 - x12 - x13
x24 = S - x21 - x22 - x23
x34 = S - x31 - x32 - x33
x41 = S - x11 - x21 - x31
x42 = S - x12 - x22 - x32
x43 = S - x13 - x23 - x33
x44 = S + x14 - x14 - x24 - x34
Что это дает? Очень многое. Вместо перебора 16 вариантов суммарным количеством
16!=20922789888000 мы должны перебрать лишь 9 вариантов, что дает 9!=362880
вариантов, т.е. в 57657600 раз меньше! Как нетрудно догадаться, мы фактически
выразили крайние строки квадрата через соседние, т.е. уменьшили размерность
поиска с 4х4 до 3х3. Это же правило будет действовать и для квадратов большей
диагонали.
digits = set(range(1,16+1))
cnt = 0
for x11 in digits:
for x12 in digits - set([x11]):
for x13 in digits - set([x11, x12]):
for x14 in digits - set([x11, x12, x13]):
s = x11 + x12 + x13 + x14
if s < 22: continue
for x21 in digits - set([x11, x12, x13, x14]):
for x22 in digits - set([x11, x12, x13, x14, x21]):
for x23 in digits - set([x11, x12, x13, x14, x21, x22]):
x24 = s - x21 - x22 - x23
if x24 <= 0 or x24 in [x11, x12, x13, x14, x21, x22, x23]: continue
for x31 in digits - set([x11, x12, x13, x14, x21, x22, x23, x24]):
for x32 in digits - set([x11, x12, x13, x14, x21, x22, x23, x24, x31]):
for x33 in digits - set([x11, x12, x13, x14, x21, x22, x23, x24, x31, x32]):
x34 = s - x31 - x32 - x33
x41 = s - x11 - x21 - x31
x42 = s - x12 - x22 - x32
x43 = s - x13 - x23 - x33
x44 = s - x14 - x24 - x34
if x34 <= 0 or x41 <= 0 or x42 <= 0 or x43 <= 0 or x44 <= 0: continue
data = [x11, x12, x13, x14, x21, x22, x23, x24, x31, x32, x33, x34, x41, x42, x43, x44]
if len(data) != len(set(data)): continue
if is_magic(data, 4):
print data
cnt += 1
print cnt
В результате, программа проработала всего лишь около часа (вместо 3х лет!), всего
было выведено 7040 квадратов размерностью 4х4. Разумеется, большинство из них
являются поворотами или отражениями друг друга, было доказано что уникальных
квадратов всего 880.
Вспомним магический квадрат Дюрера, в нижнем его столбце есть цифры 1514,
соответствующие году создания гравюры. С помощью программы можно решить еще
одну задачу: посмотреть сколько всего возможно квадратов с такими цифрами. Здесь
число вариантов перебора еще меньше, т.к. еще 2 цифры фиксированы. Оказывается,
помимо “авторского”, возможны всего 32 варианта, например:
1 15 14 4 2 15 14 3
5 11 8 10 5 10 7 12
12 6 9 7 11 8 9 6
16 2 3 13 16 1 4 13
Интересно, что верхний ряд помимо цифр 15 и 14 может содержать либо 1,4 либо 2,3,
других вариантов нет. Разные варианты содержат лишь перестановки этих цифр.
29 131 107
167 89 11
71 47 149
Приведенную выше программу легко модифицировать для такого расчета: достаточно
лишь заменить множество digits = set(range(1,16+1)) на другое, содержащее простые
числа.
Для примера будем искать квадраты среди трехзначных простых чисел от 101 до 491.
Заменим в предыдущей версии программы строку digits = set([1,2,3,4,5,6,7,8,9]) на
primes = [ 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163,
167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251,
257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443,
449, 457, 461, 463, 467, 479, 487, 491 ]
digits = set(primes)
Т.к. число вариантов перебора больше, программа работает дольше. Время поиска
составило 724с для Python-версии и 316c для программы на C++.
T = 316.00s = C++
T = 724.4s = Python
7 61 43
73 37 1
31 13 67
Примером квадрата 4х4 может быть квадрат с также “красивой” суммой 222:
97 41 73 11
17 47 83 75
59 79 13 71
49 55 53 65
9. Числа Фибоначчи
Возьмем 2 числа: 0 и 1. Следующее число рассчитаем как сумму предыдущих чисел,
затем повторим процесс.
Мы получили последовательность, известную как числа Фибоначчи:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, ...
Как можно видеть, число пар образует как раз те самые числа Фибоначчи. Сама
последовательность Фибоначчи кажется простой. Но чем она интересна? Пример с
кроликами не случаен - эти числа действительно описывают множество природных
закономерностей:
- Множество растений имеют количество лепестков, равное одному из чисел
Фибоначчи. Количество листьев на стебле также может описываться этим
законом, например у тысячелистника.
- Другое известное изображение - спираль Фибоначчи, которая строится по
похожему принципу соотношения размеров прямоугольников:
def printNumbers(n):
i1 = Decimal(0)
i2 = Decimal(1)
for p in range(1, n+1):
print("F({}) = {}".format(p, i2))
fib = i1 + i2
i1 = i2
i2 = fib
getcontext().prec = 100
N = 100
printNumbers(N)
Рассмотрим струну с условной длиной = 1. Будем умножать длину струны на 3/2, если
полученное число больше 2, разделим еще на 2.
1
3/2 = 1,5
1.5 * 3/2 = 2.25, 2.25/2 = 1,125 = 9/8
9/8 * 3/2 = 1,6875 = 27/16
Похожий ряд, если его упорядочить по возрастанию, называется пифагоровым строем:
“до” - 1
“ре” - 9/8
“ми” - 81/64
“фа” - 4/3
“соль” - 3/2
“ля” - 27/16
“си” - 243/128
“до” - 2
Он также называется квинтовым, т.к. ноты получались увеличением на квинту, т.е. на
3/2. Считается, что этот строй использовался еще при настройке лир в древней
Греции, и сохранился вплоть до средних веков. Названия нот разумеется, были другие
- современные названия придумал только через 1000 лет итальянский теоретик
музыки Гвидо д’Ареццов 1025 г..
Разумеется, в древней Греции никто не знал про частоту колебаний звука, зато
древние греки были хорошими геометрами, и проблем с умножением и делением у них
не было. Современная теория колебаний струны появилась гораздо позже, работы
Эйлера
и Д’Аламберабыли написаны в 1750х годах.
Как математически определяются частоты звуков нот? Сейчас мы знаем, что октава
(от “до” до “до” следующей октавы) - это умножение частоты на 2 (или укорочение
струны в 2 раза). Для остальных нот с 18 века используется так называемый “хорошо
темперированный строй”: октава делится на 12 равных промежутков, а
последовательность частот образует геометрическую прогрессию.
Таким образом, если знать частоту любой ноты, все остальные легко рассчитываются
по вышеприведенной формуле.
Очевидно, что “базовая” частота может быть любой. Традиционно принято например,
что частота камертона ноты “Ля” 440Гц. Остальные ноты первой октавы:
ДО 261.6 ДО# 277
МИ 329.6
СИ 494
7/12
Интересно заметить, что квинта в этой системе имеет соотношение частот 2 = 1.49,
что чуть-чуть отличается от “пифагорейского” чистого тона с соотношением 1.5. На
слух “современная квинта” имеет небольшие биения 0,5Гц, соответствующие разности
частот 392 - 392.4. До сих пор есть любители исполнения старинной музыки в
квинто-терцевом строе, называемым “чистым”. В 18м же веке дебаты между
приверженцами “старого” и “нового” строя были довольно-таки острыми. Впрочем,
преимущества равномерно темперированного строя в виде четкого соотношения
между частотами нот и возможности транспонирования музыки в любую другую
тональность “без потери качества” оказались решающими. Сейчас “чистый строй”
имеет лишь историческое значение, и используется лишь иногда для исполнения
старинных произведений.
freqLa = 440
for p in range(-32,32):
freq = freqLa*math.pow(2, p/12.0)
print p,freq
11. Вращение планет
Еще в древней Греции ученые знали, что планеты движутся по небу, но каким
образом? Сотни лет господствовала геоцентрическая система мира - в центре была
Земля, вокруг которой по окружностям двигались Луна, планеты (на то время их было
известно 5) и Солнце:
Такая система казалась вполне логичной и интуитивно понятной (даже сейчас люди
говорят что солнце “всходит” и “заходит”), однако не объясняла астрономам почему
планеты движутся по небу неравномерно, и временами даже в обратную сторону.
Вот так, к примеру, выглядит перемещение по небу планеты Марс, что никак не
укладывается в теорию движения по кругу:
Впрочем геоцентрическая система просуществовала более 1500 лет, только в 16м
веке Коперник издал свой труд «О вращениях небесных сфер», где описывал
вращение планет по круговым орбитам вокруг Солнца. Однако проблемой было то, что
и при этой схеме фактические движения планет не совпадали с расчетными.
Объяснить это не мог никто, пока в 1600 году немецкий математик Иоганн Кеплер не
стал изучать многолетние результаты наблюдений, сделанные астрономом Тихо
Браге. Кеплер был великолепным математиком, но и у него ушло несколько лет чтобы
понять суть и вывести законы, которые и сейчас называются законами Кеплера.
Кеплер считал, что весь мир подчиняется гармонии, и что солнечная система больше
похожа на часовой механизм, чем на божественное творение. Найденные им законы
не только красивы и гармоничны, но и совпали с реальными наблюдениями (уже позже
выяснилось, что законы Кеплера могут быть выведены из законов Ньютона и закона
всемирного тяготения, желающие могут найти доказательства в Википедии).
Что касается Марса, то его орбита более вытянутая, чем орбита Земли, чем и
объясняется разница как в скорости движения, так и в яркости планеты. Картинка с
сайта журнала “Наука и жизнь”:
Кстати, эта картинка хорошо объясняет, почему только некоторые годы благоприятны
для запуска космических кораблей к Марсу - те годы, в которые расстояние между
планетами минимально.
Эту задачу описывал Мартин Гарднер. Известно что у мистера Смита двое детей, и
один из них мальчик. Какова вероятность, что второй из них тоже мальчик?
Интуитивно кажется, что вероятность пола ребенка всегда равна 1/2, но не все так
просто.
Рассмотрим возможные варианты семей с двумя детьми:
- мальчик-мальчик
- мальчик-девочка
- девочка-мальчик
- девочка-девочка
Исходя из списка вариантов, ответ понятен. Вариант “девочка-девочка” по условию не
подходит. Всего остается 3 варианта семей где есть мальчик (М+М, М+Д, Д+М), значит
вероятность что второй ребенок окажется мальчиком, равна 1/3.
Бросание кубика
Вернемся к бросанию кубика. Допустим, мы бросили кубик 5 раз, и все разы выпала
цифра “3”. Какова вероятность, что мы бросим кубик еще раз, и выпадет снова цифра
“3”?
Ответ прост. Интуитивно кажется, что вероятность такого события очень мала. Но в
реальности кубик не имеет какой-либо встроенной “памяти” на предыдущие события.
Какие бы числа не выпадали до текущего момента, вероятность нового числа также
равна 1/6 (а вот если говорить о вероятности выпадения такой серии “в целом”, то она
действительно равна 1/(6*6*6*6*6*6) = 1/46656.
Кстати, такая вероятность это много или мало? Интуитивно кажется что мало, и в
принципе оно так и есть. Одному человеку пришлось бы бросать кубик каждые 10
секунд 4 дня, чтобы дождаться выпадения 6 цифр подряд. Однако если рассматривать
большие числа, то такие вероятности становятся неожиданно большими. Например,
если 6 раз кубик бросят все 5 миллионов жителей Петербурга, то 6 цифр подряд
выпадут примерно у 100 человек - довольно-таки значительное количество. Это на
самом деле важный момент: даже довольно-таки маловероятные события
гарантированно произойдут, если речь идет о большом числе попыток. Это важно при
прогнозировании таких событий как ДТП, аварии, катастрофы, и прочие негативные
явления, которые в большом городе увы, не редкость. По этой же причине редкие
заболевания эффективнее лечить в большом городе - редкая болезнь,
встречающаяся 1 раз на 100000 человек, может практически не встречаться в
небольшом городе и у врачей не будет опыта борьбы с ней, а в мегаполисе таких
больных наберется в несколько раз больше.
def C(n):
return 1000 - 1000*math.factorial(365)/(math.factorial(365-n)*365**n)
for n in range(3,50):
print("{} - {}%").format(n, 0.1*C(n))
Как видно из таблицы, уже при количестве сотрудников 50 человек, хотя бы 1 день
рождения почти гарантированно совпадет (вероятность 97%), а для 24 человек
получаем вероятность равную 0.538, т.е. более 50%.
Казалось бы, причем здесь математика? При том, что столкновение с метеоритом -
случайное событие, его частота подчиняется теории вероятности. На Луне нет
атмосферы, нет эрозии и ветра, поэтому лунная поверхность - идеальная “книга”, в
которой записаны события последних десятков тысяч лет. Изучая поверхность Луны,
можно подсчитать какого размера объекты падали на ее поверхность.
Так называемый Аризонский кратер возник около 50тыс лет назад после падения
метеорита диаметром 50 метром и весом 300 тысяч тонн. Диаметр кратера составляет
более километра. В Сибири находится кратер Попигай размером 100км, он был открыт
в 1946 году.
m = 2**31 - 1
for p in range(10):
x = (1103515245*x + 12345) % m
print(x)
Равномерное распределение
Возьмем игральный кубик и бросим его много раз. Очевидно, что вероятность
выпадения каждого числа одинакова. На графике это можно изобразить примерно так:
Другим примером может быть время ожидания автобуса. Если человек пришел на
остановку в случайное время, то период ожидания может быть любым, от нуля до
максимума интервала движения.
Нормальное распределение
Возьмем группу людей, например в 100 человек, и измерим их рост. Очевидно, что
будет некоторое количество людей небольшого роста, некоторое количество высоких
людей, совсем мало очень высоких, и совсем мало очень низких. Такое
распределение естественно для многих объектов, не только людей, потому оно и
называется нормальным.
Касаемо роста людей, согласно сайту http://tall.life, график роста для мужчин и женщин
имеет следующий вид:
Распределение Пуассона
Следующий вид распределения не менее интересен. Рассмотрим события,
происходящие с некоторой известной интенсивностью независимо друг от друга,
например приход покупателей в магазин. Допустим, в магазин приходит в среднем 10
покупателей в минуту. Какова вероятность, что в какой-то момент времени в магазин
придет 20 покупателей?
Для измерения орбит спутников Юпитера Ремер использовал момент, когда спутник
входит в тень Юпитера - момент, который можно измерить довольно-таки точно. Как
догадался Ремер, запаздывание во времени было связано с движением Земли по
орбите.
Картинка с сайта http://www.speed-light.info:
Более точное значение было получено лишь через 200 лет, французский физик Луи
Физо с помощью зубчатого колеса и двух зеркал получил значение в 312000км/c.
Расстояние между зеркалами было 8,6км, одно зеркало было расположено в доме
отца Физо недалеко от Парижа, второе зеркало было расположено на Монмартре.
Физо нашел такую скорость вращения колеса, при котором луч света проходящий
через зубец колеса затемнялся, что означало что запаздывание света соответствует
скорости вращения колеса.
Как наверное читатели уже догадались, все дело в скорости света. Расстояние от
Земли до Солнца составляет 150млн километров, свет преодолевает его за 8 минут.
Таким образом, когда мы смотрим на небо, мы видим Солнце так, как оно было 8
минут назад. Смотря на Юпитер, мы видим его с запаздыванием примерно в полчаса.
Расстояние до других звезд гораздо больше. Сириус мы видим таким, каким он был 8
лет назад, звезда Вега имеет расстояние в 25 световых лет, столько нужно свету
чтобы долететь до Земли. Туманность Андромеды мы видим с запаздыванием в 2.5
миллиона лет - время сопоставимое с периодом жизни мамонтов и первобытных
людей. Если бы там висело гигантское зеркало, в котором отражалась бы Земля,
гипотетически можно было бы их увидеть, хотя такого размера телескоп конечно же
физически невозможен.
Все просто (хотя и не совсем). Ток в сети переменный - он меняет свое направление с
частотой 50 раз в секунду. В отличие к примеру, от батарейки - если на ней написано
1.5 вольта, это значит что на ней действительно 1.5 вольта и направление тока не
меняется. Но вернемся к розетке. Ток в нее подается не просто так, а с целью
выполнения какой-либо работы. Как измерить работу переменного тока, который в
разные моменты времени движется то в одну, то в другую сторону? Для этого было
введено понятие действующего напряжения - величины постоянного тока ,
способного выполнить ту же работу (например нагрев спирали электроплитки).
Напряжение, которое показывает осциллограф - называется амплитудным . Эти
величины связаны простой формулой:
220 умноженное на √2, дает как раз 310В. Разумеется, обычный тестер откалиброван в
“бытовых” единицах, в режиме измерения переменного тока он покажет 220В. А если
выпрямить напряжение, например диодным мостиком, то тестер покажет как раз 310В
постоянного тока.
И еще немного про переменный ток. Откуда берется напряжение в 380 вольт? Ток от
трансформатора подается по 3м фазам: это 3 линии, напряжение в которых сдвинуто
на разный угол друг относительно друга.
Картинка из Википедии:
Нулевой провод - общий. В квартиры подается напряжение с одной из фаз, значением
в стандартные 220 вольт. Это напряжение называется фазным. Если же используется
3х-фазная сеть целиком, то напряжение между двумя фазами, например в точках a и с
на рисунке, составляет как раз 380 вольт. Это напряжение называется
линейным.
Есть две основные библиотеки для GPU-расчетов - NVidia CUDA и OpenCL. Первая
обладает большими возможностями, однако работает только с картами NVIDIA.
Библиотека OpenCL работает с гораздо большим числом графических карт, поэтому
мы рассмотрим именно ее.
Таким образом, если задача может быть разбита на небольшие блоки, параллельно
обрабатывающие небольшой фрагмент блока данных, такая задача может
эффективно быть решена на GPU.
Для решения такой задачи с помощью OpenCL необходимо выполнить ряд шагов.
1. Написать код микроядра (kernel):
Этот код будет запускаться непосредственно на графических процессорах
видеокарты. Код пишется на языке C. В данном примере мы для упрощения
храним код прямо в виде строки в программе.
const char *KernelSource = "\n" \
"__kernel void primes( \n" \
" __global unsigned int* input, \n" \
" __global unsigned int* output) \n" \
"{ \n" \
" unsigned int i = get_global_id(0); \n" \
" //printf(\"Task-%d\\n\", i); \n" \
" output[i] = 0; \n" \
" unsigned int val = input[i]; \n" \
" for(unsigned int p=2; p<=val/2; p++) { \n" \
" if (val % p == 0) \n" \
" return; \n" \
" } \n" \
" output[i] = 1; \n" \
"} \n" \
"\n";
Суть кода проста. Массив input хранит числа, которые нужно проверить, функция
get_global_id возвращает индекс задачи, которую выполняет данное ядро. Мы
берем число с нужным индексом, проверяем его на простоту, и записываем 0 или
1 в зависимости от результата, в массив output.
3. Подготовить данные
#define DATA_SIZE 1024
cl_uint *data = (cl_uint*)malloc(sizeof(cl_uint) * DATA_SIZE);
cl_uint *results = (cl_uint*)malloc(sizeof(cl_uint) * DATA_SIZE);
7. Освободить данные
free(data);
free(results);
clReleaseMemObject(input);
clReleaseMemObject(output);
clReleaseProgram(program);
clReleaseKernel(kernel);
clReleaseCommandQueue(commands);
clReleaseContext(context);
Как можно видеть, процесс довольно-таки громоздкий, но оно того стоит. Для примера,
проверка простоты 250000 чисел заняла на процессоре Core i5 около 6 секунд. И всего
лишь 0.5 секунд заняло выполнение вышеприведенного кода на встроенной
видеокарте. Для дешевого нетбука с процессором Intel Atom этот же код выполнялся
34 секунды на основном процессоре, и 6 секунд на GPU. Т.е. разница весьма
прилична.
Разумеется, еще раз стоит повторить, что “игра стоит свеч” лишь в том случае, если
задача хорошо распараллеливается на небольшие блоки, в таком случае выигрыш
будет заметен.
s=0
for x in range(1,1000001):
s += x*x
Результаты работы:
Sum = 333333833333500000, T = 0.47s
import time
start_time = time.time()
l = range(1000001)
s = sum(x*x for x in l)
Результаты работы:
Sum = 333333833333500000, T = 0.32s
Быстрее, но лишь чуть-чуть. К тому же, данный код хранит весь массив в памяти, что
неудобно.
int main()
{
clock_t start = clock();
Данный код выполняется за 0.02 секунды, т.е. в 5 раз быстрее первого варианта. Но
разумеется, если заранее известно, что задача состоит в обработке большого набора
чисел (например поиск простых чисел или магических квадратов), то может быть
более целесообразным сразу писать программу на С или С++, в принципе это не
намного сложнее, а работать программа будет быстрее.
Заключение
На этом данная книга закончена, хотя надеюсь, что не навсегда - по возможности и по
мере появления новых идей, новые главы будут дописываться. Автор надеется, что
хоть немного удалось познакомить читателей с увлекательным миром математики и
программирования.
Продолжение следует.