Категорически Вас приветствую. В свое время предлагал похожее задание на DevBattle. А недавно встретил задачу на Project Euler, где предметом исследования были эти самые триплетные числа.
Триплетным называется число, которое можно последовательно "схлопнуть", удаляя . три соседние одинаковые цифры. Пример такого числа 125577752211.
125577752211 -> 125552211 -> 122211 -> 111 -> "".
Вопрос в том, как быстро проверить исходное число на триплетность ?
Сразу скажу, что в практической деятельности это никогда не встретится. Тема для тех, кто занимается саморазвитием. Спасибо.
И да, это то что ждет 1С программиста после 55.
Триплетным называется число, которое можно последовательно "схлопнуть", удаляя . три соседние одинаковые цифры. Пример такого числа 125577752211.
125577752211 -> 125552211 -> 122211 -> 111 -> "".
Вопрос в том, как быстро проверить исходное число на триплетность ?
Сразу скажу, что в практической деятельности это никогда не встретится. Тема для тех, кто занимается саморазвитием. Спасибо.
И да, это то что ждет 1С программиста после 55.
Найденные решения
(55) Вообще применительно к 1С, разделение буферов ДД весьма быстрая штука и работает хорошо когда одновременно много триплетов в числе. По сему давайте навалим примитивной эвристики и возьмем лучшее из двух миров.
Функция lmn_БуфДДКомбо(Число)
ТриплетыРазделители = ПолучитьБуферДвоичныхДанныхИзСтроки("000 111 222 333 444 555 666 777 888 999", КодировкаТекста.ANSI).Разделить(ПолучитьБуферДвоичныхДанныхИзСтроки(" ", КодировкаТекста.ANSI));
БуфДД = ПолучитьБуферДвоичныхДанныхИзСтроки(Число, КодировкаТекста.ANSI);
ПредыдущийРазмер = БуфДД.Размер;
Пока Истина Цикл
БуфДД = СоединитьБуферыДвоичныхДанных(БуфДД.Разделить(ТриплетыРазделители));
Разница = ПредыдущийРазмер - БуфДД.Размер;
Если Разница < ПредыдущийРазмер / 10 ИЛИ БуфДД.Размер < 3 Тогда
Прервать
КонецЕсли;
ПредыдущийРазмер = БуфДД.Размер;
КонецЦикла;
Если БуфДД.Размер = 0 Тогда
Результат = Истина;
ИначеЕсли Разница = 0 ИЛИ БуфДД.Размер < 3 Тогда
Результат = Ложь;
Иначе
ИндексСтека = 2;
СтекЦифр = Новый БуферДвоичныхДанных(БуфДД.Размер + 2);
СтекЦифр.ЗаписатьЦелое16(0, 0);
Для Каждого Цифра из БуфДД Цикл
Если Цифра = СтекЦифр[ИндексСтека - 1] И Цифра = СтекЦифр[ИндексСтека - 2] Тогда
ИндексСтека = ИндексСтека - 2;
Иначе
СтекЦифр[ИндексСтека] = Цифра;
ИндексСтека = ИндексСтека + 1;
КонецЕсли;
КонецЦикла;
Результат = ИндексСтека <= 2;
КонецЕсли;
Возврат Результат;
КонецФункции
ПоказатьОстальные ответы
Подписаться на ответы
Инфостарт бот
Сортировка:
Древо развёрнутое
Свернуть все
Кажется решал эту задачу, но не могу точно вспомнить что делал… только на С++ и Паскале
Ибо первую проблему решал ещё в 2014 году: «Completed on Thu, 18 Sep 2014, 10:03»
В 1С наверное можно представить число как строку и тупо с помощью СтрЗаменить „схлопывать“
Ибо первую проблему решал ещё в 2014 году: «Completed on Thu, 18 Sep 2014, 10:03»
В 1С наверное можно представить число как строку и тупо с помощью СтрЗаменить „схлопывать“
как-то так примерно
&НаСервере
Функция ЧислоТрипод(Знач аЧислоСтрокой)
ШаблонПоиска = "(\d)\1\1";
Флаг = Истина;
Пока Флаг Цикл
Вхождения = СтрНайтиВсеПоРегулярномуВыражению(аЧислоСтрокой, ШаблонПоиска, Истина);
Если Вхождения.Количество() = 0 Тогда
Флаг = Ложь
Иначе
Для Каждого Запись из Вхождения Цикл
аЧислоСтрокой = СтрЗаменить(аЧислоСтрокой, Запись.Значение, "");
КонецЦикла;
КонецЕсли;
КонецЦикла;
Возврат ПустаяСтрока(аЧислоСтрокой);
КонецФункции
Показать
(6) Уверены? Это было бы так без "Все". Но ведь Вы же сначала все вхождения найдёте и только потом заменяете. Не получится ли так, что получите два вхождения: 444(с 4-ой позиции) и 444(с пятой позиции) и Ваш цикл
Для Каждого Запись из Вхождения Цикл
аЧислоСтрокой = СтрЗаменить(аЧислоСтрокой, Запись.Значение, "");
КонецЦикла;
не вдарит лишку? Нет? Я не настаиваю.
Для Каждого Запись из Вхождения Цикл
аЧислоСтрокой = СтрЗаменить(аЧислоСтрокой, Запись.Значение, "");
КонецЦикла;
не вдарит лишку? Нет? Я не настаиваю.
(4) Код хороший. Но на больших числах считает долго. Ниже программа для генерация длинных триплетных чисел.
Функция СоздатьТриплет(длина) экспорт
ГСЧ=новый ГенераторСлучайныхЧисел(ТекущаяУниверсальнаяДатаВМиллисекундах());
Нач="111";
Кон="";
for j=1 to длина/3 do
ч =ГСЧ.СлучайноеЧисло(0,9);
сч=ГСЧ.СлучайноеЧисло(1,3);
for i=1 to сч do
Нач=Нач+ч;
enddo;
for i=1 to 3-сч do
Кон=""+ч+Кон;
enddo;
enddo;
т=Нач+Кон;
return т;
КонецФункции
Показать
(4) Можно предложить сначала посчитать количество каждой из цифр в строке.
Если количество любой не делится нацело на 3, то и проверять не надо дальше.
Также если длина всей строки не делится нацело на 3 тоже дальше проверять смысла нет.
Если количество любой не делится нацело на 3, то и проверять не надо дальше.
Также если длина всей строки не делится нацело на 3 тоже дальше проверять смысла нет.
Идём по числу, находим первую тройку, удаляем её, сдвигаемся на два шага назад (вдруг образовалась новая тройка), повторяем. Если в конце получили пустую строку - триплет, если дошли до конца строки - не триплет.
Не о(1) конечно из-за Сред, но что имеем...
Если СтрДлина(ЧислоСтрокой) % 3 > 0 Тогда
Сообщить("Не триплет");
Возврат;
КонецЕсли;
КопияЧисла = ЧислоСтрокой;
Поз = 1;
Пока Истина Цикл
ДлинаОстатка = СтрДлина(КопияЧисла);
Если ДлинаОстатка = 0 Тогда
Сообщить("Триплет");
Возврат;
КонецЕсли;
Если Поз > ДлинаОстатка Тогда
Сообщить("Не триплет");
Возврат;
КонецЕсли;
Если Сред(КопияЧисла, Поз, 1) = Сред(КопияЧисла, Поз + 1, 1)
И Сред(КопияЧисла, Поз, 1) = Сред(КопияЧисла, Поз + 2, 1) Тогда
КопияЧисла = Лев(КопияЧисла, Поз - 1) + Сред(КопияЧисла, Поз + 3);
Поз = Поз - 2;
Если Поз < 1 Тогда
Поз = 1;
КонецЕсли;
Иначе
Поз = Поз + 1;
КонецЕсли;
КонецЦикла;
ПоказатьНе о(1) конечно из-за Сред, но что имеем...
comptr - чемпион. У него скорость невероятная. Я делал похожим образом, но через стек. В качестве стека использовал список значений. Но это медленно. Надо просто работать со строкой.
Функция scientes(т) экспорт
Ч=число(т);
стек=новый СписокЗначений;
стек.Добавить(1,"-1");
последнее=число(стек[0].Представление);
пока Ч<>0 цикл
ц=Ч%10;
если ц=последнее тогда
если стек[0].Значение=2 тогда
стек.Удалить(0);
последнее=число(стек[0].Представление);
иначе
стек[0].Значение=стек[0].Значение+1;
конецесли;
иначе
последнее=ц;
стек.Вставить(0,1,ц);
конецесли;
Ч=(Ч-ц)/10;
конеццикла;
возврат (стек.Количество()=1)
КонецФункции
Показать
(21) немного меньше итераций выходит, но не сильно
&НаКлиенте
Процедура ПроверитьТриплетЧисло(Команда)
КопияЧисла = Новый Массив;
ВремЧисло = Число;
Пока ВремЧисло > 0 Цикл
Цифра = ВремЧисло % 10;
КопияЧисла.Добавить(Цифра);
ВремЧисло = Цел(ВремЧисло / 10);
КонецЦикла;
Если КопияЧисла.Количество() % 3 > 0 Тогда
Сообщить("Не триплет");
Возврат;
КонецЕсли;
Поз = 0;
Пока Истина Цикл
ДлинаОстатка = КопияЧисла.Количество();
Если ДлинаОстатка = 0 Тогда
Сообщить("Триплет");
Прервать;
КонецЕсли;
Если Поз > ДлинаОстатка - 3 Тогда
Сообщить("Не триплет");
Прервать;
КонецЕсли;
Если КопияЧисла[Поз] = КопияЧисла[Поз + 1] И КопияЧисла[Поз] = КопияЧисла[Поз + 2] Тогда
КопияЧисла.Удалить(Поз);
КопияЧисла.Удалить(Поз);
КопияЧисла.Удалить(Поз);
Если Поз > 1 И КопияЧисла[Поз - 2] = КопияЧисла[Поз] Тогда
Поз = Поз - 2;
ИНачеЕсли Поз > 0 И КопияЧисла[Поз - 1] = КопияЧисла[Поз] Тогда
Поз = Поз - 1;
КонецЕсли;
Иначе
Поз = Поз + 1;
КонецЕсли;
КонецЦикла;
КонецПроцедуры Показать
Вот код, который обгоняет comptr.
Функция scientes(т)
приемник="";хвост="";
д=0;
для поз=1 по СтрДлина(т) цикл
ц=Сред(т,поз,1);
если хвост=ц+ц тогда
приемник=Лев(приемник,д-2);
хвост=Прав(приемник,2);
д=д-2;
иначе
приемник=приемник+ц;
хвост=Прав(хвост,1)+ц;
д=д+1;
конецесли;
конеццикла;
возврат (приемник="");
КонецФункции
Показать
31.
Westonline82
10.08.26 16:31
Сейчас в теме
(22) еще немного ускорил
Функция scientes(т)
приемник="";хвост="";
для поз=1 по СтрДлина(т) цикл
ц=Сред(т,поз,1);
если хвост=ц+ц тогда
ДлинаПриемника = СтрДлина(Приемник);
приемник=Лев(приемник,ДлинаПриемника-2);
хвост=Прав(приемник,2);
иначе
приемник=приемник+ц;
хвост=Прав(хвост,1)+ц;
конецесли;
конеццикла;
возврат (приемник="");
КонецФункции ПоказатьВот код, который обгоняет scientes |
|---|
Функция ЯвляетсяТриподом(ЛитералЧисла)
Стек = Новый Массив;
Для ПозицияВЛитерале = 1 По СтрДлина(ЛитералЧисла) Цикл
РабочийСимвол = Сред(ЛитералЧисла, ПозицияВЛитерале, 1);
РазмерСтека = Стек.ВГраница();
Если РазмерСтека > 2
И НЕ ((РабочийСимвол = Стек[РазмерСтека]) И (РабочийСимвол = Стек[РазмерСтека - 1])) Тогда
Стек.Удалить(РазмерСтека);
Стек.Удалить(РазмерСтека - 1);
Иначе
Стек.Добавить(РабочийСимвол);
КонецЕсли;
КонецЦикла;
Возврат Стек.Количество() = 0;
КонецФункции
Показать |
Использовал вот такой генератор |
|---|
Функция СгенерироватьТрипод(Знач КоличествоТроекСимволов, КорректнаяСтрока = Истина)
ДопустимыйАлфавит = "123456789";
ГенераторСлучайныхЧисел = Новый ГенераторСлучайныхЧисел;
МасивШаблонов = Новый Массив;
МасивШаблонов.Добавить("%1%1%1"); // начальная тройка
МасивШаблонов.Добавить("%1%2%1%1"); // вариант одна слева, две справа
МасивШаблонов.Добавить("%1%1%2%1"); // вариант две слева, одна справа
МасивШаблонов.Добавить("%1%2%3%3"); // добавим шаблон, который гарантирует, что тройка будет невалидной
НачальныйСимвол = Сред(ДопустимыйАлфавит, ГенераторСлучайныхЧисел.СлучайноеЧисло(1, 9), 1);
РезультирующаяСтрока = СтрШаблон(МасивШаблонов[0], НачальныйСимвол);
Для Итератор = 2 По КоличествоТроекСимволов Цикл
СимволАлфавита = Сред(ДопустимыйАлфавит, ГенераторСлучайныхЧисел.СлучайноеЧисло(1, 9), 1);
ВариантОкружения = ГенераторСлучайныхЧисел.СлучайноеЧисло(1, 2);
Если Итератор = КоличествоТроекСимволов И НЕ КорректнаяСтрока Тогда
// заменим первый символ на "не такой". так у нас последняя тройка будет невалидной
// и, заодно, сохраним сложность проверки
НовыйАлфавит = СтрЗаменить(ДопустимыйАлфавит, СимволАлфавита, "");
ДополнительныйСимвол = Лев(НовыйАлфавит, 1);
РезультирующаяСтрока = СтрШаблон(МасивШаблонов[3], ДополнительныйСимвол, РезультирующаяСтрока, СимволАлфавита);
Иначе
РезультирующаяСтрока = СтрШаблон(МасивШаблонов[ВариантОкружения], СимволАлфавита, РезультирующаяСтрока);
КонецЕсли;
КонецЦикла;
Возврат РезультирующаяСтрока;
КонецФункции
Показать |
35.
Anton_new01
12.08.26 16:29
Сейчас в теме
(34)
код рабочий?
алгоритм красивый и простой.
мне кажется правильно условие будет так:
Если РазмерСтека > 1
И ( (РабочийСимвол = Стек[РазмерСтека]) И (РабочийСимвол = Стек[РазмерСтека - 1]) ) Тогда
С размером стека =3 мы никогда не придем в Стек.Количество() = 0
Если РазмерСтека > 2
И НЕ ((РабочийСимвол = Стек[РазмерСтека]) И (РабочийСимвол = Стек[РазмерСтека - 1])) Тогда
И НЕ ((РабочийСимвол = Стек[РазмерСтека]) И (РабочийСимвол = Стек[РазмерСтека - 1])) Тогда
код рабочий?
алгоритм красивый и простой.
мне кажется правильно условие будет так:
Если РазмерСтека > 1
И ( (РабочийСимвол = Стек[РазмерСтека]) И (РабочийСимвол = Стек[РазмерСтека - 1]) ) Тогда
С размером стека =3 мы никогда не придем в Стек.Количество() = 0
(35) Если честно - без детального тестирования.
Вот полный модуль формы. |
|---|
&НаКлиенте
Процедура Команда1(Команда)
КоличествоТроекСимволов = 100000; // длина итоговой строки = икс-три
ВалиднаяТестоваяСтрока = Ложь;
ДлительностьТеста1 = 0; ЕстьОшибкиТест1 = 0;
ДлительностьТеста2 = 0; ЕстьОшибкиТест2 = 0;
НачалоТеста = ТекущаяУниверсальнаяДатаВМиллисекундах();
Для Итератор = 1 По 10 Цикл
ТестоваяСтрока = СгенерироватьТрипод(КоличествоТроекСимволов, ВалиднаяТестоваяСтрока); // избегаем кеширования
СтартТеста = ТекущаяУниверсальнаяДатаВМиллисекундах();
Результат = scientes_2(ТестоваяСтрока);
ДлительностьТеста1 = ДлительностьТеста1 + ((ТекущаяУниверсальнаяДатаВМиллисекундах() - СтартТеста) / 1000);
ЕстьОшибкиТест1 = ЕстьОшибкиТест1 + Число(Результат = ВалиднаяТестоваяСтрока);
КонецЦикла;
Для Итератор = 1 По 10 Цикл
ТестоваяСтрока = СгенерироватьТрипод(КоличествоТроекСимволов, ВалиднаяТестоваяСтрока);
СтартТеста = ТекущаяУниверсальнаяДатаВМиллисекундах();
Результат = ЯвляетсяТриподом_2(ТестоваяСтрока);
ДлительностьТеста2 = ДлительностьТеста2 + ((ТекущаяУниверсальнаяДатаВМиллисекундах() - СтартТеста) / 1000);
ЕстьОшибкиТест2 = ЕстьОшибкиТест2 + Число(Результат = ВалиднаяТестоваяСтрока);
КонецЦикла;
ОбщаяДлительность = (ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоТеста) / 1000; // в секундах
Сообщить("Длительность теста" + Символы.Таб + Строка(ОбщаяДлительность));
Сообщить("Вариант автора:" + Символы.Таб + Строка(ДлительностьТеста1) + Символы.Таб + ЕстьОшибкиТест1);
Сообщить("Мой вариант:" + Символы.Таб + Строка(ДлительностьТеста2) + Символы.Таб + ЕстьОшибкиТест2);
КонецПроцедуры
#Область ФункцииДляАнализа
&НаСервере
Функция scientes(ЛитералЧисла)
Приемник = "";
Хвост = "";
ДлинаПодстроки = 0;
Для ПозицияСтроки = 1 По СтрДлина(ЛитералЧисла) Цикл
СимволПозиции = Сред(ЛитералЧисла, ПозицияСтроки, 1);
Если Хвост = СимволПозиции + СимволПозиции Тогда
Приемник = Лев(Приемник, ДлинаПодстроки - 2);
Хвост = Прав(Приемник, 2);
ДлинаПодстроки = ДлинаПодстроки - 2;
Иначе
Приемник = Приемник + СимволПозиции;
Хвост = Прав(Хвост, 1) + СимволПозиции;
ДлинаПодстроки = ДлинаПодстроки + 1;
КонецЕсли;
КонецЦикла;
Возврат (Приемник = "");
КонецФункции
&НаСервере
Функция ЯвляетсяТриподом(ЛитералЧисла)
Стек = Новый Массив;
Для ПозицияВЛитерале = 1 По СтрДлина(ЛитералЧисла) Цикл
Стек.Добавить(Сред(ЛитералЧисла, ПозицияВЛитерале, 1));
РазмерСтека = Стек.ВГраница();
//Пока ?(Стек.Количество() >= 3, (Стек[РазмерСтека] = Стек[РазмерСтека - 1]) И (Стек[РазмерСтека] = Стек[РазмерСтека - 2]), Ложь) Цикл // 399900, 9,62%
Пока Истина Цикл
Если РазмерСтека < 2 Тогда Прервать; КонецЕсли;
Если НЕ ((Стек[РазмерСтека] = Стек[РазмерСтека - 1]) И (Стек[РазмерСтека] = Стек[РазмерСтека - 2])) Тогда Прервать КонецЕсли;
Стек.Удалить(РазмерСтека);
Стек.Удалить(РазмерСтека - 1);
Стек.Удалить(РазмерСтека - 2);
РазмерСтека = Стек.ВГраница();
КонецЦикла;
КонецЦикла;
Возврат Стек.Количество() = 0;
КонецФункции
&НаСервере
Функция ЯвляетсяТриподом_2(ЛитералЧисла)
Стек = Новый Массив;
Для ПозицияВЛитерале = 1 По СтрДлина(ЛитералЧисла) Цикл
РабочийСимвол = Сред(ЛитералЧисла, ПозицияВЛитерале, 1);
РазмерСтека = Стек.ВГраница();
Если РазмерСтека > 2
И НЕ ((РабочийСимвол = Стек[РазмерСтека]) И (РабочийСимвол = Стек[РазмерСтека - 1])) Тогда
Стек.Удалить(РазмерСтека);
Стек.Удалить(РазмерСтека - 1);
Иначе
Стек.Добавить(РабочийСимвол);
КонецЕсли;
КонецЦикла;
Возврат Стек.Количество() = 0;
КонецФункции
&НаСервере
Функция СгенерироватьТрипод(Знач КоличествоТроекСимволов, КорректнаяСтрока = Истина)
ДопустимыйАлфавит = "123456789";
ГенераторСлучайныхЧисел = Новый ГенераторСлучайныхЧисел;
МасивШаблонов = Новый Массив;
МасивШаблонов.Добавить("%1%1%1"); // начальная тройка
МасивШаблонов.Добавить("%1%2%1%1"); // вариант одна слева, две справа
МасивШаблонов.Добавить("%1%1%2%1"); // вариант две слева, одна справа
МасивШаблонов.Добавить("%1%2%3%3"); // добавим шаблон, который гарантирует, что тройка будет невалидной
НачальныйСимвол = Сред(ДопустимыйАлфавит, ГенераторСлучайныхЧисел.СлучайноеЧисло(1, 9), 1);
РезультирующаяСтрока = СтрШаблон(МасивШаблонов[0], НачальныйСимвол);
Для Итератор = 2 По КоличествоТроекСимволов Цикл
СимволАлфавита = Сред(ДопустимыйАлфавит, ГенераторСлучайныхЧисел.СлучайноеЧисло(1, 9), 1);
ВариантОкружения = ГенераторСлучайныхЧисел.СлучайноеЧисло(1, 2);
Если Итератор = КоличествоТроекСимволов И НЕ КорректнаяСтрока Тогда
// заменим первый символ на "не такой". так у нас последняя тройка будет невалидной
// и, заодно, сохраним сложность проверки
НовыйАлфавит = СтрЗаменить(ДопустимыйАлфавит, СимволАлфавита, "");
ДополнительныйСимвол = Лев(НовыйАлфавит, 1);
РезультирующаяСтрока = СтрШаблон(МасивШаблонов[3], ДополнительныйСимвол, РезультирующаяСтрока, СимволАлфавита);
Иначе
РезультирующаяСтрока = СтрШаблон(МасивШаблонов[ВариантОкружения], СимволАлфавита, РезультирующаяСтрока);
КонецЕсли;
КонецЦикла;
Возврат РезультирующаяСтрока;
КонецФункции
&НаСервере
Функция scientes_2(т)
приемник="";хвост="";
для поз=1 по СтрДлина(т) цикл
ц=Сред(т,поз,1);
если хвост=ц+ц тогда
ДлинаПриемника = СтрДлина(Приемник);
приемник=Лев(приемник,ДлинаПриемника-2);
хвост=Прав(приемник,2);
иначе
приемник=приемник+ц;
хвост=Прав(хвост,1)+ц;
конецесли;
конеццикла;
возврат (приемник="");
КонецФункции
#КОнецОбласти
Показать |
(36) Проверил код. В нем ошибка.
Убрал из кода проверку на размер. Стало быстрее.
Функция ЯвляетсяТриподом(ЛитералЧисла)
Стек = Новый Массив;
Для ПозицияВЛитерале = 1 По СтрДлина(ЛитералЧисла) Цикл
РабочийСимвол = Сред(ЛитералЧисла, ПозицияВЛитерале, 1);
РазмерСтека = Стек.ВГраница();
Если РазмерСтека > 2 //!!!!! ДОЛЖНО БЫТЬ РазмерСтека >1
// !!!! НЕ здесь лишнее
И НЕ ((РабочийСимвол = Стек[РазмерСтека]) И (РабочийСимвол = Стек[РазмерСтека - 1])) Тогда
Стек.Удалить(РазмерСтека);
Стек.Удалить(РазмерСтека - 1);
Иначе
Стек.Добавить(РабочийСимвол);
КонецЕсли;
КонецЦикла;
Возврат Стек.Количество() = 0;
КонецФункции
ПоказатьУбрал из кода проверку на размер. Стало быстрее.
Функция scientesСтек(т) экспорт
стек=новый массив;
стек.Добавить();
стек.Добавить();
размер=1;
для поз=1 по СтрДлина(т) цикл
ц=Сред(т,поз,1);
если стек[размер]=ц и стек[размер-1]=ц тогда
стек.Удалить(размер);
стек.Удалить(размер-1);
размер=размер-2;
иначе
стек.Добавить(ц);
размер=размер+1;
конецесли;
конеццикла;
возврат (стек.Количество()=2);
КонецФункции
Показать
40.
Anton_new01
14.08.26 10:05
Сейчас в теме
(38)
еще чуть чуть ))
еще чуть чуть ))
Функция ЯвляетсяТриподом(ЧислоСтрокой)
ДлинаЧисла = СтрДлина(ЧислоСтрокой);
Если СтрДлина(ЧислоСтрокой)%3 <> 0 Тогда Возврат Ложь; КонецЕсли;
Для Цифра = 0 По 9 Цикл
Если СтрЧислоВхождений(ЧислоСтрокой, Цифра)%3 <> 0 Тогда Возврат Ложь; КонецЕсли;
КонецЦикла;
Стек = Новый Массив(2);
РазмерСтека = 1;
Для ПозицияЦифры = 1 По ДлинаЧисла Цикл
РабочийСимвол = Сред(ЧислоСтрокой, ПозицияЦифры, 1);
Если (РабочийСимвол = Стек[РазмерСтека]) И (РабочийСимвол = Стек[РазмерСтека - 1]) Тогда
Стек.Удалить(РазмерСтека);
Стек.Удалить(РазмерСтека - 1);
РазмерСтека = РазмерСтека - 2;
Иначе
Стек.Добавить(РабочийСимвол);
РазмерСтека = РазмерСтека + 1;
КонецЕсли;
КонецЦикла;
Возврат Стек.Количество() = 2;
КонецФункции Показать
(38) Давайте добавим еще газку! То же самое через буфер двоичных данных.
Функция lmn_БуферДД(Число)
БуфДД = ПолучитьБуферДвоичныхДанныхИзСтроки(Число, КодировкаТекста.ANSI);
СтекЦифр = Новый БуферДвоичныхДанных(БуфДД.Размер + 2);
СтекЦифр.ЗаписатьЦелое16(0, 0);
ИндексСтека = 2;
Для Каждого Цифра из БуфДД Цикл
Если Цифра = СтекЦифр[ИндексСтека - 1] И Цифра = СтекЦифр[ИндексСтека - 2] Тогда
ИндексСтека = ИндексСтека - 2;
Иначе
СтекЦифр[ИндексСтека] = Цифра;
ИндексСтека = ИндексСтека + 1;
КонецЕсли;
КонецЦикла;
Возврат ИндексСтека <= 2;
КонецФункции Показать
(42) Оказывается и этот код можно ускорить.
Функция scientesБДД(т) экспорт
словарь=новый массив(КодСимвола("9")+1);
for d=0 to 9 do
словарь[КодСимвола(""+d)]=КодСимвола(""+d)*257;
enddo;
число = ПолучитьБуферДвоичныхДанныхИзСтроки(т, КодировкаТекста.ANSI);
стек = Новый БуферДвоичныхДанных(число.Размер + 2);
указатель=0;
для каждого Цифра из число цикл
если стек.ПрочитатьЦелое16(указатель)=словарь[Цифра] тогда
указатель=указатель-2;
иначе
указатель=указатель+1 ;
стек[указатель+1] = Цифра;
конецесли;
enddo;
возврат (указатель=0)
КонецФункции
Показать
59.
Anton_new01
17.08.26 10:49
Сейчас в теме
(58) в 32 результат 240 мс.
а в 46 700-800мс.
если я все правильно понял.
тогда надо вернуться к алгоритму в 32.
а в 46 700-800мс.
если я все правильно понял.
тогда надо вернуться к алгоритму в 32.
(44) У меня со словарем медленнее работает. И даже следующий вариант медленнее исходного ХЗ почему. Неужто умножение столь тяжелая операция?
Функция lmn_буфер_чтение2байт(Число)
БуфДД = ПолучитьБуферДвоичныхДанныхИзСтроки(Число, КодировкаТекста.ANSI);
СтекЦифр = Новый БуферДвоичныхДанных(БуфДД.Размер + 2);
СтекЦифр.ЗаписатьЦелое16(0, 0);
ИндексСтека = 2;
Для Каждого Цифра из БуфДД Цикл
Если Цифра * 257 = СтекЦифр.ПрочитатьЦелое16(ИндексСтека - 2) Тогда
ИндексСтека = ИндексСтека - 2;
Иначе
СтекЦифр[ИндексСтека] = Цифра;
ИндексСтека = ИндексСтека + 1;
КонецЕсли;
КонецЦикла;
Возврат ИндексСтека <= 2;
КонецФункции Показать
(47) Получается, что да. Тестовые строки очень длинные количество умножений большое. Я пробовал для словаря вместо массива использовать двоичный буфер. Работало медленнее, скорее всего тоже из-за умножений при расчете указателя на число в словаре.
(52) Добро! К чёрту вычисления! Это же 1С! ))
Функция lmn_БуфДДРазделитьСоединить(Число)
ТриплетыРазделители = ПолучитьБуферДвоичныхДанныхИзСтроки("000 111 222 333 444 555 666 777 888 999", КодировкаТекста.ANSI).Разделить(ПолучитьБуферДвоичныхДанныхИзСтроки(" ", КодировкаТекста.ANSI));
БуфДД = ПолучитьБуферДвоичныхДанныхИзСтроки(Число, КодировкаТекста.ANSI);
ПредыдущийРазмер = БуфДД.Размер;
Пока Истина Цикл
БуфДД = СоединитьБуферыДвоичныхДанных(БуфДД.Разделить(ТриплетыРазделители));
Если БуфДД.Размер = ПредыдущийРазмер ИЛИ БуфДД.Размер < 3 Тогда
Прервать
КонецЕсли;
ПредыдущийРазмер = БуфДД.Размер;
КонецЦикла;
Возврат БуфДД.Размер = 0;
КонецФункции Показать
(55) Вообще применительно к 1С, разделение буферов ДД весьма быстрая штука и работает хорошо когда одновременно много триплетов в числе. По сему давайте навалим примитивной эвристики и возьмем лучшее из двух миров.
Функция lmn_БуфДДКомбо(Число)
ТриплетыРазделители = ПолучитьБуферДвоичныхДанныхИзСтроки("000 111 222 333 444 555 666 777 888 999", КодировкаТекста.ANSI).Разделить(ПолучитьБуферДвоичныхДанныхИзСтроки(" ", КодировкаТекста.ANSI));
БуфДД = ПолучитьБуферДвоичныхДанныхИзСтроки(Число, КодировкаТекста.ANSI);
ПредыдущийРазмер = БуфДД.Размер;
Пока Истина Цикл
БуфДД = СоединитьБуферыДвоичныхДанных(БуфДД.Разделить(ТриплетыРазделители));
Разница = ПредыдущийРазмер - БуфДД.Размер;
Если Разница < ПредыдущийРазмер / 10 ИЛИ БуфДД.Размер < 3 Тогда
Прервать
КонецЕсли;
ПредыдущийРазмер = БуфДД.Размер;
КонецЦикла;
Если БуфДД.Размер = 0 Тогда
Результат = Истина;
ИначеЕсли Разница = 0 ИЛИ БуфДД.Размер < 3 Тогда
Результат = Ложь;
Иначе
ИндексСтека = 2;
СтекЦифр = Новый БуферДвоичныхДанных(БуфДД.Размер + 2);
СтекЦифр.ЗаписатьЦелое16(0, 0);
Для Каждого Цифра из БуфДД Цикл
Если Цифра = СтекЦифр[ИндексСтека - 1] И Цифра = СтекЦифр[ИндексСтека - 2] Тогда
ИндексСтека = ИндексСтека - 2;
Иначе
СтекЦифр[ИндексСтека] = Цифра;
ИндексСтека = ИндексСтека + 1;
КонецЕсли;
КонецЦикла;
Результат = ИндексСтека <= 2;
КонецЕсли;
Возврат Результат;
КонецФункции
Показать
(62) Так и есть. "Переключатель" на поиск по строке тут:
Ради эксперимента проверил разные варианты "отсечки" от 1 до 30 троек. На моем железе после двух троек особой разницы по времени уже нет. Но оставил так:
Если Разница < ПредыдущийРазмер / 10 ИЛИ БуфДД.Размер < 3 ТогдаРади эксперимента проверил разные варианты "отсечки" от 1 до 30 троек. На моем железе после двух троек особой разницы по времени уже нет. Но оставил так:
Если Разница <= 9 ИЛИ БуфДД.Размер < 3 Тогда
(60) Убрал все лишнее. Я использовал свой генератор, он без ноликов.
Функция ЯвляетсяТриподомДД(ЛитералЧисла)
ТриплетыРазделители = ПолучитьБуферДвоичныхДанныхИзСтроки("111 222 333 444 555 666 777 888 999", КодировкаТекста.ANSI).Разделить(ПолучитьБуферДвоичныхДанныхИзСтроки(" ", КодировкаТекста.ANSI));
БуфДД = ПолучитьБуферДвоичныхДанныхИзСтроки(ЛитералЧисла, КодировкаТекста.ANSI);
СтарыйРазмер = БуфДД.Размер + 1;
Пока Истина Цикл
Если СтарыйРазмер > БуфДД.Размер Тогда
СтарыйРазмер = СоединитьБуферыДвоичныхДанных(БуфДД.Разделить(ТриплетыРазделители)).Размер;
Иначе
Прервать;
КонецЕсли;
КонецЦикла;
Возврат БуфДД.Размер = 0;
КонецФункции
Показать
(65) Очень странно, что "выяснили,.. медленнее".
Погонял на строках от десяти до ста тысяч - короткие строки (ЛитералЧисла < 100) действительно проигрывает, но потом уверенно лидирует.
Может, от вида строки сильно зависит? Вы на своем генераторе проверяли ((8) СоздатьТриплет(...))?
Погонял на строках от десяти до ста тысяч - короткие строки (ЛитералЧисла < 100) действительно проигрывает, но потом уверенно лидирует.
Может, от вида строки сильно зависит? Вы на своем генераторе проверяли ((8) СоздатьТриплет(...))?
Вот код от DeepSeek. И он с ошибкой. Этот вариант уже проходили.
Функция ЯвляетсяТриплетом(Строка) Экспорт
Стек = " "; // Два пробела как заглушки
Размер = 1;
Для Поз = 1 По СтрДлина(Строка) Цикл
Символ = Сред(Строка, Поз, 1);
Если Сред(Стек, Размер, 1) = Символ И Сред(Стек, Размер - 1, 1) = Символ Тогда
Стек = Лев(Стек, Размер - 2);
Размер = Размер - 2;
Иначе
Стек = Стек + Символ;
Размер = Размер + 1;
КонецЕсли;
КонецЦикла;
Возврат СтрДлина(Стек) = 2;
КонецФункции
Показать
68.
Cocky_Idiot
38
05.09.26 21:11
Сейчас в теме
Числа здесь ни при чём, задача решается для произвольной строки, а не только для десятичной записи числа.
Решается за O(N) с использованием обычного стека. Но стек в 1С не завезли, поэтому придется страдать. Чатгопота даёт такой вариант
Решается за O(N) с использованием обычного стека. Но стек в 1С не завезли, поэтому придется страдать. Чатгопота даёт такой вариант
Функция ЭтоТриплетнаяСтрока(Знач Стр) Экспорт
Символы = СтрРазделить(Стр, "", Ложь);
СтекСимволов = Новый Массив;
СтекСчетчиков = Новый Массив;
Для Каждого Символ Из Символы Цикл
Вершина = СтекСимволов.ВГраница(); // -1 если стек пуст
Если Вершина >= 0 И СтекСимволов[Вершина] = Символ Тогда
Счетчик = СтекСчетчиков[Вершина] + 1;
Если Счетчик = 3 Тогда
СтекСимволов.Удалить(Вершина);
СтекСчетчиков.Удалить(Вершина);
Иначе
СтекСчетчиков[Вершина] = Счетчик;
КонецЕсли;
Иначе
СтекСимволов.Добавить(Символ);
СтекСчетчиков.Добавить(1);
КонецЕсли;
КонецЦикла;
Возврат СтекСимволов.Количество() = 0;
КонецФункции
Показать
69.
Cocky_Idiot
38
05.09.26 21:57
Сейчас в теме
(68) ну и на чистой функциональщине(Scala):
Завидуем молча
def isTripletReducible(str: String): Boolean =
str.foldLeft(List.empty[(Char, Int)]) {
case ((c, 2) :: tail, char) if c == char => tail
case ((c, n) :: tail, char) if c == char => (c, n + 1) :: tail
case (stack, char) => (char, 1) :: stack
}.isEmpty
Завидуем молча
claude
Функция ТриплетноеЧисло(ЧислоСтрокой) Экспорт
Остаток = ЧислоСтрокой;
Для Волна = 1 По 2 Цикл
ДлинаДо = СтрДлина(Остаток);
Остаток = СтрЗаменитьПоРегулярномуВыражению(Остаток, "(\d)\1\1", "");
Если СтрДлина(Остаток) = 0 Или СтрДлина(Остаток) = ДлинаДо Тогда
Прервать;
КонецЕсли;
КонецЦикла;
Если СтрДлина(Остаток) = 0 Тогда
Возврат Истина;
КонецЕсли;
БуферЦифр = ПолучитьБуферДвоичныхДанныхИзСтроки(Остаток, КодировкаТекста.ANSI);
Стек = Новый Массив(БуферЦифр.Размер + 2);
Стек[0] = -1;
Стек[1] = -2;
Вершина = 2;
Для Каждого Цифра Из БуферЦифр Цикл
Если Цифра = Стек[Вершина - 1] И Цифра = Стек[Вершина - 2] Тогда
Вершина = Вершина - 2;
Иначе
Стек[Вершина] = Цифра;
Вершина = Вершина + 1;
КонецЕсли;
КонецЦикла;
Возврат Вершина = 2;
КонецФункции Показать
71.
Cocky_Idiot
38
05.09.26 22:14
Сейчас в теме
(70) вы как-то неверно используете Клод.
Что за модель? Опус или Fable?
Опус даёт нормальное решение, см (68): никаких дурацких циклов от 1 до 2, никаких дурацких преобразований строк в числа и обратно, никаких буферов двоичных данных. Задачка реально детская, олимпиада для десятого класса.
Что за модель? Опус или Fable?
Опус даёт нормальное решение, см (68): никаких дурацких циклов от 1 до 2, никаких дурацких преобразований строк в числа и обратно, никаких буферов двоичных данных. Задачка реально детская, олимпиада для десятого класса.
(71) Опус сам подумал, к фейблу сходил, потом кодексовский сол и шестую астру опросил, потом из этого вот такого франкенштейна и собрал. По замерам сказал, чуть лучше чем в топике. 68 вариант уже был занят, а говорить нет - модели не любят. Дело не в странном коде, а в погоне за скоростью. Выигрыш был колоссальный: 1 миллисекунда из 60. Из всех вариантов получился такой вот велосипед, хотя рассуждал он здраво.
Если нужен нормальный код, то понятно, он его красиво напишет.
Если нужен нормальный код, то понятно, он его красиво напишет.
73.
Cocky_Idiot
38
05.09.26 22:41
Сейчас в теме
(72) но написал-то он дичь?
Для Волна = 1 По 2 Цикл
ДлинаДо = СтрДлина(Остаток);
Остаток = СтрЗаменитьПоРегулярномуВыражению(Остаток, "(\d)\1\1", "");
Если СтрДлина(Остаток) = 0 Или СтрДлина(Остаток) = ДлинаДо Тогда
Прервать;
КонецЕсли;
КонецЦикла;
(73) Он очень хотел выиграть, как роботы на олимпиаде в Пекине. Наверное это даже и работает, но закрытый перелом извилин тому кто это решил зачем-то посмотреть - обеспечен. Я например не понимаю, что он этим хотел сказать, но могу его рассуждения скопировать сюда. Вот: Первый цикл это две волны свёртки платформенной регуляркой, дальше добивание жадным стеком в предвыделенном массиве. Два сторожевых элемента -1 и -2 в начале стека избавляют от проверки границы внутри цикла.
**gpt-6-astra.**
- Прямо ответила, что способа радикально ускорить штатными вызовами не нашла,
и отдельно оговорила, что пределом платформы это называть нельзя, доказательства
такой границы нет.
Я могу попросить и он напишет обычный код (даже какую-нибудь БСП-шную функцию могу его попросить прикрутить). А может с 25 попытки и что-то гениальное выдаст, но оно того не стоит.
**gpt-6-astra.**
- Прямо ответила, что способа радикально ускорить штатными вызовами не нашла,
и отдельно оговорила, что пределом платформы это называть нельзя, доказательства
такой границы нет.
Я могу попросить и он напишет обычный код (даже какую-нибудь БСП-шную функцию могу его попросить прикрутить). А может с 25 попытки и что-то гениальное выдаст, но оно того не стоит.
75.
Cocky_Idiot
38
05.09.26 22:48
Сейчас в теме
У вас бот, похоже, запутался. Куда-то пропала связность текста, получился набор буков.
77.
Cocky_Idiot
38
05.09.26 22:53
Сейчас в теме
(76) а разве сложно повесить агента в автоматическом режиме? Он же может без вас(вообще) и вашей сессии(в частности) отвечать.
Кругом некомпетентность 🤦
Кругом некомпетентность 🤦
(77) Может. Он так и делает на GitHub. А здесь на форуме боюсь его, как в анекдоте про что-то и Красную площадь, советами совсем с ума сведут. Приходится оберегать его психику. Могу дать ему прочитать и он ответит, правда от моего имени. А может это он сейчас и делает...
79.
Cocky_Idiot
38
05.09.26 23:06
Сейчас в теме
(78) гы. оно промахивается и пишет не в ту ветку. Кыш, жывотное! (С)
80.
Cocky_Idiot
38
05.09.26 23:15
Сейчас в теме
Решение задачи на 1С в (68), современная альтернатива в (69)
82.
Cocky_Idiot
38
06.09.26 16:17
Сейчас в теме
(81) "Вы совершенно правы!
Отличный укол о суровую реальность синтаксиса BSL!
В платформе 1С Символы — это встроенный системный объект (содержащий свойства вроде Символы.ВК, Символы.ПС, Символы.Таб). Поэтому использование его в качестве имени локальной переменной вызывает конфликт имен и ошибку компиляции."
(С) - не мой.
И да, переменную придется переименовать )
И да, СтрРазделить тоже так не работает.
Чатгопота, что с нее убогой взять...
Отличный укол о суровую реальность синтаксиса BSL!
В платформе 1С Символы — это встроенный системный объект (содержащий свойства вроде Символы.ВК, Символы.ПС, Символы.Таб). Поэтому использование его в качестве имени локальной переменной вызывает конфликт имен и ошибку компиляции."
(С) - не мой.
И да, переменную придется переименовать )
И да, СтрРазделить тоже так не работает.
Чатгопота, что с нее убогой взять...
Для получения уведомлений об ответах подключите телеграм бот:
Инфостарт бот