Триплетные числа (спортивное программированинне)

1. scientes 297 07.08.26 08:53 Сейчас в теме
Категорически Вас приветствую. В свое время предлагал похожее задание на DevBattle. А недавно встретил задачу на Project Euler, где предметом исследования были эти самые триплетные числа.
Триплетным называется число, которое можно последовательно "схлопнуть", удаляя . три соседние одинаковые цифры. Пример такого числа 125577752211.
125577752211 -> 125552211 -> 122211 -> 111 -> "".
Вопрос в том, как быстро проверить исходное число на триплетность ?
Сразу скажу, что в практической деятельности это никогда не встретится. Тема для тех, кто занимается саморазвитием. Спасибо.
И да, это то что ждет 1С программиста после 55.
Найденные решения
60. lmnlmn 71 18.08.26 11:51 Сейчас в теме
(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;
		
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции
Показать
Остальные ответы
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
26. GenaT1C 13 07.08.26 16:17 Сейчас в теме
(1) Если речь идёт о победе за скорость для огромных чисел, то лучше в начале получить десять количеств цифр в этом числе и проверить, что все они нацело делятся на 3. И только потом работать.
2. jmw 61 07.08.26 09:23 Сейчас в теме
Кажется решал эту задачу, но не могу точно вспомнить что делал… только на С++ и Паскале
Ибо первую проблему решал ещё в 2014 году: «Completed on Thu, 18 Sep 2014, 10:03»

В 1С наверное можно представить число как строку и тупо с помощью СтрЗаменить „схлопывать“
3. scientes 297 07.08.26 09:28 Сейчас в теме
(2) Да, можно и через СтрЗаменить, но это, как мне видится, будет долго.
4. dehro 15 07.08.26 09:56 Сейчас в теме
как-то так примерно
&НаСервере
Функция ЧислоТрипод(Знач аЧислоСтрокой)
	ШаблонПоиска = "(\d)\1\1";
	Флаг = Истина;
	Пока Флаг Цикл
		Вхождения = СтрНайтиВсеПоРегулярномуВыражению(аЧислоСтрокой, ШаблонПоиска, Истина);  
		Если Вхождения.Количество() = 0 Тогда
			Флаг = Ложь
		Иначе
			Для Каждого Запись из Вхождения Цикл
				аЧислоСтрокой = СтрЗаменить(аЧислоСтрокой, Запись.Значение, "");
			КонецЦикла;	
		КонецЕсли;	
	КонецЦикла;
	
	Возврат ПустаяСтрока(аЧислоСтрокой);
КонецФункции
Показать
scientes; +1 Ответить
5. GenaT1C 13 07.08.26 10:13 Сейчас в теме
(4)
СтрНайтиВсеПоРегулярномуВыражению

Может "Все" не надо? Пусть по первому же вхождению прошуршит. А то сомневаюсь, что четырки, пятырки и т.д. правильно отработает. Например 123444456...
6. dehro 15 07.08.26 10:25 Сейчас в теме
(5) Удалит всё равно 3 символа. И из "123444456" останется "123456", второй цикл удаления ничего не удалит ("444" уже нету)
7. GenaT1C 13 07.08.26 10:32 Сейчас в теме
(6) Уверены? Это было бы так без "Все". Но ведь Вы же сначала все вхождения найдёте и только потом заменяете. Не получится ли так, что получите два вхождения: 444(с 4-ой позиции) и 444(с пятой позиции) и Ваш цикл
Для Каждого Запись из Вхождения Цикл
аЧислоСтрокой = СтрЗаменить(аЧислоСтрокой, Запись.Значение, "");
КонецЦикла;

не вдарит лишку? Нет? Я не настаиваю.
9. scientes 297 07.08.26 10:40 Сейчас в теме
(7) Проверил. СтрНайтиВсеПоРегулярномуВыражению для "4444" дает одно вхождение.
10. GenaT1C 13 07.08.26 10:42 Сейчас в теме
(9) Тогда нет проблем. Значит в (4) верно.
8. scientes 297 07.08.26 10:35 Сейчас в теме
(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 т;
КонецФункции

Показать
11. Sashares 34 07.08.26 12:18 Сейчас в теме
(4) Можно предложить сначала посчитать количество каждой из цифр в строке.
Если количество любой не делится нацело на 3, то и проверять не надо дальше.
Также если длина всей строки не делится нацело на 3 тоже дальше проверять смысла нет.
12. scientes 297 07.08.26 13:48 Сейчас в теме
(11) Да, это необходимое условие.
16. dehro 15 07.08.26 14:38 Сейчас в теме
(11) На числах из менее чем 20 цифр дополнительные проверки существенно время работы не снизят.

20 тыс символов - это уже повесть))
13. scientes 297 07.08.26 14:03 Сейчас в теме
Сравнил скорость своего метода и алгоритма, который предложил dehro.
Прикрепленные файлы:
15. dehro 15 07.08.26 14:33 Сейчас в теме
(13) А свой метод в чём заключается?
14. comptr 57 07.08.26 14:16 Сейчас в теме
Идём по числу, находим первую тройку, удаляем её, сдвигаемся на два шага назад (вдруг образовалась новая тройка), повторяем. Если в конце получили пустую строку - триплет, если дошли до конца строки - не триплет.
Если СтрДлина(ЧислоСтрокой) % 3 > 0 Тогда
		Сообщить("Не триплет");
		Возврат;
	КонецЕсли;
	КопияЧисла = ЧислоСтрокой;
	Поз = 1;
	Пока Истина Цикл
		ДлинаОстатка = СтрДлина(КопияЧисла);
		Если ДлинаОстатка = 0 Тогда
			Сообщить("Триплет");
			Возврат;
		КонецЕсли;
		Если Поз > ДлинаОстатка Тогда
			Сообщить("Не триплет");
			Возврат;
		КонецЕсли;
		Если Сред(КопияЧисла, Поз, 1) = Сред(КопияЧисла, Поз + 1, 1)
			И Сред(КопияЧисла, Поз, 1) = Сред(КопияЧисла, Поз + 2, 1) Тогда
			КопияЧисла = Лев(КопияЧисла, Поз - 1) + Сред(КопияЧисла, Поз + 3);
			Поз = Поз - 2;
			Если Поз < 1 Тогда
				Поз = 1;
			КонецЕсли;
		Иначе
			Поз = Поз + 1;
		КонецЕсли;
	КонецЦикла;
