• XSS.stack #1 – первый литературный журнал от юзеров форума

Арифметика чисел в 256-ричной системе исчесления

firon

RAID-массив
Пользователь
Регистрация
17.12.2022
Сообщения
78
Реакции
4
Встала задача работы с большими неотрицательными целыми числами, намного больше чем любая из стандартных структур может в себе содержать.
Для этого в качестве основы была выбрана 256-ричная система исчесления, т.к. в ней числа можно хранить как массив байт byte[] number {...}. Наименьший разряд - слева, найбольший - справа. То есть числа будут выглядеть так:
Код:
x = sum from i to N-1 {number[i]*B^i}
N = number.Length
где B - это основание системы (для двоичной B=2, для десятичной B=10, для шестнадцатеричной B=16 и т.д). В нашем случае B=256 (B=2^8). byte (или uint8) принимет значения от 0 до 255.

Мне нужна функция, которая будет возвращать результат операции a mod b деления с остатком, или хотя бы просто деление a div b ([a / b]). Или же алгоритм быстрого нахожднеия a*b mod c. Подойдет любое из этих трех (желательно 1-е или 3-е).

Вот некоторые мои наработки, которые могут натолкнуть кого-то на идею. Эти функции возвращают неправильный результат, как их исправить?

(Для простоты можно считать, что размеры входных массивов одинаковы)
Код:
    public static byte[] _Modulus(byte[] dividend, byte[] divisor) {
        int len = dividend.length;
        byte[] remainder = new byte[len];
  
        Memory.copy(remainder, dividend, len);
  
        for (int i = 0; i < len; i++) {
            int carry = 0;
            for (int j = i; j < len; j++) {
                int temp = (remainder[j] & 0xff) + (carry << 8);
                if (divisor[i] != 0) {
                    carry = temp % (divisor[i] & 0xff);
                } else {
                    carry = temp;
                }
                //remainder[j] = (byte)carry;
                //stdout.printf(@"i: $(i) j: $(j)\t carry: $(carry)\n");
            }
            remainder[i] = (byte)(carry & 0xff);
        }
  
        return remainder;
    }
    public static byte[] Modulus(byte[] dividend, byte[] divisor) {
        int len = dividend.length;
        byte[] remainder = new byte[len];
  
        Memory.copy(remainder, dividend, len);
  
        for (int i = 0; i < len; i++) {
            int carry = 0;
            for (int j = i; j < len; j++) {
                int temp = (remainder[j] & 0xff) + (carry << 8);
                if (divisor[i] != 0) {
                    carry = temp / (divisor[i] & 0xff);
                    remainder[j] = (byte)(temp % (divisor[i] & 0xff));
                } else {
                    carry = temp;
                }
            }
        }
  
        return remainder;
    }

Алгоритм быстрого взятия (mod c) от произведения a*b
Код:
    /*
    1.[Начальная установка]
        s = 0;
    2.[Преобразовать все D байтов]
        for (D-1 >= j >= 0){
            s = 256s;
            if (s >= N) {s = s - N};
            s = s + x_j*y;
            if (s >= N) {s = s - N};
        }
        return s;
    */
    public static byte[] _MulMod(byte[] x, byte[] y, byte[] N) {
        int D = N.length;
        byte[] s = new byte[D];
        byte[] byte256 = new byte[D];
        byte256[1] = 1;
        for (int j = D - 1; j >= 0; j--) {
            s = multiply(s, byte256);
            if (compare(s, N) >= 0) {
                s = substract(s, N);
            }
            byte[] x_j = new byte[D];
            x_j[0] = x[j];
            //byte[] x_j = (byte[]) x[j];
            s = add(multiply(y, x_j), s);
            if (compare(s, N) >= 0) {
                s = substract(s, N);
            }
            s = s[0:D];
        }
        return s;
    }
Код:
    //https://en.wikipedia.org/wiki/Long_division#Division
    //https://en.wikipedia.org/wiki/Division_algorithm#Long_division
    public static byte[] Divide(byte[] dividend, byte[] divisor) {
        int len = dividend.length;
        byte[] quotient = new byte[len];
        byte[] remainder = new byte[len];
  
        Memory.copy(remainder, dividend, len);
  
        for (int i = 0; i < len; i++) {
            int carry = 0;
            for (int j = i; j < len; j++) {
                int temp = (remainder[j] & 0xff) + (carry << 8);
                if (divisor[i] != 0) {
                    quotient[j] = (byte)(temp / (divisor[i] & 0xff));
                    carry = temp % (divisor[i] & 0xff);
                } else {
                    quotient[j] = (byte)carry;
                    carry = temp;
                }
                remainder[j] = (byte)carry;
            }
        }
  
        return quotient;
    }

Готовые библиотеки не предлагать - в моем случае их нельзя использовать!
Код:
    public static byte[] mod_int(uint64 x, uint64 y){
        uint64 modulus = x, divisor = y;
        while (modulus >= y){
            while (divisor > modulus){
                divisor >>= 1;
            }
            modulus = modulus - divisor;
        }
        return (byte[]) modulus;
    }

Вот некоторые примеры чисел:
13120_10 = {64 51 0 0 0 0 0 0}_256
31214211316_10 = {244 16 131 68 7 0 0 0}_256
418302432341123382_10 = {54 225 95 144 216 27 206 5}_256

Дополнительные источники: https://ru.wikipedia.org/wiki/Система_счисления
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
В четвертых дотнетах вроде был BigInteger в System.Numerics, не? И опять же, если ты пытаешься реализовать RSA (судя по mod и div) или ECC руками таким образом, то я бы рекомендовал не выкручивать себе яица и взять либо встроенную System.Security.Cryptography, либо какой-нибудь Bouncy Castle.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Да я может бы и был рад взять готвый BigInteger, да только в GLib его нет, а таскать GMP я не могу
О хоспаде, это Vala штоль? Я подумал, что это С#, слишком синтаксис похож. Я бы предположил, что раз это Вала, то это Линукс с Гномом, а на серверном или декстопном Линуксе 99.9% будет предустановлен OpenSSL или MbedTLS, ищи в рантайме эти библиотеки, делай dlopen и dlsym, если не хочешь с собой таскать GMP. Хотя это имхо бред, если ты перепишешь GMP, то получишь ровно столько кода, сколько было в GMP, и этим ты проблемы размера не решишь. Можешь еще посмотреть какой-то wolf-crypto или axTLS, они по идее меньше по объемам, чем OpenSSL или MbedTLS.
 
C# просто для удобства написал; тут главное сам алгоритм понять, я на другой язык перевести не проблема. Это ж по-сути задача деления двух полиномов, для который есть решение: https://en.wikipedia.org/wiki/Synthetic_division#Python_implementation. Просто как с переполнением быть-то? Да и коэфициенты должны всегда целыми получаться в ответе.
серверном или декстопном Линуксе
На линуксе может и есть, а на Windows что? На Windows тоже Glib исользую.
если ты перепишешь GMP, то получишь ровно столько кода, сколько было в GMP, и этим ты проблемы размера не решишь
А мне и не надо все переписывать. Мне достаточно 4 арифметические операции и побитовый сдвиг. Все кроме деления уже готово и там кода совсем чуть-чуть. Тут просто mod нужно искать делением, а не вычитинием. Циклическое вычитание слишком долгое при большом уменьшаемом и маленьким вычитаемым. Может как-то можно обойтись чисто комбинацией битовых операций '>>', '<<', '&', ну может ещё '|'?
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх