0. mrsmrv 63 13.05.20 23:49 Сейчас в теме

Кодирование по алфавиту. Большие целые числа

А вы знали, что 1С умеет в очень длинные числа?
Кодирование информации в строку по указанному алфавиту, возможен любой алфавит и не только.
В тексте приведён алгоритм для кодирования последовательности байт в любой и из любого односимвольного алфавита.

Перейти к публикации

Комментарии
Избранное Подписка Сортировка: Древо развёрнутое
Свернуть все
1. mrsmrv 63 14.05.20 21:38 Сейчас в теме
Разгон обработки. Чуть чуть удалось увеличить быстродействие, на тестовых данных объёмом 8 килобайт.
за счёт преобразования алфавита в массив символов. При Кодировании удаётся добиться большего увеличения быстродействия, чем при Раскодировании. При кодировании берётся Сред по номеру символа в строке алфавита, а если брать элемент массива, то это примерно в 4 раза быстрее. А при Раскодировании делается поиск в строке символа, и скорость поиска примерно такая же как и в массиве. Стоит ли делать индексированную ТЗ? проверю, позже.
Время затраченное на создание массива для 58ми символов настолько малО, что единичные вызовы функций происходят дольше. Так что даже на малом объёме данных не имеет смысла Отключать формирования массива.
А сейчас текст функций после оптимизации:

Функция ПолучитьМассивАлфавита(пАлфавит)
	лМассив = Новый Массив;
	Для н=1 По СтрДлина(пАлфавит) Цикл
		лМассив.Добавить(Сред(пАлфавит,н,1));
	КонецЦикла;
	Возврат лМассив;
КонецФункции

Функция Кодировать(пДвДанные, пАлфавит) Экспорт
	лБольшоеЧисло = 0;
	лБуферДвДанных = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(пДвДанные);
	лЧислоБайт = лБуферДвДанных.Размер;
	пМассивАлфавита = ПолучитьМассивАлфавита(пАлфавит);
	лРазмерАлфавита = пМассивАлфавита.Количество(); // Алфавит односимвольный
	
	// Получаем большое число
	лСтрока1 = "";

	лНулевойСимвол = пМассивАлфавита[0]; // определяем количество лидирующих "нулей"
	Для Каждого лТекБайт Из лБуферДвДанных Цикл
		лБольшоеЧисло = лБольшоеЧисло*256 + лТекБайт;
		Если лБольшоеЧисло=0 И лТекБайт=0 Тогда
			лСтрока1 = лСтрока1 + лНулевойСимвол; // пишем ноль вначале, если лидирующие байты - нулевые
		КонецЕсли;
	КонецЦикла;
	
	лСтрока2 = "";
	// Разделяем на символы
	лОстаток = лБольшоеЧисло%лРазмерАлфавита;
	лБольшоеЧисло = Цел(лБольшоеЧисло/лРазмерАлфавита);
	Пока лОстаток <> 0 или лБольшоеЧисло > 0 Цикл
		лСтрока2 = пМассивАлфавита[лОстаток] + лСтрока2;
		лОстаток = лБольшоеЧисло%лРазмерАлфавита;
		лБольшоеЧисло = Цел(лБольшоеЧисло/лРазмерАлфавита);
	КонецЦикла;
	Возврат лСтрока1+лСтрока2;
КонецФункции

Функция Раскодировать(пСтрока, пАлфавит) Экспорт
	лБольшоеЧисло = 0;
	пМассивАлфавита = ПолучитьМассивАлфавита(пАлфавит);
	лРазмерАлфавита = пМассивАлфавита.Количество(); // Алфавит односимвольный
	лДлинаСтроки = СтрДлина(пСтрока);
	
	лНулевойСимвол = пМассивАлфавита[0]; // определяем количество лидирующих "нулей"
	лКолвоНулевых = 0;
	Пока Сред(пСтрока,лКолвоНулевых+1,1) = лНулевойСимвол Цикл
		лКолвоНулевых = лКолвоНулевых + 1;
	КонецЦикла;
	
	// Лидирующие нули добавляют значащие символы в любой системе счисления 1 к 1, а не как a^x = b
	лДлинаБуфера = Окр((лДлинаСтроки-лКолвоНулевых)/ЛогарифмПоОснованию(лРазмерАлфавита,256))+лКолвоНулевых+1; // Создаём буфер с учётом лидирующих "нулей" и небольшим запасом
	лБуферДвДанных = Новый БуферДвоичныхДанных(лДлинаБуфера);
	
	// Получаем большое число
	лСтрока = "";
	нн=0;
	Для н=1 по лДлинаСтроки Цикл
		лТекЧисло = пМассивАлфавита.Найти(Сред(пСтрока,н,1));
		лБольшоеЧисло = лБольшоеЧисло*лРазмерАлфавита + лТекЧисло;
		Если лБольшоеЧисло=0 И лТекЧисло=0 Тогда
			нн=н;
			// лБуферДвДанных.Установить(нн,0); // пишем нулевые байты вначале, если лидирующие символы - нулевые
			// писать нулевые байты смысла нет, они и так там нулевые. просто запоминаем сколько нулевых вначале и всё.
		КонецЕсли;
	КонецЦикла;
	
	// Разделяем на байты
	н = 0;
	лОстаток = лБольшоеЧисло%256;
	лБольшоеЧисло = Цел(лБольшоеЧисло/256);
	Пока лОстаток <> 0 или лБольшоеЧисло > 1 Цикл
		лБуферДвДанных.Установить(лДлинаБуфера-н-1,лОстаток);
		лОстаток = лБольшоеЧисло%256;
		лБольшоеЧисло = Цел(лБольшоеЧисло/256);
		н = н + 1;
	КонецЦикла;
	
	лРеальнаяДлинаБуфера = нн+н;
	
	лДвДанные = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(лБуферДвДанных.Прочитать(лДлинаБуфера-лРеальнаяДлинаБуфера,лРеальнаяДлинаБуфера));
	
	Возврат лДвДанные;
КонецФункции

Показать
Прикрепленные файлы:
2. mrsmrv 63 14.05.20 22:00 Сейчас в теме
Применение таблицы значений с индексированной колонкой замедлило.


Функция ПолучитьТЗАлфавита(пАлфавит)
	лТЗ = Новый ТаблицаЗначений;
	лОписаниеТиповСтрока1 = Новый ОписаниеТипов("Строка",,Новый КвалификаторыСтроки(1));
	лОписаниеТиповНомер3 = Новый ОписаниеТипов("Число",Новый КвалификаторыЧисла(3,0,ДопустимыйЗнак.Неотрицательный));
	лТЗ.Колонки.Добавить("Символ",лОписаниеТиповСтрока1);
	лТЗ.Колонки.Добавить("Число",лОписаниеТиповНомер3);
	лТЗ.Индексы.Добавить("Символ");
	Для н=1 По СтрДлина(пАлфавит) Цикл
		л_НовСтр = лТЗ.Добавить();
		л_НовСтр.Символ = Сред(пАлфавит,н,1);
		л_НовСтр.Число = н-1;
	КонецЦикла;
	Возврат лТЗ;
КонецФункции

Показать


место, где было общаение к ТЗ:
		лТекЧисло = лТЗАлфавита.Найти(Сред(пСтрока,н,1),"Символ").Число;


разница даже больше чем при использовании массива, только в обратную сторону, см. Скриншот
Прикрепленные файлы:
3. mrsmrv 63 15.05.20 21:44 Сейчас в теме
(2) Применение соответствия быстрее чем массив.

Функция ПолучитьСоответствиеАлфавита(пАлфавит)
	лСоответствие = Новый Соответствие;
	Для н=1 По СтрДлина(пАлфавит) Цикл
		лСоответствие.Вставить(Сред(пАлфавит,н,1),н-1);
	КонецЦикла;
	Возврат лСоответствие;
КонецФункции

