Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.67/9: Рейтинг темы: голосов - 9, средняя оценка - 4.67
130 / 25 / 12
Регистрация: 12.08.2015
Сообщений: 221
1

Скоростная реализация перевода символьной строки в численные массивы

11.10.2015, 16:42. Показов 1882. Ответов 26
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Здравствуйте! Недавно загорелся идеей создать свой собственный класс больших чисел (с десятичной дробью)
Для этого я буду использовать два целочисленных массива (целая и дробная часть числа), каждый элемент которых будет не более 1 байта размером, и одни флажок bool для "означивания" числа положительное\отрицательное.

Разумеется, чтобы вводить такое длинное число, я решил использовать строку, вводимую пользователем.
Внимание, вопрос:
Представим, что человек ввел 500 000 символов. Возможна ли проверка введенного числа на корректность, и его разбор + раскидывание по массивам менее, чем за 0,5 секунды?
Если да, то какими приемами тут лучше пользоваться? У меня есть немного оформившиеся планы, но они немного мутноватые.
Ах, да, забыл сказать, что лучше не ограничивать человека в вводимой строке, кроме как в совсем очевидных случаях (если две разделительные запятые, или два знака, или буквенный символ) например, функция должна понять эти строки одинаково и не сообщать об ошибке.
C++
1
2
3
4
5
6
""
"     "
"00000"
"0"
"0.0"
"+0.0"
И так далее. Разумеется, необходима хорошая скорость распознавания, а то иначе это никому не нужно будет. На что мне следует сейчас ориентироваться?
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
11.10.2015, 16:42
Ответы с готовыми решениями:

реализация (строки и массивы)
Собсно, можете подсказать реализацию на ассамблере вот таких задач: как найти в матрице,...

Существует ли метод/функция перевода значения символьной переменной в int
Хотел спросить, существует ли метод/функция перевода значения символьной ПЕРЕМЕННОЙ в int?

Написать программу удаления из текстового файла символов перевода строки ‘\n’ и перевода каретки ‘\r’
Здравствуйте,помогите написать программу на подобии этой,только не подсчета,а удаления.Заранее...

Строки. Удалить часть символьной строки,заключенной в скобки (вместе со скобками)
Удалить часть символьной строки,заключенной в скобки(вместе со скобками).

26
7787 / 6555 / 2983
Регистрация: 14.04.2014
Сообщений: 28,633
11.10.2015, 19:34 2
Ну это измерять надо. От компьютера зависит. Ты замеры делал? Может, полсекунды хватает.
0
130 / 25 / 12
Регистрация: 12.08.2015
Сообщений: 221
11.10.2015, 20:24  [ТС] 3
Пока мне удалось довести расшифровку до 3,2 секунды, при условии наличия 1 000 000 элементов в каждом массиве. (всего 2 миллиона на 2 массива).

Мне кажется, надо переделывать алгоритм, сделать его "хитрым" и умным, с "ленивым" заполнением элементов. То есть мы заполняем только необходимые цифры в массив. И если пользователь ввел 10 цифр до запятой, то и записать в массив надо 10. А не весь миллион переписывать. Просто я думаю, как это решить. Сейчас очень сложно определиться с ключевыми параметрами строки, которые необходимы для такого заполнения. Попробовать несколько вариантов. Если у меня все сработает, то чем более маленькое число вводит пользователь, тем быстрее строка расшифровывается.

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

Так можно компенсировать огромную тяжеловесность класса.

