добавлено: 11 Jul 2008 10:53 Китайская теорема об остаткахФормулировкаВ своей современной формулировке теорема звучит так: Пусть Поставим в соответствие произвольному числу Тогда это соответствие (между числами и кортежами) будет являться взаимно однозначным. И, более того, операции, выполняемые над числом Т.е., если то справедливо: В своей первоначальной формулировке эта теорема была доказана китайским математиком Сунь-Цзы приблизительно в 100 г. н.э. А именно, он показал в частном случае эквивалентность решения системы модулярных уравнений и решения одного модулярного уравнения (см. следствие 2 ниже). Следствие 1Система модулярных уравнений: имеет единственное решение по модулю (как и выше, Следствие 2Следствием является связь между системой модулярных уравнений и одним соответствующим модулярным уравнением: Уравнение: эквивалентно системе уравнений: (как и выше, предполагается, что Алгоритм ГарнераИз китайской теоремы об остатках следует, что можно заменять операции над числами операциями над кортежами. Напомним, каждому числу Это может найти широкое применение на практике (помимо непосредственного применения для восстановления числа по его остаткам по различным модулям), поскольку мы таким образом можем заменять операции в длинной арифметике операциями с массивом "коротких" чисел. Скажем, массива из Будем искать решение в виде: т.е. в смешанной системе счисления с весами разрядов Обозначим через Подставим выражение Подставим теперь выражение во второе уравнение: Преобразуем это выражение, отняв от обеих частей Подставляя в третье уравнение, аналогичным образом получаем: Уже достаточно ясно видна закономерность, которую проще всего выразить кодом: for (int i=0; i<k; ++i) { x[i] = a[i]; for (int j=0; j<i; ++j) { x[i] = r[j][i] * (x[i] - x[j]); x[i] = x[i] % p[i]; if (x[i] < 0) x[i] += p[i]; } } Итак, мы научились вычислять коэффициенты Стоит заметить, что на практике почти всегда вычислять ответ нужно с помощью Длинной арифметики, но при этом сами коэффициенты Реализация алгоритма ГарнераУдобнее всего реализовывать этот алгоритм на языке Java, поскольку она содержит стандартную длинную арифметику, а потому не возникает никаких проблем с переводом числа из модульной системы в обычное число (используется стандартный класс BigInteger). Приведённая ниже реализация алгоритма Гарнера поддерживает сложение, вычитание и умножение, причём поддерживает работу с отрицательными числами (об этом см. пояснения после кода). Реализован перевод числа обычного десятичкого представления в модулярную систему и наоборот. В данном примере берутся final int SZ = 100; int pr[] = new int[SZ]; int r[][] = new int[SZ][SZ]; void init() { for (int x=1000*1000*1000, i=0; i<SZ; ++x) if (BigInteger.valueOf(x).isProbablePrime(100)) pr[i++] = x; for (int i=0; i<SZ; ++i) for (int j=i+1; j<SZ; ++j) r[i][j] = BigInteger.valueOf( pr[i] ).modInverse( BigInteger.valueOf( pr[j] ) ).intValue(); } class Number { int a[] = new int[SZ]; public Number() { } public Number (int n) { for (int i=0; i<SZ; ++i) a[i] = n % pr[i]; } public Number (BigInteger n) { for (int i=0; i<SZ; ++i) a[i] = n.mod( BigInteger.valueOf( pr[i] ) ).intValue(); } public Number add (Number n) { Number result = new Number(); for (int i=0; i<SZ; ++i) result.a[i] = (a[i] + n.a[i]) % pr[i]; return result; } public Number subtract (Number n) { Number result = new Number(); for (int i=0; i<SZ; ++i) result.a[i] = (a[i] - n.a[i] + pr[i]) % pr[i]; return result; } public Number multiply (Number n) { Number result = new Number(); for (int i=0; i<SZ; ++i) result.a[i] = (int)( (a[i] * 1l * n.a[i]) % pr[i] ); return result; } public BigInteger bigIntegerValue (boolean can_be_negative) { BigInteger result = BigInteger.ZERO, mult = BigInteger.ONE; int x[] = new int[SZ]; for (int i=0; i<SZ; ++i) { x[i] = a[i]; for (int j=0; j<i; ++j) { long cur = (x[i] - x[j]) * 1l * r[j][i]; x[i] = (int)( (cur % pr[i] + pr[i]) % pr[i] ); } result = result.add( mult.multiply( BigInteger.valueOf( x[i] ) ) ); mult = mult.multiply( BigInteger.valueOf( pr[i] ) ); } if (can_be_negative) if (result.compareTo( mult.shiftRight(1) ) >= 0) result = result.subtract( mult ); return result; } } О поддержке отрицательных чисел следует сказать особо (флаг
|