Функция Раскодировать(пСтрока, пАлфавит) Экспорт
	лБольшоеЧисло = 0;
	лСоответствие = ПолучитьСоответствиеАлфавита(пАлфавит);
	лРазмерАлфавита = лСоответствие.Количество(); // Алфавит односимвольный
	лДлинаСтроки = СтрДлина(пСтрока);
	
	лНулевойСимвол = Сред(пАлфавит,1,1); // определяем количество лидирующих "нулей"
	лКолвоНулевых = 0;
	Пока Сред(пСтрока,лКолвоНулевых+1,1) = лНулевойСимвол Цикл
		лКолвоНулевых = лКолвоНулевых + 1;
	КонецЦикла;
	
	// Лидирующие нули добавляют значащие символы в любой системе счисления 1 к 1, а не как a^x = b
	лДлинаБуфера = Окр((лДлинаСтроки-лКолвоНулевых)/ЛогарифмПоОснованию(лРазмерАлфавита,256))+лКолвоНулевых+1; // Создаём буфер с учётом лидирующих "нулей" и небольшим запасом
	лБуферДвДанных = Новый БуферДвоичныхДанных(лДлинаБуфера);
	
	// Получаем большое число
	лСтрока = "";
	нн=0;
	Для н=1 по лДлинаСтроки Цикл
		лТекЧисло = лСоответствие[Сред(пСтрока,н,1)];
		лБольшоеЧисло = лБольшоеЧисло*лРазмерАлфавита + лТекЧисло;
		Если лБольшоеЧисло=0 И лТекЧисло=0 Тогда
			нн=н;
			// лБуферДвДанных.Установить(нн,0); // пишем нулевые байты вначале, если лидирующие символы - нулевые
			// писать нулевые байты смысла нет, они и так там нулевые. просто запоминаем сколько нулевых вначале и всё.
		КонецЕсли;
	КонецЦикла;
	
	// Разделяем на байты
	н = 0;
	лОстаток = лБольшоеЧисло%256;
	лБольшоеЧисло = Цел(лБольшоеЧисло/256);
	Пока лОстаток <> 0 или лБольшоеЧисло > 1 Цикл
		лБуферДвДанных.Установить(лДлинаБуфера-н-1,лОстаток);
		лОстаток = лБольшоеЧисло%256;
		лБольшоеЧисло = Цел(лБольшоеЧисло/256);
		н = н + 1;
	КонецЦикла;
	
	лРеальнаяДлинаБуфера = нн+н;
	
	лДвДанные = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(лБуферДвДанных.Прочитать(лДлинаБуфера-лРеальнаяДлинаБуфера,лРеальнаяДлинаБуфера));
	
	Возврат лДвДанные;
КонецФункции
Показать


5-ти кратный запуск 4-х килобайтной входной строки с массивом 0,110983, с соответствием 0,104367
4. vsesam80 20.05.20 00:02 Сейчас в теме
А где это можно применить?
5. mrsmrv 63 20.05.20 09:01 Сейчас в теме
(4) ну вот например разработка https://infostart.ru/public/1153292/ использовалась в разработке https://infostart.ru/public/1179411/, а вообще, при необходимости передать некоторые двоичные данные в виде текста, с настраиваемыми требованиями к составу символом. Например при передаче двоичных данных в тексте сообщений. Применяя расширенный набор символов существенно сокращается длина строки. А например base58 применяется для кодирования с использованием только удобочитаемых символов, перепутать которые с аналогичными по начертанию проблематично. Таким способом записываются адреса в популярных блокчейнах.
Оставьте свое сообщение
Новые вопросы с вознаграждением
Автор темы объявил вознаграждение за найденный ответ, его получит тот, кто первый поможет автору.

Вакансии

Программист 1С
Екатеринбург
зарплата от 80 000 руб. до 130 000 руб.
Полный день

Автор новостных обзоров на тему 1С и бухучета
Санкт-Петербург
По совместительству

Программист 1С
Санкт-Петербург
зарплата до 150 000 руб.
Полный день

Ведущий программист 1С
Москва
зарплата от 150 000 руб.
Полный день

Ведущий программист 1С (УТ 11)
Москва
зарплата до 200 000 руб.
Полный день