Счастливые билеты
index.htm - English text
HappyTickets.zip - архивированная директория, в которой содержится все необходимое для этого примера.
Целью данного примера является исследование способностей суперкомпилятора по ускорению исполнения программ, содержащих чужие пакеты. В данном случае, пакет для работы с комплексными числами.
Шестизначный автобусный билет называется счастливым, если сумма его первых трех цифр равна сумме последних трех цифр. Например, 123 204 (1+2+3=2+0+4). Определение обобщается на произвольную длину билета и произвольную систему счисления.
Возникает задача определения количества счастливых билетов среди всех 1000000 билетов. Ответ равен 55 252.
Давно известна формула
N = (1 / pi) * (интеграл от 0 до pi от функции ( sin10x / sin x )6 ).
В общем случае число 10 заменяется на основание системы счисления, число 6 - на длину билетов. В дальнейшем для наглядности во всех формулах будут присутствовать числа 10 и 6 вместо букв.
Лет 10 назад я вычислял на компьютере приближенно количество счастливых билетов по приведенной выше формуле, в результате чего обнаружил, что интеграл можно заменить на конечную сумму. В журнале Квант была опубликована соответствующая статья.
Здесь предлагается для вычисления количества счастливых билетов использовать интегрирование в комплексной области.
Верна формула
N = (1 / (2*pi*i)) * (интеграл от функции f(z) по замкнутому контуру в комплексной области, содержащему число нуль),
где
f(z) = ( (z10 - 1) / (z - 1) )6 / z28
при этом 10 - основание системы счисления, можно заменять на любое другое,
6 - длина билетов, можно зпменять на любую другую длину,
28 = (10 - 1) * (6 / 2) + 1
Вторая формула фактически вытекает из процесса доказательства первой формулы. Приведем без деталей схему доказательства.
1. Количество счастливых билетов равно количеству билетов с суммой цифр 27.
2. В многочлене
( 1 + z + z2 + z3 + z4 + z5 + z6 + z7 +z8 + z9 )6
коэффициент при zk равен количеству билетов с суммой цифр k.
3. Значит, количество счастливых билетов равно коэффициенту при x27 в многочлене
( (z10 - 1) / (z - 1) )6
Различные формулы для количества счастливых билетов соответствуют различным способам извлечения указанного коэффициента их этого многочлена.
4. После деления на x28 количество счастливых билетов будет коэффициентом при x-1 .
5. Теперь используем теорию функций комплексной переменной. Коэффициент при x-1 называется вычетом, который равен приведенному выше интегралу. В ТФКП обычно поступают наоборот - для вычисления интегралов используют вычеты .
Суперкомпиляция программ на Яве
Использовался пакет для работы с комплексными числами.
http://www.netlib.org/java/JavaComplex.tgz
http://www.almide.demon.co.uk/html/Complex/Complex_for_Java.html
В программе HappyTickets.java реализовано интегрирование в комплексной области по окружности радиуса 1 с центром в начале координат.
Переменные
public static final int notation = 2; public static final int lengthTicket = 6;
задают основание системы счисления и длину билетов. Все тестовые пуски суперкомплятора производились для двоичной системы счисления. Здесь получаются более обозримые программы, и известен ответ - C2nn (C из 6 по 3 = 20).
При помощи параметров final происходит управление специализацией. Можно получать остаточную программу, которая вычисляет лишь количество указанных счастливых билетов (как здесь). Можно получить общую программу, которая годится для любой системы счисления и любой длины билетов. Можно ограничиться десятичной системой счисления.
Коэффицинт ускорения - 6.5 раз.
Приведем здесь текст программы HappyTickets.java и текст остаточной программы HappyTickets.js. Мы видим, что суперкомпиляция идеальна в том смысле, что не осталось вызовов функций работы с комплексными числами.
/** * Class for calculation of number of the happy tickets * through complex numbers. * For example, * 123 204 - happy ticket, because the sum 1, 2, 3 * is equal to the sum 2, 0, 4 * 123 254 - is not the happy ticket, because the sum 1, 2, 3 * is not equal to the sum 2, 5, 4 */ public class HappyTickets{ public static final int notation = 2; public static final int lengthTicket = 6; public static final int c28 = (notation-1)*(lengthTicket/2)+1; public static int numberSteps = 1000000; public static final Complex Compl1 = new Complex(1.0, 0.0); public static final Complex Compl0 = new Complex(0.0, 0.0); public static final Complex i2pi = new Complex(0.0, 2.0 * Math.PI); public static Complex Compl28 = new Complex(numberSteps, 0.0); /** * Calculation of number of the happy tickets * through complex numbers. * The formula of calculation * N = (1.0 / 2*pi*i) * * integral on the closed contour containing number zero, * from function * f(z) = ((z^10 - 1) / (z - 1))^6 / z^28 * where * 10 - basis of a notation, * 6 - length of the tickets. */ public static void main (String[] args) { Complex res = new Complex(0.0, 0.0); for (int it=0; it<5; it++) { long start = System.currentTimeMillis(); res = test(numberSteps); res = res.div(i2pi); System.out.println("result = "+ res); long end = System.currentTimeMillis(); System.out.println("Total time = "+ (end-start)*0.001); } } /** * The closed contour is a circle of radius 1 * with the centre in the beginning of coordinates */ public static Complex test(int numberSteps1) { double st = 2.0 * Math.PI / numberSteps1; Complex eps = new Complex(Math.cos(st), Math.sin(st)); Complex res = new Complex(0.0, 0.0); Complex epsr = new Complex(1.0, 0.0); for (int it=0; it<numberSteps1; it++) { Complex r1 = eps.mul(epsr); Complex delta = r1.sub(epsr); res = delta.mul(fi(epsr)).add(res); epsr = eps.mul(epsr); } return res; } /** Calculation f(z) * @param x Complex number z */ public static Complex fi(Complex z) { Complex ff1 = new Complex( 0.0, 0.0); Complex ff2 = new Complex( 1.0, 0.0); for (int i=0; i<notation; i++) { ff1 = ff1.add(ff2); ff2 = ff2.mul(z); } Complex ff3 = degc(ff1, lengthTicket); Complex ff4 = ff3.mul(degc(z, c28).conj()); return ff4; } /** Calculation of a degree of complex number * @param ccc complex number * @param ddd degree */ public static Complex degc(Complex c, int d) { Complex r = new Complex( 1.0, 0.0); for (int i = 0; i < d; i++) { r = r.mul(c); } return r; } }
Остаточная программа
//-------------------------------------- 1 sec - postprocessing... public static ORG.netlib.math.complex.Complex test (final int numberSteps1_29) { final double st_33 = 2D * java.lang.Math.PI / (double)numberSteps1_29; final double re_34 = java.lang.Math.cos(st_33) /*static*/; final double im_35 = java.lang.Math.sin(st_33) /*static*/; final ORG.netlib.math.complex.Complex res_39 = new ORG.netlib.math.complex.Complex(0D, 0D); //- this is res_39 = new ORG.netlib.math.complex.Complex(0D, 0D) // res_39.re = 0D; // res_39.im = 0D; final ORG.netlib.math.complex.Complex epsr_42 = new ORG.netlib.math.complex.Complex(1D, 0D); //- this is epsr_42 = new ORG.netlib.math.complex.Complex(1D, 0D) // epsr_42.re = 1D; // epsr_42.im = 0D; ORG.netlib.math.complex.Complex res_261 = res_39; ORG.netlib.math.complex.Complex epsr_260 = epsr_42; int it_259 = 0; while (it_259 < numberSteps1_29) { final double re_282 = re_34 * epsr_260.re - im_35 * epsr_260.im - epsr_260.re; final double im_284 = re_34 * epsr_260.im + im_35 * epsr_260.re - epsr_260.im; final double im_304 = epsr_260.im; final double re_317 = 1D + epsr_260.re; final double re_363 = re_317 * re_317 - im_304 * im_304; final double im_366 = re_317 * im_304 + im_304 * re_317; final double re_379 = re_363 * re_317 - im_366 * im_304; final double im_382 = re_363 * im_304 + im_366 * re_317; final double re_395 = re_379 * re_317 - im_382 * im_304; final double im_398 = re_379 * im_304 + im_382 * re_317; final double re_411 = re_395 * re_317 - im_398 * im_304; final double im_414 = re_395 * im_304 + im_398 * re_317; final double re_427 = re_411 * re_317 - im_414 * im_304; final double im_430 = re_411 * im_304 + im_414 * re_317; final double re_453 = epsr_260.re; final double im_455 = epsr_260.im; final double re_471 = re_453 * epsr_260.re - im_455 * epsr_260.im; final double im_476 = re_453 * epsr_260.im + im_455 * epsr_260.re; final double re_491 = re_471 * epsr_260.re - im_476 * epsr_260.im; final double im_496 = re_471 * epsr_260.im + im_476 * epsr_260.re; final double re_511 = re_491 * epsr_260.re - im_496 * epsr_260.im; final double im_533 = -(re_491 * epsr_260.im + im_496 * epsr_260.re); final double re_541 = re_427 * re_511 - im_430 * im_533; final double im_544 = re_427 * im_533 + im_430 * re_511; final double re_563 = re_282 * re_541 - im_284 * im_544 + res_261.re; final double im_565 = re_282 * im_544 + im_284 * re_541 + res_261.im; final ORG.netlib.math.complex.Complex res_566 = new ORG.netlib.math.complex.Complex(re_563, im_565); //- this is res_566 = new ORG.netlib.math.complex.Complex(re_563, im_565) // res_566.re = re_563; // res_566.im = im_565; final double re_575 = re_34 * epsr_260.re - im_35 * epsr_260.im; final double im_580 = re_34 * epsr_260.im + im_35 * epsr_260.re; final ORG.netlib.math.complex.Complex epsr_581 = new ORG.netlib.math.complex.Complex(re_575, im_580); //- this is epsr_581 = new ORG.netlib.math.complex.Complex(re_575, im_580) // epsr_581.re = re_575; // epsr_581.im = im_580; it_259++; epsr_260 = epsr_581; res_261 = res_566; continue;} /*while*/ return res_261; } //-------------------------------------- 2 sec - JScp version 0.0.75