LINUX.ORG.RU

Работа с большими числами

 


0

1

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

#define BYTE_CHAR_SIZE   (sizeof(unsigned char))  // размер char
#define BYTE_NUMBER_MAX_SIZE  (4096/BYTE_CHAR_SIZE)  // максимальное количество байт для представления числа
struct big_number
{
  unsigned short byte_count;  // количество занятых байт, для представления числа
  unsigned char big_number[BYTE_NUMBER_MAX_SIZE]; // память для хранения числа
};

big_number - представляю в виде char а не int , т.к. вчера пытался разобраться в принципах работы с такими числами. И в одной библиотеке прочёл, что используется char для избежания проблем с умножением и делением больших чисел.(к сожалению, не могу найти ни ту библиотеку, ни описание..)
Давайте разберём инкримент числа.
Как полагаю, мне нужно будет в цикле пройти byte_count байт числа &big_number
При этом делать так
unsigned shor sign_flag = 0;  // флаг, указывающий было ли переполнение
char *current_byte_ptr = &big_number; // текущий проверяемый байт
unsigned short tmp = 0; // хранение временного значения



/* Само действие инкримента */
tmp = *current_byte_ptr + 1; // сохранили в временную переменную, чтоб узнать было ли переполнение

/* Определяем, случилось ли переполнение */
if(tmp > BYTE_CHAR_SIZE) sign_flag = 1;  // переполнение произошло

/* Если произошло переполнение */
if(sign_flag == 1)
{
  /* Обработка по аналогии следующего байта и так, пока byte_count не станет равен 0 */
}

return;  // Окончание действий инкремента
Будет ли такой способ работать?
(возможно я просто логику действия не понимаю)
Далее интересует будет ли такой код (обработки *char) работать корректно на системах с разным порядком байт.


Как реализовывается сложение чисел? скажем мне нужно к какому-то числу, представленному struct big_number прибавить другое число struct big_number , к примеру 888888888889999999999999 , какой алгоритм действия должен быть?
Аналогичный вопрос относительно умножения и деления.
Заранее спасибо
(Библиотеки смотрел, не получилось разобраться, потому и задаю вопрос тут)

Deleted
Ответ на: комментарий от emulek

А реализацию?

А реализацию сделай сам. Это тебе задание на дом.

i-rinat ★★★★★
()
Ответ на: комментарий от emulek

в криптографии, например.

В океанологии (микробиологии, астрономии ), например

Собссно это то, где я их пользовал.

anonymous
()
Ответ на: комментарий от anonymous

в криптографии, например.

читай тред, я про произвольную разрядность, а не про фиксированную, например 2048 бит. Кстати в криптографии, длинные числа как раз для того и применяются, что-бы их было _сложнее_ _считать_. Числа в 32 бита НЕ применяются потому, что факторизировать их может любой дурак на любой кофемолке.

В океанологии (микробиологии, астрономии ), например

там double не катит, да? Почему?

emulek
()
Ответ на: комментарий от emulek

я про произвольную разрядность, а не про фиксированную, например 2048 бит

Кстати в криптографии, длинные числа как раз для того и применяются, что-бы их было _сложнее_ _считать_. Числа в 32 бита НЕ применяются потому, что факторизировать их может любой дурак на любой кофемолке.

перечитал 2 раза, них не понял. так их там, по-твоему, всё ж, применяют, или нет?

там double не катит, да? Почему?

Не катит. Не всегда не катит, но иногда нужна просто астрономическая точность. Это сложно объяснить, если не видел те модели, которые эти бородатые дядьки строят.

anonymous
()

А зачем их представлять чарами? Лучше массив int64_t — производительней будет.

Reset ★★★★★
()
Ответ на: комментарий от anonymous

перечитал 2 раза, них не понял. так их там, по-твоему, всё ж, применяют, или нет?

там _другие_ числа.

Не катит. Не всегда не катит, но иногда нужна просто астрономическая точность. Это сложно объяснить, если не видел те модели, которые эти бородатые дядьки строят.

1. для этого есть стандартные библиотеки

2. в большинстве случаев, это расстрел комаров из BFG10K. Есть другие способы минимизировать погрешность, не только топорное наращивание числа битов. Эти твои «дядьки» конечно про это не знают, да и не должны знать в общем-то. А вот ты — должен, если пилишь реализацию модели.

emulek
()
Ответ на: комментарий от Reset