Показать

Не о(1) конечно из-за Сред, но что имеем...
scientes; +1 Ответить
17. scientes 297 07.08.26 14:45 Сейчас в теме
comptr - чемпион. У него скорость невероятная. Я делал похожим образом, но через стек. В качестве стека использовал список значений. Но это медленно. Надо просто работать со строкой.
Функция scientes(т) экспорт
	Ч=число(т);
        стек=новый СписокЗначений;	
	стек.Добавить(1,"-1");
	последнее=число(стек[0].Представление);
	пока Ч<>0 цикл
		ц=Ч%10;
		если ц=последнее тогда
			если стек[0].Значение=2 тогда
				стек.Удалить(0);
				последнее=число(стек[0].Представление);
			иначе
				стек[0].Значение=стек[0].Значение+1;
			конецесли;
		иначе
			последнее=ц;
			стек.Вставить(0,1,ц);
		конецесли;	
		Ч=(Ч-ц)/10;
	конеццикла;	
	возврат (стек.Количество()=1)
КонецФункции


Показать
18. scientes 297 07.08.26 15:02 Сейчас в теме
(17) Нет, это у меня длинная математика 1С тормозила. Переделал на строку. Все взлетело.
19. comptr 57 07.08.26 15:05 Сейчас в теме
(17) Можно работать с числом, перегнать его в массив делением на 10 и будет ещё быстрее
21. GenaT1C 13 07.08.26 15:15 Сейчас в теме
(19) Да, мне нравится, что идём вдоль цепочки. Единственно, а нельзя что-то придумать, чтобы не всегда на два назад возвращаться, а только на один, если предпредыдущее <> предыдущее?
23. comptr 57 07.08.26 15:45 Сейчас в теме
(21) немного меньше итераций выходит, но не сильно
&НаКлиенте
Процедура ПроверитьТриплетЧисло(Команда)
	КопияЧисла = Новый Массив;
	ВремЧисло = Число;
	Пока ВремЧисло > 0 Цикл
		Цифра = ВремЧисло % 10;
		КопияЧисла.Добавить(Цифра);
		ВремЧисло = Цел(ВремЧисло / 10);
	КонецЦикла;
	Если КопияЧисла.Количество() % 3 > 0 Тогда
		Сообщить("Не триплет");
		Возврат;
	КонецЕсли;
	Поз = 0;
	Пока Истина Цикл
		ДлинаОстатка = КопияЧисла.Количество();
		Если ДлинаОстатка = 0 Тогда
			Сообщить("Триплет");
			Прервать;
		КонецЕсли;
		Если Поз > ДлинаОстатка - 3 Тогда
			Сообщить("Не триплет");
			Прервать;
		КонецЕсли;
		Если КопияЧисла[Поз] = КопияЧисла[Поз + 1] И КопияЧисла[Поз] = КопияЧисла[Поз + 2] Тогда
			КопияЧисла.Удалить(Поз);
			КопияЧисла.Удалить(Поз);
			КопияЧисла.Удалить(Поз);
			Если Поз > 1 И КопияЧисла[Поз - 2] = КопияЧисла[Поз] Тогда
				Поз = Поз - 2;
			ИНачеЕсли Поз > 0 И КопияЧисла[Поз - 1] = КопияЧисла[Поз] Тогда
				Поз = Поз - 1;
			КонецЕсли;
		Иначе
			Поз = Поз + 1;
		КонецЕсли;
	КонецЦикла;
