Вычисление sin(x)

index.htm - English text 

Focus.zip - архивированная директория. 

 

Рассмотрим следующую программу вычисления sin(x) через обычное разложение в ряд      sin(x) = x - x3/3! + x5/5! - x7/7! + ...

/**
 *  Supercompilation of calculation of sin(x) 
 */
public class Sn{
//    public static final int iters = 3;
//    public static final int iters = 100;
    public static final int iters = 1000;
    public static double x = 3.141592653589793D / 6;

    public static void main (String[] args) {
        double x2n = x;
        double res = x;
        double step = 2.0;
        double znam = -1.0 / 6.0;

        for (int i=0; i<iters; i++) {
            x2n = x2n * x * x ;
            res = res + (x2n * znam);
            step = step + 2.0;
            znam = -znam / (step * (step+1.0));
        }
        System.out.println("x      = " + x);
        System.out.println("sin(x) = " + res);
    }
}

Программа вычисляет сумму слагаемых ряда в количестве iters. Переменная iters зафиксирована, и по ней будет происходить специализация. Переменная x для суперкомпилятора не известна (x = 3.141592653589793D / 6 исключительно для контрольных пусков на счет, должно получиться 0.5).

 При iters = 3 получается очень хорошая остаточная программа

//--------------------------------------   0 sec - method Sn.main(java.lang.String[])
//--------------------------------------   0 sec - postprocessing...
	public static void main (final java.lang.String[] args_2)
	{
	  final double sn_x_3 = Sn.x;
	  final double sn_x_4 = Sn.x;
	  final double x2n_9 = sn_x_3 * Sn.x * Sn.x;
	  final double x2n_23 = x2n_9 * Sn.x * Sn.x;
	  final double res_39 = sn_x_4 + 
	                        x2n_9 * -0.16666666666666666D + 
	                        x2n_23 * 0.008333333333333333D +
	                        x2n_23 * Sn.x * Sn.x * -1.984126984126984e-4D;
	  java.lang.System.out.println("x      = " + Sn.x) /*virtual*/;
	  java.lang.System.out.println("sin(x) = " + res_39) /*virtual*/;
	  return;
	}
//--------------------------------------   0 sec - JScp version 0.0.77 

Теперь меняю iters = 100, получаю через 30 секунд программу sn1000.js.

Смотрю количество слагаемых и вижу, что их явно меньше 100.

С целью посмотреть, что произойдет дальше полагаю последовательно iters = 200, iters = 400, iters = 1000.

Время суперкомпиляции увеличивается незначительно, остаточная программа совершенно не меняется !

Здесь мы наблюдаем знание суперкомпилятора о том, что A + 0 = A.

Все описанное можно посмотреть в директории focus приложения Focus.zip.

Перейдем ко второму примеру, расположенному в директории focus2 приложения Focus.zip.

Здесь организуем суммирование ряда для sin(x) с обратного конца.

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

Исходная программа 

/**
 *  Supercompilation of calculation of sin(x)
 *  Calculation sin(x) through decomposition
 *  sin(x) = x - x^3/3! + x^5/5! - x^7/7! + ...
 *  The return order of addition.
 */
public class Sn {
//    public static final int iters = 200; /* 2*Quantity of addition*/
    public static final int iters = 2000; /* 2*Quantity of addition*/


    public static void main (String[] args) {
        int iprint1 = 5;
        int iprint = 100000;

        for (int ip = 0; ip < iprint1; ip++) {
            long start = System.currentTimeMillis();

            double x = 3.141592653589793D / 6;
            double res = 0.0;

            for (int i = 0; i < iprint; i++) {
                res = sin(x);
            }

            System.out.println("x      = " + x);
            System.out.println("sin(x) = " + res);
       
            long end = System.currentTimeMillis();
            System.out.println("Total time = "+ (end-start)*0.001);
        }
    }

    public static double sin(double x) {
        return sin1(x, x, 2, 1.0);
    }


    /** One step of calculation sin(x)
      * @param double x    - argument sin(x)
      * @param double x2n  - x^(2*n+1)
      * @param int step    - 2*n
      * @param double znam - +-1/(2*n+1)! 
      */
    public static double sin1(double x, double x2n, int step, double znam) {
        if (step==iters) return 0.0;
          else return sin1(x, x2n*x*x, step+2, -znam/(step*(step+1)))
                      + x2n*znam;
    }
}

Остаточные программы sn.js одинаковые для iters=100 и для iters-1000.

Если iters=100, то время исполнения исходной программы 1.95 секунды, остаточной программы - 0.70 секунды, ускорение - 2.8 раза.

Теперь меняем iters = 1000, в результате чего остаточная программа не меняется, а исходная будет исполняться в 10 раз дольше.

Время исполнения исходной программы 16.25 секунды, остаточной программы - 0.70 секунды, ускорение - 23.2 раза.

В этом примере много предположений и недостатков, но идея использования таким образом суперкомпиляции - хорошая идея.