А зачем их представлять чарами? Лучше массив int64_t — производительней будет.

1. тред не читай, сразу отвечай.

2. uint64_t

emulek
()
Ответ на: комментарий от emulek

1. для этого есть стандартные библиотеки

А ты думал я их там вручную чтоль писал?

Однако наличие gmplib не отменяет нужности понимать, как они работают.

anonymous
()
Ответ на: комментарий от anonymous

Однако наличие gmplib не отменяет нужности понимать, как они работают.

ладно, убедил. Если в ТЗ сказано «точность Over9000 знаков», то проще подключить GMP.

А работают они меееедлеееенннноооо.

emulek
()
Ответ на: комментарий от emulek

читай тред, я про произвольную разрядность, а не про фиксированную, например 2048 бит. Кстати в криптографии, длинные числа как раз для того и применяются, что-бы их было _сложнее_ _считать_. Числа в 32 бита НЕ применяются потому, что факторизировать их может любой дурак на любой кофемолке.

Вообще-то никто специально разрядность ключей и прочих больших циферок не ограничивает, просто используют какие-то общепринятые значения, с учётом производительности современных компьютеров с одной стороны и достижений криптоанализа с другой

Тебе никто не запрещает в своей программе ключи по 16384 бита и больше использовать

Harald ★★★★★
()
Ответ на: комментарий от Harald

Вообще-то никто специально разрядность ключей и прочих больших циферок не ограничивает

ограничивает. См. первый пост, у него размер задан как переменная, а не как константа.

emulek
()
Ответ на: комментарий от emulek

ограничивает. См. первый пост, у него размер задан как переменная, а не как константа.

Возьмём к примеру OpenSSL, у него внутри своя библиотека для больших чисел, называется BIGNUM, там размер числа задаётся переменной

Harald ★★★★★
()
Ответ на: комментарий от Harald

Возьмём к примеру OpenSSL, у него внутри своя библиотека для больших чисел, называется BIGNUM, там размер числа задаётся переменной

ты уверен, что там нет своих специальных функций для обычных размеров ключа, а BIGNUM нужно только если ты какой-то нестандартный размер задашь, типа 32768 бит?

emulek
()

Так называемые переполнения можно обрабатывать более понятным способом. Кроме того, основание системы счисления 10 это довольно маленькое основание, можно взять и больше.

Более понятный способ обработхки переносов: расчёты производишь (на каждом этапе) сначала без учёта переноса вообще, потом нормализуешь число. Предположим, что ты хранишь цифры в словах длины 2n бит.

Тогда, чтоб произведение двух цифр влезло в такое слово, каждая из них должна быть не более n бит в длину. Их произведение будет длины 2n-1 бит.

Но в случае умножения, мы должны убедиться, что не просто произведение влезает в слово, а произведение плюс перенос (для понятной работы процесса нормализации). Оценим сверху с избытком: перенос длины n бит, произведение длины 2n-1 бит, их сумма 2n бит. Влезает!

Это значит, что располагая словами длины 32 бита под цифры ты можешь выбрать в качестве основания системы счисления p=2^16. И обрабатывать переносы в отдельном цикле - это сделает общий код намного яснее.

Далее - перемножение столбиком реализую все равно, мой тебе совет. Дело в том, что до определённой длины числа это самый быстрый способ умножения. Ни Карацуба, ни БПФ не будут быстрее для коротких чисел, даже если одно из чисел короткое. (А короткие числа в твоей истории будут, даже если оригинальные числа длинные).

Далее, знак числа. Используй или избыточные системы счисления, например -2, или используй дополнительный код. Не храни знак отдельно. Не надо.

Деление сделай обязательно. Википедию, Кнута почитай. Это не сложно. Отдельно заморочся с умножением, делением, сложением, вычитанием с одной цифрой. Умножать и делить на 2 например это частая операция.

Что ещё? Ну, сразу временно если где создаешь свое число, позаботится нужно о том, чтоб сразу как только оно не нужно, чтоб хламом затиралось.

Память под числа выделяй динамически - на этом не сэкономишь время. Длина числа должна быть его непременным атрибутом. Числа длиной в одну цифру оптимально представляй. Заранее сообрази, какой длины будут результаты, исходя из длины операндов.

Систему счисления используй с основанием равным степени двойки. В биты, если понадобится, можно будет быстро посмотреть.

От ведущих нулей сразу избавляйся, уменьшай длину числа. Избавляясь от них, сразу затирай хламом.

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