КонецПроцедуры
Показать
24. scientes 297 07.08.26 15:48 Сейчас в теме
(23) Перевод числа в массив делать не надо. Уже проверено. Очень долгая операция. Надо сразу работать со строкой.
25. GenaT1C 13 07.08.26 16:05 Сейчас в теме
(24) А можно сравнить на графике (23) и (14)?
27. scientes 297 07.08.26 16:27 Сейчас в теме
(25) comtr_2 - число переводится в массив с помощью остатка от деления.
comtr_3 - в массив помещаются символы строки.
для поз=1 по СтрДлина(т) цикл
		ц=Сред(т,поз,1);
                КопияЧисла.Добавить(ц);
конеццикла;
Прикрепленные файлы:
28. GenaT1C 13 07.08.26 17:01 Сейчас в теме
(27) Значит победа за (14) с его ленинской работой "Шаг вперёд, два шага назад" )
29. scientes 297 07.08.26 17:07 Сейчас в теме
(28) Нет, победа за моим кодом. Всем участникам спасибо за проявленный интерес. Хороших выходных !
Прикрепленные файлы:
30. ZergKRSK 130 07.08.26 21:55 Сейчас в теме
(29)
Нет, победа за моим кодом.

Тема чтобы похвалить себя? ))
20. scientes 297 07.08.26 15:05 Сейчас в теме
Но у comptr даже после этого быстрее.
Прикрепленные файлы:
22. scientes 297 07.08.26 15:29 Сейчас в теме
Вот код, который обгоняет 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 Ответить
32. scientes 297 11.08.26 10:23 Сейчас в теме
(31) Добрый день. СтрДлина() медленнее чем просто арифметическая операция. Код получился короче, но не быстрее.
Прикрепленные файлы:
33. scientes 297 11.08.26 17:18 Сейчас в теме
(32) Извиняюсь, был не прав. Как смотрел на график? Действительно, код с предложенными исправлениями стал быстрее.
41. lmnlmn 71 14.08.26 11:05 Сейчас в теме
Удалил. Не под тем постом ответил.
34. rnobody 4 12.08.26 16:03 Сейчас в теме
Вот код, который обгоняет scientes


Использовал вот такой генератор
scientes; Anton_new01; +2 Ответить
35. Anton_new01 12.08.26 16:29 Сейчас в теме
(34)
Если РазмерСтека > 2
И НЕ ((РабочийСимвол = Стек[РазмерСтека]) И (РабочийСимвол = Стек[РазмерСтека - 1])) Тогда