Добавлено через 7 минут
Вопрос в том, что м.б. у кого - то были подобные случаи, и кто-то может поделиться опытом организации кода для такого дела.
У меня мозг ломается.Там очень много нужно учесть.
Сначала моя функция занимала 298 строчек кода.
Потом я сократил до 245 строк кода.
Сейчас вот, оптимизировал, и теперь она занимает 250 строк кода. Да, появилось несколько новых, но зато я убрал много лишних повторяющихся вычислений в циклах, которые по сути, являются константными.
0
7787 / 6555 / 2983
Регистрация: 14.04.2014
Сообщений: 28,633
11.10.2015, 20:38 4
Цитата Сообщение от gledor Посмотреть сообщение
если пользователь ввел 10 цифр до запятой, то и записать в массив надо 10. А не весь миллион переписывать.
Разве это не очевидно?
0
130 / 25 / 12
Регистрация: 12.08.2015
Сообщений: 221
11.10.2015, 21:08  [ТС] 5
Цитата Сообщение от nmcf Посмотреть сообщение
Разве это не очевидно?
Весьма очевидно. но трудноосуществимо по причинам:

1. В связи со спецификой массивов, у меня идет связка в виде:

массив целочисленной части числа массив дробной части числа
0эл. 1эл. 2эл. 3эл. 4эл. 5эл. 6эл. 0эл. 1эл. 2эл. 3эл. 4эл. 5эл. .....

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

2.В отличие от общепринятого удаления из строки ненужных пробелов, я отказался от данной вещи как от вещи ненужной.
Так как я от этого отказался, у меня нет лишнего прогона строки, что положительно сказывается на скорости,но...
значительно тормозит разработку кода, трудно перестроить восприятие и воплотить алгоритм в коде.

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

4. Для реализации вот такого "ленивого" заполнения необходимо будет написать анализатор положения "ориентиров", по которым я буду одновременно заполнять оба массива, а так же возрастет сложность заполнения массивов и сложность алгоритма благодаря вводу в него таких понятий, как ориентиры заполнения.

Я уже 4 дня мучаюсь. Наконец, оформил то, что надо сделать. Но мне кажется, что это неподъемно... -_- быть может, я изобретаю велосипед в случае анализа строки, и есть какие-то сопутствующие функции в библиотеке C++. В общем, очень прошу помощи в организации, как этот объем провернуть?

+ я все это хочу воплотить в лице одной функции, единым куском кода. Потому что логически верно считать расшифровку строки одной функцией.
0
7787 / 6555 / 2983
Регистрация: 14.04.2014
Сообщений: 28,633
11.10.2015, 21:23 6
Лучший ответ Сообщение было отмечено gledor как решение

Решение

Ты целый трактат написал о простом вводе числа. Пробелов внутри числа не должно быть. За один проход ты не введёшь его. Надо сначала искать точку, если она есть, а от начала двигаться, чтобы нули пропустить и уже между этими позициями извлекать целую часть, двигаясь от точки.
1
130 / 25 / 12
Регистрация: 12.08.2015
Сообщений: 221
11.10.2015, 21:33  [ТС] 7
Цитата Сообщение от nmcf Посмотреть сообщение
Ты целый трактат написал о простом вводе числа. Пробелов внутри числа не должно быть. За один проход ты не введёшь его. Надо сначала искать точку, если она есть, а от начала двигаться, чтобы нули пропустить и уже между этими позициями извлекать целую часть, двигаясь от точки.
Хех)))) Ну, что поделать...
На счет пробелов - теоретически я хочу дать возможность человеку их вставлять неограниченно, дабы во время написания длинных чисел он мог группировать скажем, каждые три разряда при помощи пробелов. Но я не хочу жертвовать производительностью от лишнего прогона (если он делается в одном ядре с другими) . Поэтому хочу добавить алгоритм пропуска пробелов уже во время разбрасывания результатв по массивам
Так же, я хочу дать возможность человеку ставить перед перед числом знак +, - или не ставить знак вообще.

То есть, функция должна корректно считать в крайнем случае такое:

" - 000 00000 0 758 87 7 , 0045 67 8 00000 0"
И выдать на выходе такое:
"-758877,0045678"

