Pull to refresh

Comments 25

Мне кажется, что выпуклые функции синус и косинус можно аппроксимировать не только рядом Тейлора ( многочленом вида a*x + b*x^3 +c*x^5 ... ) , а обычной параболой поведенной через точки 1,2,3 как на рисунке

Кажется - это плохо.

Если нужно посчитать быстро и не очень точно, то лучше таблица + линейная интерполяция

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

требуемую точность можно и размером таблицы подобрать

Если нужно быстро и не очень точно, то есть старая добрая формула Бхаскара, работающая на отрезке от 0 до 180°.
image
а отрезка от 0 до 90° мало? + что проще считать? где больше делений и умножений?

Это больше вопрос с индусу, который в 7 веке додумался до этой формулы;).

ЗЫ: нужна ли программисту математика за пределами школьной?

В геймдеве - очень нужна, вообще всё завязано на алгебре и аналитической геометрии

это первый курс института, но ок

вопрос так и стоял, первый курс в моем понимании - за пределами школьной

Отрезка 0-90°, конечно, достаточно, но формула симметрична относительно 90°.
А по вычислениям тут два вычитания, два умножения и деление.
если считать по предлагаемому мной способу, то всего на 6 интервалах точность вычисления синуса примерно в 10 раз выше чем по формуле Бхаскара. + по сложности (число делений и умножений ) как вычисление числителя. Снимаю шляпу перед Бхаскаром. Очень интересно, как он до своей формулы додумался?
У вас полином второй степени ax2+bx+c. Значит три умножения и два сложения.
y = x*x, y = a*y, z = b*x, z = z+y, z = z+c.

Столько же операций и в формуле Бхаскара, только там не надо искать интервал в таблице.
y = 180-x, y = y*x, z = 40500-y, y = y*4, y = y/z.

Операция деления совсем не равнозначна по машинным циклам умножению.

 Rsa97 вы не правы у меня формула (a*x + b) *x + c. Значит два умножения и два сложения + у меня точность "регулируемая" + нет деления

Мк без математики, это как запорожец на соревнованиях формулы F1. Ну получили синус, а дальше-то что с ним делать? Оно конечно будет ехать, и даже с хорошей скоростью, но только по прямой линии. А чтобы проходить повороты - нужна математика.

https://github.com/AVI-crak/Rtos_cortex/blob/master/math_s.c#L356

Целью данной публикации попытка предложить способ расчета синуса и косинуса достаточно быстро

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

Мне кажется, что выпуклые функции синус и косинус можно аппроксимировать ...

Математика – это точная наука, здесь не может ничего казаться. Как только вы определите критерии, сразу появится возможность будет вычислить, можно или нельзя.

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

У вас публикация и так короткая, можно было бы и добавить (ну или хотя бы выложить на github). Не то, что это сложно или лично мне интересно, но просто глаз резануло от такого заявления.
согласен, что математика точная наука. но фактически все наши вычисления с вещественными числами приближенные. код не привел т.к. он существенно зависит от требуемой точности вычислений и размера интервала (шага). А так достаточно просто, определили в какой интервал попадает аргумент, вытащили коэффициенты ( могут храниться в массиве ), вычислили полином степени 2.
но фактически все наши вычисления с вещественными числами приближенные.

Не понял, к чему это. Я же не писал, что мы вычисляем что-то идеально точно.

код моего метода расчета синуса и косинуса приведен в комментариях к https://habr.com/ru/post/659089/ + в конце скрипт для расчета коэффициентов в зависимости от числа интервалов на 0 ... пи/2

Мне кажется, линейной функцией будет еще быстрее :)

ПС: а можно и константой 0.5 :) Так будет практически мгновенно и довольно точно :)

А на интервале (0..2Pi) лучше аппроксимировать нулем!

В последнюю неделю — уже не нулём, а π.

пример функции косинус на golang интервал 0...90 градусов

интервал разбит на 4 равных отрезка

максимальное отклонение от настоящего косинуса на 85 градусах =

0.08715574274765812 - 0.0876323 =   -0,000476557