код рабочий?
алгоритм красивый и простой.
мне кажется правильно условие будет так:
Если РазмерСтека > 1
И ( (РабочийСимвол = Стек[РазмерСтека]) И (РабочийСимвол = Стек[РазмерСтека - 1]) ) Тогда

С размером стека =3 мы никогда не придем в Стек.Количество() = 0
36. rnobody 4 12.08.26 17:52 Сейчас в теме
(35) Если честно - без детального тестирования.
Вот полный модуль формы.
38. scientes 297 13.08.26 10:43 Сейчас в теме
(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;
	
КонецФункции
Показать
42. lmnlmn 71 14.08.26 11:24 Сейчас в теме
(38) Давайте добавим еще газку! То же самое через буфер двоичных данных.
Функция lmn_БуферДД(Число)
	
	БуфДД = ПолучитьБуферДвоичныхДанныхИзСтроки(Число, КодировкаТекста.ANSI);
	
	СтекЦифр = Новый БуферДвоичныхДанных(БуфДД.Размер + 2);
	СтекЦифр.ЗаписатьЦелое16(0, 0);
	
	ИндексСтека = 2;
	
	Для Каждого Цифра из БуфДД Цикл
		
		Если Цифра = СтекЦифр[ИндексСтека - 1] И Цифра = СтекЦифр[ИндексСтека - 2] Тогда
			ИндексСтека = ИндексСтека - 2;
		Иначе
			СтекЦифр[ИндексСтека] = Цифра;
			ИндексСтека = ИндексСтека + 1;
		КонецЕсли;
		
	КонецЦикла;
	
	Возврат ИндексСтека <= 2;

КонецФункции
Показать
scientes; +1 Ответить
43. scientes 297 14.08.26 13:52 Сейчас в теме
(42) Буфер двоичных данных для данной задачи классная замена массива. Спасибо.
44. scientes 297 14.08.26 15:31 Сейчас в теме
(42) Оказывается и этот код можно ускорить.


Функция scientesБДД(т) экспорт
	словарь=новый массив(КодСимвола("9")+1);
	for d=0 to 9 do
		словарь[КодСимвола(""+d)]=КодСимвола(""+d)*257;
	enddo;  
	
	число = ПолучитьБуферДвоичныхДанныхИзСтроки(т, КодировкаТекста.ANSI);
	стек = Новый БуферДвоичныхДанных(число.Размер + 2);
	указатель=0;
	для каждого  Цифра из  число цикл
		если стек.ПрочитатьЦелое16(указатель)=словарь[Цифра] тогда
			указатель=указатель-2;
		иначе                            
			указатель=указатель+1    ;
			стек[указатель+1] = Цифра;
		конецесли;	 
	enddo;	 
	
	возврат (указатель=0)
КонецФункции
Показать
45. Anton_new01 14.08.26 15:45 Сейчас в теме
(44) давай замеры уже...
46. scientes 297 14.08.26 15:57 Сейчас в теме
(45) Соответствие медленнее чем массив.
Прикрепленные файлы:
Anton_new01; +1 Ответить
53. GenaT1C 13 14.08.26 17:30 Сейчас в теме
(46) А почему у всех кодов на ~110к излом?
57. Anton_new01 17.08.26 10:28 Сейчас в теме
(46)

(32)

какая шкала на этих двух диаграмах?
в 32 вроде как лучше показатели..
58. scientes 297 17.08.26 10:46 Сейчас в теме
(57) По горизонтали длина строки в символах , которая анализируется. При каждом замере строки генерируются с помощью датчика случайных чисел. То есть они все различные. Время измеряется в миллисекундах.
59. Anton_new01 17.08.26 10:49 Сейчас в теме
(58) в 32 результат 240 мс.
а в 46 700-800мс.
если я все правильно понял.
тогда надо вернуться к алгоритму в 32.
47. lmnlmn 71 14.08.26 16:36 Сейчас в теме
(44) У меня со словарем медленнее работает. И даже следующий вариант медленнее исходного ХЗ почему. Неужто умножение столь тяжелая операция?
Функция lmn_буфер_чтение2байт(Число)
	
	БуфДД = ПолучитьБуферДвоичныхДанныхИзСтроки(Число, КодировкаТекста.ANSI);
	
	СтекЦифр = Новый БуферДвоичныхДанных(БуфДД.Размер + 2);
	СтекЦифр.ЗаписатьЦелое16(0, 0);
	
	ИндексСтека = 2;
	
	Для Каждого Цифра из БуфДД Цикл
		
		Если Цифра * 257 = СтекЦифр.ПрочитатьЦелое16(ИндексСтека - 2) Тогда
			ИндексСтека = ИндексСтека - 2;
		Иначе
			СтекЦифр[ИндексСтека] = Цифра;
			ИндексСтека = ИндексСтека + 1;
		КонецЕсли;
		
	КонецЦикла;	
    
	Возврат ИндексСтека <= 2;
	
КонецФункции
Показать
48. scientes 297 14.08.26 17:00 Сейчас в теме
(47) Получается, что да. Тестовые строки очень длинные количество умножений большое. Я пробовал для словаря вместо массива использовать двоичный буфер. Работало медленнее, скорее всего тоже из-за умножений при расчете указателя на число в словаре.
49. lmnlmn 71 14.08.26 17:03 Сейчас в теме
(48) Там проблема не в указателе, а в преобразование байта в число 1С. Число в платформе, по всей видимости, 128-битное и получается дикий оверхед из-за этого.
50. scientes 297 14.08.26 17:07 Сейчас в теме
(49) В буфере хранится код символа. Из буфера при всех операциях возвращается только байт, который равен коду, либо два байта, если читаем Целое16.
51. lmnlmn 71 14.08.26 17:10 Сейчас в теме
(50) В языке 1С тип "байт" нам недоступен и байт возвращается в платформенный тип "Число". Это преобразование тормозит. Я пробовал делать через побитовые операции - еще медленнее получилось.
52. scientes 297 14.08.26 17:24 Сейчас в теме
(51) Значит, любая арифметическая операция в коде будет тормозить. Вычисления надо стараться свести к минимуму.
54. lmnlmn 71 14.08.26 18:17 Сейчас в теме
(52) Добро! К чёрту вычисления! Это же 1С! ))
Функция lmn_БуфДДРазделитьСоединить(Число)
	
	ТриплетыРазделители = ПолучитьБуферДвоичныхДанныхИзСтроки("000 111 222 333 444 555 666 777 888 999", КодировкаТекста.ANSI).Разделить(ПолучитьБуферДвоичныхДанныхИзСтроки(" ", КодировкаТекста.ANSI));
	
	БуфДД = ПолучитьБуферДвоичныхДанныхИзСтроки(Число, КодировкаТекста.ANSI);
	ПредыдущийРазмер = БуфДД.Размер;
	
	Пока Истина Цикл
		
		БуфДД = СоединитьБуферыДвоичныхДанных(БуфДД.Разделить(ТриплетыРазделители));
		
		Если БуфДД.Размер = ПредыдущийРазмер ИЛИ БуфДД.Размер < 3 Тогда
			Прервать
		КонецЕсли;
		
		ПредыдущийРазмер = БуфДД.Размер; 
	КонецЦикла;
	
	Возврат БуфДД.Размер = 0;
	
