Исключение Гаусса по модулю p

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

Мой алгоритм берет первую строку, нормализует ее, находя обратный первому элементу, и умножает каждый элемент в строке на этот обратный. Затем он вычитает эту строку из других строк столько раз, чтобы их первый элемент стал нулевым. На следующей итерации он переходит к следующей строке и выполняет ту же процедуру, пока элемент поворота строки i не окажется в столбце i. Наконец, он вычитает каждую строку из предыдущих, чтобы сделать только один ненулевой элемент каждого столбца (кроме последнего). (На данный момент я использую двойные, в этом нет необходимости, но это не должно быть проблемой). Вот мой код:

// Transforms A so that the leftmost square matrix has at most one 1 per row,
    // and no other nonzero elements.
    // O(n^3)
 public static void gauss(int[][] A, int num_columns) {
        int n = A.length;
        int m = A[0].length;

        for (int i = 0; i < num_columns; i++) {
            // Finding row with nonzero element at column i, swap this to row i
            for(int k = i; k < num_columns; k++){
                if(A[k][i] != 0){
                    int t[] = A[i];
                    A[i] = A[k];
                    A[k] = t;
                }
            }
            // Normalize the i-th row.
            int inverse = (int)inverse((long)A[i][i], prime);
            for (int k = i ; k < m; k++) A[i][k] = (A[i][k]*inverse) % prime;

            // Combine the i-th row with the following rows.
            for (int j = 0; j < n; j++) {
                if(j == i) continue;
                int c = A[j][i];
                A[j][i] = 0;
                for (int k = i + 1; k < m; k++){
                    A[j][k] = (A[j][k] - c * A[i][k] + c * prime) % prime;
                }
            }
        }
    }

    public static void gauss(int[][] A) {
        gauss(A, Math.min(A.length, A[0].length));
    }
    public static long gcd(long a, long b){
        if(a < b){
            long temp = a;
            a = b;
            b = temp;
        }
        if(b == 0) return a;
        return gcd(b, a % b);
     }
    public static Pair ext_euclid(long a, long b){
        if(a < b){
            Pair r = ext_euclid(b,a);
            return new Pair(r.second, r.first);
        }
        if(b == 0) return new Pair(1, 0);
        long q = a / b;
        long rem = a - b * q;
        Pair r = ext_euclid(b, rem);
        Pair ret = new Pair(r.second, r.first - q * r.second);
        return ret;
    }

    public static  long inverse(long num, long modulo){
        num = num%modulo;
        Pair p = ext_euclid(num, modulo);
        long ret = p.first;
        if(ret < 0) return (modulo + ret) % modulo;
        return ret % modulo;
    }

    static class Pair{
        public long first;
        public long second;
        public Pair(long frst, long scnd){
            first = frst;
            second = scnd;
        }
    }

Это работает на небольших примерах (мод 29):

matrix = {{1.0, 1.0, 1.0, 1.0}, {1.0, 2.0, 1.0, 2.0},{1.0, 0.0, 0.0‚ 3.0}};
answer= {{1.0, 0.0, 0.0, 0.0},{0.0, 1.0, 0.0, 1.0}, {0.0, 0.0, 1.0, 0.0}};

Что правильно (первая переменная = 0, вторая = 1.0, третья = 0), что можно проверить с помощью WolframAlpha для 0 * k ^ 0 + 1 * k ^ 1 + 0 * k ^ 2 для k = 1..3.

В этом примере с 10 переменными и уравнениями a * k ^ 0 + b * k ^ 1 + c * k ^ 2 ... (mod 29) для k = 1..11 у меня есть эта матрица:

1 1 1 1 1 1 1 1 1 1 1 8 
1 2 4 8 16 3 6 12 24 19 9 5 
1 3 9 27 23 11 4 12 7 21 5 12 
1 4 16 6 24 9 7 28 25 13 23 12 
1 5 25 9 16 22 23 28 24 4 20 15 
1 6 7 13 20 4 24 28 23 22 16 0 
1 7 20 24 23 16 25 1 7 20 24 5 
1 8 6 19 7 27 13 17 20 15 4 1 
1 9 23 4 7 5 16 28 20 6 7 18 
1 10 13 14 24 8 22 17 25 18 7 20 
1 11 5 26 25 14 9 12 16 7 7 8  

Используя свой алгоритм, я получаю ответ:

1 0 0 0 0 0 0 0 0 0 0 18 
0 1 0 0 0 0 0 0 0 0 0 8 
0 0 1 0 0 0 0 0 0 0 0 15 
0 0 0 1 0 0 0 0 0 0 0 11 
0 0 0 0 1 0 0 0 0 0 0 28 
0 0 0 0 0 1 0 0 0 0 0 27 
0 0 0 0 0 0 1 0 0 0 0 7 
0 0 0 0 0 0 0 1 0 0 0 21 
0 0 0 0 0 0 0 0 1 0 0 9 
0 0 0 0 0 0 0 0 0 1 0 24 
0 0 0 0 0 0 0 0 0 0 1 14  

Но это неправильно! (можно проверить с помощью WolframAlpha). Правильный ответ должен быть (a b c ...) = (8 13 9 13 4 27 18 10 12 24 15).

Кто-нибудь может заметить мою ошибку? Или я неправильно понял, как сделать Gauss mod p?


person Jambaman    schedule 17.11.2013    source источник


Ответы (1)


Кажется, на первом этапе вы не найдете строки с ненулевым значением на i-й позиции. Учитывая это, а также тот факт, что я не знаю, как работает ваша inverse функция, вы можете столкнуться с проблемой или не столкнуться с ней.

Я не понимаю, зачем вы ищете «развороты» на втором этапе. Вы знаете, где они находятся с первого этапа. На самом деле, я вообще не понимаю, зачем у вас второй этап. Вместо этого выполните все исключения на первом этапе. Это значительно прояснит ваш код.

Я не понимаю, почему вы используете doubles в своей матрице. Я также не понимаю, почему вы используете Math.abs повсюду; здесь вполне уместны сравнения на равенство.

Наконец, вы решаете систему Вандермонда. Если это ваше приложение, а не просто тестовый пример, вам, вероятно, следует вместо этого использовать интерполяцию Лагранжа.

person tmyklebu    schedule 17.11.2013
comment
Итак, я обновил свой код в соответствии с вашими рекомендациями, а также включил обратную функцию. Я также изменил матрицы на целые элементы. Ответ все тот же! Проверка с помощью WA показывает мне, что полученный мной ответ (18,8,15,11,28,27,7,21,9,24,14) верен для всех строк, кроме последней 3. Что это может означать? - person Jambaman; 18.11.2013
comment
Как бы то ни было, ваш обновленный код дает правильный ответ. Я предполагаю, что ваш старый код в какой-то момент наткнулся на 0. - person tmyklebu; 18.11.2013