// sin_cos project main.go
package main
import (
	"fmt"
	"math"
)
const pi32 float64 = math.Pi
func Cos4(ugol float32) float32 {
	///  ugol от 0 до пи/2 
	var interval int8
	var a float32
	var b float32
	var c float32
	interval = int8((8 * float64(ugol)) / pi32)
	switch interval {
	case 0:
		a = -0.4888191
		b = -0.0018803
		c = 1
	case 1:
		a = -0.4144009
		b = -0.0638028
		c = 1.0128406
	case 2:
		a = -0.2768938
		b = -0.2824576
		c = 1.0997504
	case 3:
		a = -0.0972322
		b = -0.7072145
		c = 1.3508006
	default:
		a = 0
		b = 0
		c = 0
	}
	return ugol*(a*ugol+b) + c
}
func main() {
	var arg1 float64
	for ii := 0; ii <= 90; ii++ {
		arg1 = float64(ii) * pi32 / float64(180) ///, cos(1)
		fmt.Print(ii, "\t", arg1, "\t", math.Cos(arg1))
		fmt.Print("\t", Cos4(float32(arg1)))
		fmt.Println("\t", 1000*(float32(math.Cos(arg1))-Cos4(float32(arg1))))
	}
}

cos=x=>1-.4053*x*x; // грязная апроксимация косинуса (для +-Pi/2) максимальная погрешность 5%
sin=x=>(1.27325-0.40529*Math.abs(x))*x //погрешность 5.5%

Помниться забавы ради развлекался изучал что можно "выжать" из второй степени без всяких разбиений - первая проблема собственно что парабола относительно совпадает с косинусом до 90 градусов если ее просто отмасштабировать получается полином `cos=x=>1-.4053*x*x`.
Через формулы приведения можно бы получить то же и для синуса, но в такой формуле появляется перемена знака... и нужен был как бы назвать "инверсный квадрат" - при возведении в который всегда получается отрицательный знак... тогда бы получалось 2й степенью обойтись без кондишнов и для синуса. В итоге это развернулось что один из иксов в формулу берется с отбросом знака.

В принципе для максимально быстрого отброса знака почти честно можно ксорить все биты числа на его знаковый бит, для этого (вероятнее всего у вас не будет float который бы работал еще и с XOR) понадобиться пересчитать найденные константы в арифметику статической точки, что бы все считать в целых

погрешность может быть уменьшена ценой потери контрольных точек `sin=x=>(1.217-0.38529*Math.abs(x))*x` до 3.9% но обычно точность контрольных точек чрезвыйчайно ожидается множеством алгоритмов, которые без нее могут начать работать не правильно.

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

dErr=(f,g,x=0,to=Math.PI/2,steps=100)=>{
	let step= (to-x)/steps, e, ers=[];
	for(; x<=to; x+=step){
	    if((e=Math.abs(f(x)-g(x)))< ers[(ers.length=30)-1]?.e)continue;
	    ers.splice(ers.findIndex(o=>o===undefined|| e>o.e),0,{e, x})
	}
	return ers;
}
console.log(dErr(Math.sin,sin))

оказалось что в некоторых случаях 5% погрешности вполне визуально допустимы.
Еще стоит вспомнить что столько степень полинома ресурсозатратна сколько количество членов и например формула синуса на основе коэффициентов Тэзлора 5го порядка уже довольно визуально точна но в ней нет 2го и 4го членов `sinTailor5=x=>x - x**3/6 + x**5/120` что то же самое что `x(1-xx(1/6+xx/120)`, где на возведениях в квадрат экономим заводя переменную xx. Важно так же отметить что эта формула является частичной суммой, потому не оптимальна, и ее как и формулу до 3го порядка `x=> x - x**3/6` можно значительно подтюнить масштабированием коэффициентов.

Так же не стоит забывать, если вы почему-то считаете много синусов, то если у вас получиться их хотя бы равномерно упорядочить, то полином может считаться феерически быстро дельта функциями. В этом случае дело в том, что значение sinTailor5(x+dx) отличается от sinTailor5(x) на полином меньшей на один степени (потому что см. степенная производная)
Тот отличается от предыдущего на полином третьей, который отличается на полином 2ой... короче... итеративный счет полинома 5й степени это не больше 6 сложений - гарантировано.

Когда я говорю не больше я имею ввиду что может быть и меньше, но это уже отдельная битхакерская песня... В качестве задачи с двумя звездочками на досуге можете сунуть ЭТО в консоль броузера и подумать как и почему это оно выводит квадраты (учитывая что в цикле только есть умножение на константу):

let x=new Uint32Array(1).fill(531489<<8), c=8193/8192;
new Uint8Array(31).fill().map(()=>x[0]*=c);

Sign up to leave a comment.

Articles