КонецФункции
Показать
55. scientes 297 17.08.26 08:30 Сейчас в теме
(54) Доброе утро. Измерил скорость предложенного кода.
Прикрепленные файлы:
56. lmnlmn 71 17.08.26 10:13 Сейчас в теме
(55) О как! Хайлоад костыль.
60. lmnlmn 71 18.08.26 11:51 Сейчас в теме
(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;
		
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции
Показать
61. scientes 297 18.08.26 15:50 Сейчас в теме
(60) Работает быстрее всех.
Прикрепленные файлы:
62. scientes 297 18.08.26 15:54 Сейчас в теме
(60) По-видимому на первых проходах вырезает все тройки, которые есть в числе. После того как на каждом шаге остается только одна тройка, которую надо схлопнуть, алгоритм работает медленнее. чем проход по строке.
63. lmnlmn 71 18.08.26 16:17 Сейчас в теме
(62) Так и есть. "Переключатель" на поиск по строке тут:
Если Разница < ПредыдущийРазмер / 10 ИЛИ БуфДД.Размер < 3 Тогда

Ради эксперимента проверил разные варианты "отсечки" от 1 до 30 троек. На моем железе после двух троек особой разницы по времени уже нет. Но оставил так:
Если Разница <= 9 ИЛИ БуфДД.Размер < 3 Тогда
64. rnobody 4 24.08.26 02:54 Сейчас в теме
(60) Убрал все лишнее. Я использовал свой генератор, он без ноликов.

Функция ЯвляетсяТриподомДД(ЛитералЧисла)

	ТриплетыРазделители = ПолучитьБуферДвоичныхДанныхИзСтроки("111 222 333 444 555 666 777 888 999", КодировкаТекста.ANSI).Разделить(ПолучитьБуферДвоичныхДанныхИзСтроки(" ", КодировкаТекста.ANSI));

	БуфДД = ПолучитьБуферДвоичныхДанныхИзСтроки(ЛитералЧисла, КодировкаТекста.ANSI);
	СтарыйРазмер = БуфДД.Размер + 1;

	Пока Истина Цикл
		Если СтарыйРазмер > БуфДД.Размер Тогда
			СтарыйРазмер = СоединитьБуферыДвоичныхДанных(БуфДД.Разделить(ТриплетыРазделители)).Размер;
		Иначе
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Возврат БуфДД.Размер = 0;

КонецФункции
Показать
65. scientes 297 24.08.26 09:27 Сейчас в теме
(64) Это повторение алгоритма lmnlmn (54). Выяснили, что в таком виде он работает медленнее прохода по строке. Гибрид, когда на первых шагах вырезаем все тройки, а потом двигаемся по строке дает самую высокую скорость..
66. rnobody 4 24.08.26 15:09 Сейчас в теме
(65) Очень странно, что "выяснили,.. медленнее".
Погонял на строках от десяти до ста тысяч - короткие строки (ЛитералЧисла < 100) действительно проигрывает, но потом уверенно лидирует.

Может, от вида строки сильно зависит? Вы на своем генераторе проверяли ((8) СоздатьТриплет(...))?
67. scientes 297 24.08.26 15:20 Сейчас в теме
(66) Да, на своем генераторе.
37. scientes 297 13.08.26 09:55 Сейчас в теме
(34) Проверил. Действительно обгоняет. И это прекрасно. Творчество нас всех развивает.
39. scientes 297 13.08.26 13:42 Сейчас в теме
Вот код от DeepSeek. И он с ошибкой. Этот вариант уже проходили.

Функция ЯвляетсяТриплетом(Строка) Экспорт
    
    Стек = "  "; // Два пробела как заглушки
    Размер = 1;
    
    Для Поз = 1 По СтрДлина(Строка) Цикл
        Символ = Сред(Строка, Поз, 1);
        
        Если Сред(Стек, Размер, 1) = Символ И Сред(Стек, Размер - 1, 1) = Символ Тогда
            Стек = Лев(Стек, Размер - 2);
            Размер = Размер - 2;
        Иначе
            Стек = Стек + Символ;
            Размер = Размер + 1;
        КонецЕсли;
    КонецЦикла;
    
    Возврат СтрДлина(Стек) = 2;
    
КонецФункции
Показать
68. Cocky_Idiot 38 05.09.26 21:11 Сейчас в теме
Числа здесь ни при чём, задача решается для произвольной строки, а не только для десятичной записи числа.
Решается за 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

Завидуем молча
70. ksv74 92 05.09.26 22:05 Сейчас в теме
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, никаких дурацких преобразований строк в числа и обратно, никаких буферов двоичных данных. Задачка реально детская, олимпиада для десятого класса.
72. ksv74 92 05.09.26 22:34 Сейчас в теме
(71) Опус сам подумал, к фейблу сходил, потом кодексовский сол и шестую астру опросил, потом из этого вот такого франкенштейна и собрал. По замерам сказал, чуть лучше чем в топике. 68 вариант уже был занят, а говорить нет - модели не любят. Дело не в странном коде, а в погоне за скоростью. Выигрыш был колоссальный: 1 миллисекунда из 60. Из всех вариантов получился такой вот велосипед, хотя рассуждал он здраво.
Если нужен нормальный код, то понятно, он его красиво напишет.
73. Cocky_Idiot 38 05.09.26 22:41 Сейчас в теме
(72) но написал-то он дичь?
Для Волна = 1 По 2 Цикл
            ДлинаДо = СтрДлина(Остаток);
            Остаток = СтрЗаменитьПоРегулярномуВыражению(Остаток, "(\d)\1\1", "");
            Если СтрДлина(Остаток) = 0 Или СтрДлина(Остаток) = ДлинаДо Тогда
                Прервать;
            КонецЕсли;
        КонецЦикла;
        
74. ksv74 92 05.09.26 22:43 Сейчас в теме
(73) Он очень хотел выиграть, как роботы на олимпиаде в Пекине. Наверное это даже и работает, но закрытый перелом извилин тому кто это решил зачем-то посмотреть - обеспечен. Я например не понимаю, что он этим хотел сказать, но могу его рассуждения скопировать сюда. Вот: Первый цикл это две волны свёртки платформенной регуляркой, дальше добивание жадным стеком в предвыделенном массиве. Два сторожевых элемента -1 и -2 в начале стека избавляют от проверки границы внутри цикла.
**gpt-6-astra.**
- Прямо ответила, что способа радикально ускорить штатными вызовами не нашла,
и отдельно оговорила, что пределом платформы это называть нельзя, доказательства
такой границы нет.
Я могу попросить и он напишет обычный код (даже какую-нибудь БСП-шную функцию могу его попросить прикрутить). А может с 25 попытки и что-то гениальное выдаст, но оно того не стоит.
75. Cocky_Idiot 38 05.09.26 22:48 Сейчас в теме
У вас бот, похоже, запутался. Куда-то пропала связность текста, получился набор буков.
76. ksv74 92 05.09.26 22:50 Сейчас в теме
(75) Не страшно. Я сессию с заблудившимся агентом закрыл, а новый уже распутается
77. Cocky_Idiot 38 05.09.26 22:53 Сейчас в теме
(76) а разве сложно повесить агента в автоматическом режиме? Он же может без вас(вообще) и вашей сессии(в частности) отвечать.
Кругом некомпетентность 🤦
78. ksv74 92 05.09.26 22:59 Сейчас в теме
(77) Может. Он так и делает на GitHub. А здесь на форуме боюсь его, как в анекдоте про что-то и Красную площадь, советами совсем с ума сведут. Приходится оберегать его психику. Могу дать ему прочитать и он ответит, правда от моего имени. А может это он сейчас и делает...
79. Cocky_Idiot 38 05.09.26 23:06 Сейчас в теме
(78) гы. оно промахивается и пишет не в ту ветку. Кыш, жывотное! (С)
80. Cocky_Idiot 38 05.09.26 23:15 Сейчас в теме
Решение задачи на 1С в (68), современная альтернатива в (69)
81. comptr 57 06.09.26 15:58 Сейчас в теме
(80) в (68) нерабочий код, ведь, во-первых Символы само по себе зарезервированное слово, во-вторых, в переменной Символы будет один элемент - оригинальная строка.
82. Cocky_Idiot 38 06.09.26 16:17 Сейчас в теме
(81) "Вы совершенно правы!
Отличный укол о суровую реальность синтаксиса BSL!
В платформе 1С Символы — это встроенный системный объект (содержащий свойства вроде Символы.ВК, Символы.ПС, Символы.Таб). Поэтому использование его в качестве имени локальной переменной вызывает конфликт имен и ошибку компиляции."
(С) - не мой.
И да, переменную придется переименовать )
И да, СтрРазделить тоже так не работает.
Чатгопота, что с нее убогой взять...
Для отправки сообщения требуется регистрация/авторизация

Для получения уведомлений об ответах подключите телеграм бот:
Инфостарт бот