Дальше - больше. Если алгоритм работы с числами сначала будет конструироваться (как например PreparedStatement в жабе для jdbc), и только потом выполняться, то его (алгоритм) можно проанализировать на тему - в какой момент времени какие числа становятся не нужны, и не париться об освобождении памяти. Но это уже на будущее тебе тема.

anonymous
()

Насчёт целочисленного деления тебя здесь сильно зря пугали - методом ньютона, а он сходится правильно, найди обратное к делителю и умножь на делимое. Если умножение на 2 и на основание системы счисления ты правильно сделаешь, то это один из самых верных методов получится и один из самых простых для реализации.

anonymous
()

А если ты представишь алгоритмы, использующие большие числа (не те, которые реализуют элементарные операции, а те которые их используют в итоге), в виде цепи (сумматоры там, умножители и тд) то появится дополнительный профит - скедулер её работы сможешь настроить как хочешь - для минимизации используемой памяти, максимального распараллеливания, и тд. Вот это может быть уже темой курсовой работы.

anonymous
()
Ответ на: комментарий от emulek

ок, это реализация.

Скажи, а ты вообще софт пишешь? Имеется в виду что-нибудь, с длительностью разработки больше пары-тройки месяцев. Я никак не могу понять, как можно писать софт, не задумываясь над тем, как его будут тестировать. А из твоих высказываний так и прёт «бац-бац и в продакшн».

i-rinat ★★★★★
()
Последнее исправление: i-rinat (всего исправлений: 1)
Ответ на: комментарий от emulek

То, что они произвольной длины не значит, что реально будут использоваться числа в миллион бит длиной. Конечно же, числа не будут превышать какого-то конкретного числа бит в длину, например 2^16 бит. Но большой профит от знания этого факта извлечь трудно. К примеру, наши числа будут 2^16 бит длиной, для хранения цифр мы используем слова длиной 2^6 бит, значит основание системы счисления, если мы хотим избежать дрочерства, должно быть 2^5 бит, значит длина числа должна быть 2^11 слов. Много раз в столбик не поделишь и не поумножаешь, во внутреннем стеке много таких не подержишь. А если мы используем нормальные алгоритмы, то и числа длиной 2^30 бит для нас не проблема.

anonymous
()
Ответ на: комментарий от i-rinat

А из твоих высказываний так и прёт «бац-бац и в продакшн».

а из твоих: «ну мы ещё тестировать будем, рефакторить, это же не продакшен…».

emulek
()
Ответ на: комментарий от anonymous

То, что они произвольной длины не значит, что реально будут использоваться числа в миллион бит длиной.

я без понятия, что там у ТСа планируется. Честно. Спроси у него.

лично мне вообще непонятно

#define BYTE_CHAR_SIZE   (sizeof(unsigned char))  // размер char
#define BYTE_NUMBER_MAX_SIZE  (4096/BYTE_CHAR_SIZE)  // максимальное количество байт для представления числа
struct big_number
{
  unsigned short byte_count;  // количество занятых байт, для представления числа
  unsigned char big_number[BYTE_NUMBER_MAX_SIZE]; // память для хранения числа
};
на кой ляд тут byte_count? Преждевременная оптимизация, что-бы хранить 4096 байт, а умножать 5 из них?

Я теряюсь в догадках.

emulek
()
Ответ на: комментарий от anonymous

Ладно, 2^30 это я загнул

не, ну почему? Будут числа в 128Мб размером, прикольно.

emulek
()
Ответ на: комментарий от emulek

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

anonymous
()
Ответ на: комментарий от emulek

а я здесь троллю

Неплохо получается, заработал репутацию местной достопримечательности.

i-rinat ★★★★★
()
Ответ на: комментарий от anonymous

Точнее тысячу раз по тысячу нулей😊

Казалось бы, разгадка проста - не умножать в столбик. Но во первых, надо это уметь делать, во вторых лишние тысячу нулей никогда не помешает проигнорировать. Оптимизацией это даже сложно назвать - просто не делаем ненужной работы. И в третьих, я подозреваю студентоту и его преподавателя, который непременно обратит на это внимание.

anonymous
()
Ответ на: комментарий от anonymous

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

это можно оптимизировать. ПОТОМ. ТСу для начала хоть какой-то прототип нужен, что-бы хоть как-то считал(но без ошибок).

emulek
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.