Да, согласен! Минимум - два цикла выходит. Ну, может быть, еще поцдикл в одном из циклов.
То есть задачу можно уже разбить так:
Первый цикл - проверка синтаксиса и сбор сведений об ориентирах ввода
второй цикл - заполнение двух массивов одновременно, один - слева направо, другой - справа налево.
0
7787 / 6555 / 2983
Регистрация: 14.04.2014
Сообщений: 28,633
11.10.2015, 22:16 8
Что за массивы, обычные? Как именно хранятся цифры частей?
0
130 / 25 / 12
Регистрация: 12.08.2015
Сообщений: 221
11.10.2015, 22:42  [ТС] 9
Массивы обычные, не динамические. Потому что с динамическими будет очень много проблем... НО! Если руки дойдут - и если это будет себя оправдывать, то сделаю массивы в классе динамическими.
Цифры являются переменными типа signed char, просто при выводе на экран, я привожу элементы к их численному значению.
Каждый элемент это 1 десятичный разряд. Хранятся они по порядку. но заполнение массива целой части числа начинается с попы, где последний элемент массива - это самый первый разряд единиц. А заполнение дробной части - с начала, где 0 элемент - количество десятых частей.
0
2063 / 1542 / 168
Регистрация: 14.12.2014
Сообщений: 13,402
12.10.2015, 00:42 10
Лучший ответ Сообщение было отмечено gledor как решение

Решение

Цитата Сообщение от gledor Посмотреть сообщение
Каждый элемент это 1 десятичный разряд. Хранятся они по порядку. но заполнение массива целой части числа начинается с попы, где последний элемент массива - это самый первый разряд единиц. А заполнение дробной части - с начала, где 0 элемент - количество десятых частей.
Удали из строки пробелы,вычти из каждого элемента '0' вот тебе и твои массивы. Вернее массив можно иметь вообще один, только завести переменную которая показывает положение в нем запятой. Кстати их можно и в BCD конвертнуть, операции с которым интеловский проц выполнять обучен от рождения.
1
Модератор
Эксперт CЭксперт С++
5284 / 2371 / 342
Регистрация: 20.02.2013
Сообщений: 5,770
Записей в блоге: 20
12.10.2015, 06:53 11
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
завести переменную которая показывает положение в нем запятой
Указатель на элемент, с которого начинается дробная часть?
0
2063 / 1542 / 168
Регистрация: 14.12.2014
Сообщений: 13,402
12.10.2015, 07:40 12
Цитата Сообщение от gru74ik Посмотреть сообщение
Указатель на элемент, с которого начинается дробная часть?
типа того
0
130 / 25 / 12
Регистрация: 12.08.2015
Сообщений: 221
12.10.2015, 11:00  [ТС] 13
Спасибо всем за ответы! Задачу я ДОвыполнил сегодня утром.
В общем, пришлось переписывать с самого начала. В итоге у меня вышло много циклов, НО! даже при самой максимальной нагрузке, строка проходит не более, чем через 3 цикла, а минимально - через 2.

числа по - прежнему хранятся в signed char, но в виде целых чисел, а не символов. То есть, я отнимаю '0' от символа, введенного пользователем и присваиваю результат элементу signed char.

+ я смог дать возможность пользователю вводить в строку пробелы по своему усмотрению, группируя ввод разрядов как ему удобно, и избежать дополнительного цикла по избавлению от пробелов.

В итоге, я обеспечил как мне кажется, хорошую скорость выполнения расшифровки.
Проводил тест на вводе около 250 000 символов в массив, а потом выводе элементов массива в консоль.
На расшифровку и разбрасывание по массивам ушло 98 ms, а на вывод через cout - около 300 ms

