Как стать автором
Обновить

Комментарии 6

Вангую, что вот в таком фрагменте


factorial(points.Length - 1) / (factorial(i) * factorial(points.Length - 1 - i))

вы наступите на обалденные грабли. Факториал растет чуть медленнее, чем xx, поэтому на 13! происходит переполнение как int, так и uint, и в unchecked контексте получится ерунда (не знаю как там Unity работает с числами).
Есть так же ненулевой шанс отхватить при умножении Point на факториал большого числа (что будет вычислено первым), после чего получить Point с потерянной точностью (вы используете float — поля), и уже его делить на оставшиеся факториалы (если я правильно помню приоритет операторов и направление вычисления).


Вы на каждом шаге вычисляете factorial(points.Length - 1), хотя его можно было бы закешировать.
Я уже не говорю о том, что вычисление биномиальных коэффициентов можно упростить и избавиться от деления больших чисел, например Ckn = ((k + 1) * (k + 2) * ... * n) / (n -k)! в случае когда k > n / 2 (для k < n / 2 можно воспользоваться симметрией), что требует меньше вычислительных операций. Так C810 это всего лишь 9 * 10 / 1 / 2 = 45, а не 3628800 / 40320 / 2.
Ну и большинство значений как факториала так и биномиального коэффициента можно посчитать в одну прогонку по заданному points.Length и затем использовать по необходимости.

Я использую поля типа float т.к. в Unity для изменения положения объекта требуется Vector2 или Vector3, которые имеют два и три поля типа float соответственно. Я нигде и не говорил, что мой код имеет хорошую оптимизацию)

Ну вы можете использовать double для вычислений и кастовать к float когда изменяете transform объекта, сохраняя точность максимально долго.


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

Ну так если кому-то надо использовать double, он может изменить код. Если уж совсем придираться, почему бы не написать свой тип? Тогда можно точность вообще до небес поднять) Еще раз повторюсь, я не говорю что мой код оптимален.
Как говорят «вставлю и я своих 5 копеек», возможно это кому то пригодится.
Как и в моделировании физических процессов для научных робот так и для симуляции в играх неоспоримо важна скорость не теряя точности.
В данном случае, алгоритм вычисления факториала имеет свой большой «плюс» — очень прост и понятен, и не отвлекает от главной цели статьи.
Но, все же, если хотите ускорить или оптимизировать его расчет можно воспользоваться Гамма-функцией (там все очень просто) и двумя аппроксимированными формулами Стирлинга (Stirling) и Рамануджана (Ramanujan).
Замечу, что точное значение они рассчитывают до 11 (Ст.) и 9 (Рам.) соответственно. И вроде бы формула Стирлинга дает лучшее быстродействие.
Что бы «облегчить жизнь», даю ссылку на код C# по вычислению факториала: . Unit-тесты прилагаются и замеры скорости вычисления с помощью разных способов, в том числе реализацию через указатели и небезопасный код и многопоточность при расчете массива факториалов.
Также, замечу, что
написать свой тип
не нужно, в C# уже давно есть для этого такой тип как BigInteger (неизменяемый), которому не страшны никакие переполнения. А вот и ссылка на него . Я его также реализовал в коде.
Чем смог тем помог, пользуйтесь на здоровье! Удачи!
Спасибо большое!
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории