Предварительный расчет - это путь, если вы хотите максимальную скорость. Поскольку у вас есть 2 16 (65 536) возможных входов, наивный подход заключается в том, чтобы иметь массив из такого количества 16-битных значений (всего используется 128 КБ памяти).
Но это можно немного улучшить, если вы не хотите использовать всю эту память (за счет небольшой скорости).
Предполагая, что ваши входные данные выражены в радианах (я предполагаю, что они таковы, поскольку ваши диапазоны для числа 4.12 составляют от 0 до 15.999 ..., недостаточно для представления всех градусов по кругу), вы можете воспользоваться фактами, которые :
sin (n) = sin (n % (2*PI)) for n >= 2*PI
sin (n) = -sin (n - PI) for PI <= n < 2*PI
sin (n) = sin (PI - n) for PI/2 <= n < PI
Таким образом, ваша справочная таблица должна содержать только значения от 0 до PI / 2 радиан, что резко сокращает требования к хранилищу: вы храните только от 0 до PI / 2 (~ 1.571), а не полный диапазон от 0 до 15.999 .. ., сокращение примерно на 90%.
Тогда вам просто нужно уменьшить значения до значений ниже 2 * Пи радиан по модулю (первое правило) и изменить его с помощью двух других правил, чтобы найти правильный индекс для поиска. Модуль будет работать с фиксированной точкой так же быстро, как и с целыми числами.
Так что вы бы смотрели на что-то вроде:
def sin(r):
if r >= PI_BY_2:
return sin (r % PI_BY_2)
if r >= PI:
return -sin (r - PI)
if r >= PI_DIV_2:
return sin (PI - r)
return sin_lookup[r]
def cos(r):
if r < PI_DIV_2:
return sin (r + PI_DIV_2_BY_3)
return sin (r - PI_DIV_2)
Эта функция cos
показывает, как дешево получать косинусы из одной таблицы, поскольку на самом деле они имеют сдвиг фазы на 90 градусов по отношению к синусам.
Если скорость чрезвычайно важна, а память не имеет значения, просто используйте полный диапазон индексов, чтобы не требовались никакие вычисления.
Еще одна хитрость, если вы готовы пожертвовать некоторой точностью при выигрыше в использовании меньшего количества памяти, это не хранить значения поиска для всех входных значений.
Учитывая, что функция синуса является гладкой (без разрывов), вы можете хранить каждое второе значение и интерполировать значения для промежуточных значений, просто усредняя значения по обе стороны от нее.
Например, если у нас была функция f(x) = x * x
, следует таблица действительных и интерполированных значений (предположим, что мы сохраняем значения только для четных x
, нечетные значения x
интерполируются, отмечены *
ниже):
x real f(x) interpolated f(x)
-- --------- -----------------
0 0 0
1 1 2 *
2 4 4
3 9 10 *
4 16 16
5 25 26 *
6 36 36
7 49 50 *
8 64 64
9 91 82 *
10 100 100
Теперь это не идеальный пример, поскольку разброс между значениями f (x) может быть довольно большим, но он лучше работает для функций, где значения ближе друг к другу.
Например, действительные значения sin (0), sin (1) и sin (2) (градусы, а не радианы) равны 0, 0,017452406 и 0,034899496 (и именно здесь наклон является наибольшим). Среднее значение sin (0) и sin (2) составляет 0,017449748, что составляет погрешность 0,0152% от действительного значения, одна часть в 6500
.
Таким образом, для минимальной ошибки вы могли бы вдвое сократить требования к памяти примерно до 5% от 128 КБ или до шести с половиной К.