В общем, план явно выполнен! Всем спасибо!
0
7787 / 6555 / 2983
Регистрация: 14.04.2014
Сообщений: 28,633
12.10.2015, 12:34 14
Целая часть должна храниться начиная с разряда единиц (индекс 0), иначе при сложении тебе придётся сдвигать всё это при переполнении. Тоже самое касается дробной части.
А для хранения лучше std::vector.
0
130 / 25 / 12
Регистрация: 12.08.2015
Сообщений: 221
12.10.2015, 13:26  [ТС] 15
Цитата Сообщение от nmcf Посмотреть сообщение
Целая часть должна храниться начиная с разряда единиц (индекс 0), иначе при сложении тебе придётся сдвигать всё это при переполнении. Тоже самое касается дробной части.
Дык если у меня разряд переполнен в результате сложения двух элементов одного и того же разряда (скажем, тысячного). Например,мы сложили 5+7, то получится 12. Мы по-любому вычтем десять, и прибавим к старшему разряду единицу,оставив 2 во младшем. То есть мы по любому перекинем всю эту информацию. Только в одном случае справа налево, а в другом - слева направо.

Как я это представляю:

C++
1
2
3
4
5
0 0 0 0 0 1 6 7 9        
         + + + + +  =  0 0 0 0  9 10 14 12 15 = 0 0 0 0 9 10 14 13  5 = 0 0 0 0 9 10 15 3 5 
0 0 0 0 9 9 8 5 6
 
= 0 0 0 0 9 11 5 3 5 = 0 0 0 0 10 1 5 3 5 = 0 0 0 1 0 1 5 3 5 = 101535
То есть в любом случае у переполненного разряда отнимаем десятку и прибавляем единицу к более старшему.
И тут вообще не важно, слева направо мы считаем или справа налево. Разумеется, ни о каком "сдвиге" в массиве речи не идет. Обработка переполненных разрядов - да, будет в обоих случаях. А полный сдвиг всех элементов массива при добавлении\переполнении - нет. не будет. Потому что это жесткий каркас. И вектор тут не нужен. Я хочу протестировать эту версию. Если на будет достаточно быстра - я её оставлю. Если нет - попробую с вектором по другой системе.
0
7787 / 6555 / 2983
Регистрация: 14.04.2014
Сообщений: 28,633
12.10.2015, 13:36 16
5+5=10, т. е. число становится двухразрядным и чтобы старший разряд вместить в нулевой индекс, придётся сдвинуть всё. А если старшие разряды в конце, то просто добавить.
0
130 / 25 / 12
Регистрация: 12.08.2015
Сообщений: 221
12.10.2015, 14:03  [ТС] 17
Цитата Сообщение от nmcf Посмотреть сообщение
5+5=10, т. е. число становится двухразрядным и чтобы старший разряд вместить в нулевой индекс, придётся сдвинуть всё. А если старшие разряды в конце, то просто добавить.
А, врубился.
Знаешь, я просто решил сделать всё-таки конечное число. Потому что по моему скромному мнению, людям вполне хватит и 100 разрядов до запятой, и 100 000 разрядов после запятой. Я же сделаю до миллиона. Много памяти это не сожрет, такая переменная будет занимать всего около 1,95МБ памяти (если считать не только массивы), что просто пустяк...
По-моему в прикладной математике даже "сумасшедшим ученым" не нужна бОльшая точность и громадность. Ибо поросту, и считать больше нечего.

А при помощи "ориентиров" я сделаю быстрые вычисления для коротких чисел.

Между прочим, надо не забывать, что запас числа очень большой. И вряд ли человек от руки вобьет больше 300 - 400 символов. Да даже если огромное число будет результатом какой-либо операции и просто будет передано строкой в функцию, это не очень удобно. Поэтому я не вижу особого смысла делать динамически добавляющиеся ячейки. Мб, я не прав... слово Вам.

Добавлено через 16 минут
И что пока еще говорит НЕ в пользу вектора и динамики, так это вопрос:

