Как я могу переписать следующий псевдокод на С++?
real array sine_table[-1000..1000]
for x from -1000 to 1000
sine_table[x] := sine(pi * x / 1000)
Мне нужно создать таблицу поиска sine_table.
Как я могу переписать следующий псевдокод на С++?
real array sine_table[-1000..1000]
for x from -1000 to 1000
sine_table[x] := sine(pi * x / 1000)
Мне нужно создать таблицу поиска sine_table.
Вы можете уменьшить размер своей таблицы до 25% от исходного, сохранив значения только для первого квадранта, то есть для x в [0,pi/2].
Для этого вашей подпрограмме поиска просто нужно сопоставить все значения x с первым квадрантом, используя простые идентификаторы триггеров:
Чтобы сопоставить квадрант III с квадрантом I, примените оба тождества, т. е. sin(x) = - sin (pi + x)
Поможет ли эта стратегия, зависит от того, насколько важно использование памяти в вашем случае. Но кажется расточительным хранить в четыре раза больше значений, чем вам нужно, только чтобы избежать сравнения и вычитания или двух во время поиска.
Я поддерживаю рекомендацию Джереми оценить, лучше ли построение таблицы, чем просто использование std::sin(). Даже с исходной большой таблицей вам придется тратить циклы при каждом просмотре таблицы, чтобы преобразовать аргумент в ближайшее приращение pi/1000, и вы потеряете некоторую точность в этом процессе.
Если вы действительно пытаетесь обменять точность на скорость, вы можете попытаться аппроксимировать функцию sin(), используя только первые несколько членов разложения в ряд Тейлора.
Конечно, для эффективности вы должны предварительно вычислить факториалы и использовать более низкие степени x для вычисления более высоких, например. используйте x^3 при вычислении x^5.
И последнее замечание: приведенный выше усеченный ряд Тейлора более точен для значений, близких к нулю, поэтому все же стоит сопоставить первый или четвертый квадрант перед вычислением приблизительного синуса.
Приложение: Еще одно потенциальное улучшение, основанное на двух наблюдениях:
1. Вы можете вычислить любую триггерную функцию, если вы можете вычислить и синус, и косинус в первом октанте [0,pi/4]
2. Тейлора разложение в ряд с центром в нуле более точно вблизи нуля
Таким образом, если вы решите использовать усеченный ряд Тейлора, вы можете повысить точность (или использовать меньше членов для аналогичной точности), сопоставив либо синус, либо косинус, чтобы получить угол в диапазоне [0,pi/4], используя такие тождества sin(x) = cos(pi/2-x) и cos(x) = sin(pi/2-x) в дополнение к приведенным выше (например, если x > pi/4 после отображения на первый квадрант.)
Или, если вы решите использовать поиск по таблице как для синуса, так и для косинуса, вы можете обойтись двумя меньшими таблицами, которые охватывают только диапазон [0,pi/4] за счет другого возможного сравнения и вычитания при поиске для сопоставления с меньший диапазон. Тогда вы можете либо использовать меньше памяти для таблиц, либо использовать ту же память, но с большей детализацией и точностью.
sin (-x) = -sin(x)
- person Ben Voigt; 19.09.2010
Еще один момент: вызов тригонометрических функций стоит дорого. если вы хотите подготовить справочную таблицу для синуса с постоянным шагом - вы можете сэкономить время расчета за счет некоторой потенциальной потери точности.
Считайте, что ваш минимальный шаг — «а». То есть вам нужен sin(a), sin(2a), sin(3a),...
Тогда вы можете проделать следующий трюк: сначала вычислить sin(a) и cos(a). Тогда для каждого последующего шага используйте следующие тригонометрические равенства:
Недостаток этого метода заключается в том, что при этой процедуре накапливается ошибка округления.
Вам понадобится функция std::sin()
из <cmath>
.
double table[1000] = {0};
for (int i = 1; i <= 1000; i++)
{
sine_table[i-1] = std::sin(PI * i/ 1000.0);
}
double getSineValue(int multipleOfPi){
if(multipleOfPi == 0) return 0.0;
int sign = 1;
if(multipleOfPi < 0){
sign = -1;
}
return signsine_table[signmultipleOfPi - 1];
}
Вы можете уменьшить длину массива до 500 с помощью трюка sin(pi/2 +/- angle) = +/- cos(angle). Поэтому сохраните sin и cos от 0 до pi/4. Я не помню из головы, но это увеличило скорость моей программы.
другое приближение из книги или что-то
streamin ramp;
streamout sine;
float x,rect,k,i,j;
x = ramp -0.5;
rect = x * (1 - x < 0 & 2);
k = (rect + 0.42493299) *(rect -0.5) * (rect - 0.92493302) ;
i = 0.436501 + (rect * (rect + 1.05802));
j = 1.21551 + (rect * (rect - 2.0580201));
sine = i*j*k*60.252201*x;
полное обсуждение здесь: http://synthmaker.co.uk/forum/viewtopic.php?f=4&t=6457&st=0&sk=t&sd=a
Я полагаю, вы знаете, что использование деления намного медленнее, чем умножение на десятичное число, /5 всегда медленнее, чем *0,2
это просто приближение.
также:
streamin ramp;
streamin x; // 1.5 = Saw 3.142 = Sin 4.5 = SawSin
streamout sine;
float saw,saw2;
saw = (ramp * 2 - 1) * x;
saw2 = saw * saw;
sine = -0.166667 + saw2 * (0.00833333 + saw2 * (-0.000198409 + saw2 * (2.7526e-006+saw2 * -2.39e-008)));
sine = saw * (1+ saw2 * sine);
#include <cmath>
2) Это фактически создает на один элемент меньше; это эквивалентно sine_table[-1000..999]
в псевдокоде.
- person Mike DeSimone; 11.09.2010
sin
? Справляетесь с тем, что вам нужны отрицательные индексы? Объявить массив? Написание циклаfor
на С++? Узнать имя типа в С++, который представляет собой действительное число? Все они? - person Steve Jessop   schedule 11.09.2010x — (x^3)/(3!) + (x^5)/(5!) — (x^7)/(7!) + ...
- person Mike DeSimone   schedule 11.09.2010sin
. В этом случае Тейлор является наиболее известным алгоритмом для программной реализации, но компьютеры АЛУ, калькуляторы и другие цифровые системы часто эмулируют функцию синуса (и многие другие) аппаратно, реализуя CORDIC (примеры а>). Он занимает меньше места, чем полная таблица поиска. - person mins   schedule 29.10.2020