Допустим, я перегружу оператор *, и напишу умножение.
Я приму на входе четыре массива (два числа) и... тут оказывается, что все эти массивы абсолютно разной длины! Как мне умножать и координировать их относительно друг друга? Я же не знаю, сколько места потребуется зарезервировать для результата умножения, потому что сначала мне надо умножить. Нет, я, конечно, могу выделить места сразу по максимуму (количество ячеек самого большого числа * 2), а потом урезать мусорные лидирующие нули, но тогда смысл в этой динамичности, если она только мешает? Усложняет алгоритм, работает неэффективно, тратит больше времени. Лучше сразу задать какие-то рамки. А выход за рамки пресекать сообщением о нехватке места. Так может не очень гибко, но зато очень быстро!
0
7787 / 6555 / 2983
Регистрация: 14.04.2014
Сообщений: 28,633
12.10.2015, 15:00 18
Да не в ориентирах дело, а в оптимальности. Статический массив или нет, при твоём способе хранения потенциально понадобится сдвиг для операций типа += и -=, и придётся этот мегабайт сдвигать.
std::vector можно использовать с фиксированным размером, если ты об этом. Про разную длину массивов я не понял, как это мешает?
0
130 / 25 / 12
Регистрация: 12.08.2015
Сообщений: 221
12.10.2015, 15:36  [ТС] 19
Цитата Сообщение от nmcf Посмотреть сообщение
Да не в ориентирах дело, а в оптимальности. Статический массив или нет, при твоём способе хранения потенциально понадобится сдвиг для операций типа += и -=, и придётся этот мегабайт сдвигать.
Мб. мы немного разные вещи подразумеваем и думаем? ))))

согласен, потенциальный сдвиг чисел будет в том случае, если мы заполняем массив от "попы", где самый старший разряд постоянно является наибольшим элементом.

То есть, Вы скорее всего думаете, что я храню число 1024 таким способом:

0 0 0 4 2 0 1.

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

0 0 0 4 2 0 1
0 0 0 0 0 0 9
= 0 0 0 4 2 0 10,

То тут да, это плохое решение, необходим сдвиг влево при увеличении числа на один разряд.

может быть, Вы думаете, что я сохраняю число 1024, как

1 0 2 4 0 0 0 0,

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

Я же говорю о другом случае хранения. тоже сзади начинается, но не задом наперед, а по-нормальному, в моем случае

где мне не надо при увеличении числа его постоянно сдвигать.

я говорю о варианте

0 0 0 0 0 0 0 0 1 0 2 4

Это неплохой вариант, потому что при переполнении старшего разряда, я перекидываю дополнительную единицу в свободный, нуленый старший разряд и нет необходимости переписывать и сдвигать ВСЕ число. Когда у меня переполнится старший разряд (0 элемент), я просто выдам сообщение о переполнении и сделаю обнуление.
0
7787 / 6555 / 2983
Регистрация: 14.04.2014
Сообщений: 28,633
12.10.2015, 15:47 20
1. Впустую расходуется память. И не мало.
2. Усложняется индексация при работе с числом. Хранить, как я описал, проще и расширению это не мешает. Можно использовать фиксированный размер с запасом, а можно и динамический.
1
12.10.2015, 15:47
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
12.10.2015, 15:47
Помогаю со студенческими работами здесь

Вырезать два первых символа символьной строки и поместить их в конец строки
Помоги те пожалуйста, решите сколько сможете. Задачи на строки. 2. Вырежьте два первых символа...

Вырезать два первых символа символьной строки и поместить их в конец данной строки
Вырезать два первых символа символьной строки и поместить их в конец данной строки.

Какая СУБД самая скоростная?
Доброго времени суток. Занимаюсь разработкой одной очень важной автономной системы(большего сказать...

Моя реализация функции перевода string в int
#include <iostream> #include <string> using namespace std; int str_to_int(string a); int...

Моя реализация функции перевода STRING в DOUBLE
#include <iostream> #include <string> using namespace std; double str_to_double(string a); ...

реализация табличного перевода из 2-ной системы счисления в 8-ю
реализация табличного перевода из 2-ной системы счисления в 8-ю


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru